forked from VoxeLibre/VoxeLibre
Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
|
87462ee853 | |
|
8f735cc644 | |
|
422076f761 | |
|
9ce59d4438 | |
|
99357d467c | |
|
018fc64a27 | |
|
b53329b452 | |
|
c27dea59b0 | |
|
80c6b547e5 | |
|
0de53a9dcb | |
|
67dd48b06c | |
|
34c31cac1f | |
|
788b6dc080 | |
|
200ce76c70 | |
|
ca94a1c354 |
4
API.md
4
API.md
|
@ -17,10 +17,6 @@ Items can have these fields:
|
||||||
anvil.
|
anvil.
|
||||||
See `mcl_banners` for an example.
|
See `mcl_banners` for an example.
|
||||||
|
|
||||||
Tools can have these fields:
|
|
||||||
* `_mcl_diggroups`: Specifies the digging groups that a tool can dig and how
|
|
||||||
efficiently. See `_mcl_autogroup` for more information.
|
|
||||||
|
|
||||||
All nodes can have these fields:
|
All nodes can have these fields:
|
||||||
|
|
||||||
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0
|
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0
|
||||||
|
|
|
@ -7,11 +7,13 @@ But first, some things to note:
|
||||||
MineClone 2's development target is to make a free software clone of Minecraft,
|
MineClone 2's development target is to make a free software clone of Minecraft,
|
||||||
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
|
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
|
||||||
|
|
||||||
MineClone 2 is maintained by three persons. Namely, kay27, EliasFleckenstein and jordan4ibanez. You can find us
|
MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
|
||||||
in the Minetest forums (forums.minetest.net), in IRC in the #mineclone2
|
in the Minetest forums (forums.minetest.net), in IRC in the #minetest
|
||||||
channel on irc.freenode.net. And finally, you can send e-mails to
|
channel on irc.freenode.net. And finally, you can send e-mails to
|
||||||
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
|
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
|
||||||
|
|
||||||
|
There is **no** guarantee we will accept anything from anybody.
|
||||||
|
|
||||||
By sending us patches or asking us to include your changes in this game,
|
By sending us patches or asking us to include your changes in this game,
|
||||||
you agree that they fall under the terms of the LGPLv2.1, which basically
|
you agree that they fall under the terms of the LGPLv2.1, which basically
|
||||||
means they will become part of a free software.
|
means they will become part of a free software.
|
||||||
|
@ -24,7 +26,8 @@ For small and medium changes:
|
||||||
|
|
||||||
* Fork the repository
|
* Fork the repository
|
||||||
* Do your change in a new branch
|
* Do your change in a new branch
|
||||||
* Create a pull request to get your changes merged into master
|
* Upload the repository somewhere where it can be accessed from the Internet and
|
||||||
|
notify us
|
||||||
|
|
||||||
For small changes, sending us a patch is also good.
|
For small changes, sending us a patch is also good.
|
||||||
|
|
||||||
|
@ -38,30 +41,40 @@ reserve the right to revert everything that we don't like.
|
||||||
For bigger changes, we strongly recommend to use feature branches and
|
For bigger changes, we strongly recommend to use feature branches and
|
||||||
discuss with me first.
|
discuss with me first.
|
||||||
|
|
||||||
If your code causes bugs and crashes, it is your responsibility to fix them as soon as possible.
|
Contributors will be credited in `README.md`.
|
||||||
|
|
||||||
We mostly use plain merging rather than rebasing or squash merging.
|
## Quality remarks
|
||||||
|
Again: There is ***no*** guarantee we will accept anything from anybody.
|
||||||
|
But we will gladly take in code from others when we feel it saves us work
|
||||||
|
in the long run.
|
||||||
|
|
||||||
Your commit names should be relatively descriptive, e.g. when saying "Fix #issueid", the commit message should also contain the title of the issue.
|
### Inclusion criteria
|
||||||
|
Depending on what you add, the chances for inclusion vary:
|
||||||
|
|
||||||
Contributors will be credited in `CREDITS.md`.
|
### High chance for inclusion
|
||||||
|
* Gameplay features in Minecraft which are missing in MineClone 2
|
||||||
|
|
||||||
## Features > 1.12
|
### Medium chance for inclusion (discuss first)
|
||||||
|
* Features which don't a impact on gameplay
|
||||||
|
* GUI improvement
|
||||||
|
* Features from pocket or console edition
|
||||||
|
|
||||||
If you want to make a feature that was added in a Minecraft version later than 1.12, you should fork MineClone5 (mineclone5 branch in the repository) and add your changes to this.
|
### Low chance for inclusion (discuss/optimize first)
|
||||||
|
* Overhaul of architecture / mod structure
|
||||||
|
* Mass-itemstring changes all over the place
|
||||||
|
* Added files have a unusual high file size
|
||||||
|
* Indentation looks like crazy
|
||||||
|
* Single commits which add several unrelated things
|
||||||
|
* Gameplay features which don't exist in Minecraft
|
||||||
|
|
||||||
## What we accept
|
### Instant rejection
|
||||||
|
* Proprietary **anything**
|
||||||
|
* Code contains `minetest.env` anywhere
|
||||||
|
|
||||||
* Every MC features up to version 1.12 JE.
|
## Coding style guide
|
||||||
* Every already finished and working good features from versions above (only when making a MineClone5 PR / Contribution).
|
* Indentations should reflect the code flow
|
||||||
* Except features which couldn't be done easily and bugfree because of Minetest engine limitations. Eg. we CAN extend world boundaries by playing with map chunks, just teleporting player onto next layer after 31000 , but it would cost too much (time, code, bugs, performance, stability, etc).
|
* Use tabs, not spaces for indentation (tab size = 8)
|
||||||
* Some features, approved by the rest of the community, I mean maybe some voting and really missing any negative feedback.
|
* Never use `minetest.env`
|
||||||
|
|
||||||
## What we reject
|
|
||||||
|
|
||||||
* Any features which cause critical bugs, sending them to rework/fix or trying to fix immediately.
|
|
||||||
* Some small portions of big entirely missing features which just definitely break gamplay balance give nothing useful
|
|
||||||
* Controversial features, which some people support while others do not should be discussed well, with publishing forum announcements, at least during the week. In case if there are still doubts - send them into the mod.
|
|
||||||
|
|
||||||
## Reporting bugs
|
## Reporting bugs
|
||||||
Report all bugs and missing Minecraft features here:
|
Report all bugs and missing Minecraft features here:
|
||||||
|
|
117
CREDITS.md
117
CREDITS.md
|
@ -1,117 +0,0 @@
|
||||||
# Credits
|
|
||||||
|
|
||||||
## Creator of MineClone
|
|
||||||
* davedevils
|
|
||||||
|
|
||||||
## Creator of MineClone2
|
|
||||||
* Wuzzy
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
* Fleckenstein
|
|
||||||
* kay27
|
|
||||||
* oilboi
|
|
||||||
|
|
||||||
## Developers
|
|
||||||
* bzoss
|
|
||||||
* AFCMS
|
|
||||||
* epCode
|
|
||||||
* ryvnf
|
|
||||||
* iliekprogrammar
|
|
||||||
* MysticTempest
|
|
||||||
* Rootyjr
|
|
||||||
* Nicu
|
|
||||||
* aligator
|
|
||||||
* Code-Sploit
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
* Laurent Rocher
|
|
||||||
* HimbeerserverDE
|
|
||||||
* TechDudie
|
|
||||||
* Alexander Minges
|
|
||||||
* ArTee3
|
|
||||||
* ZeDique la Ruleta
|
|
||||||
* pitchum
|
|
||||||
* wuniversales
|
|
||||||
* Bu-Gee
|
|
||||||
* David McMackins II
|
|
||||||
* Nicholas Niro
|
|
||||||
* Wouters Dorian
|
|
||||||
* Blue Blancmange
|
|
||||||
* Jared Moody
|
|
||||||
* Li0n
|
|
||||||
* Midgard
|
|
||||||
* NO11
|
|
||||||
* Saku Laesvuori
|
|
||||||
* Yukitty
|
|
||||||
* ZedekThePD
|
|
||||||
* aldum
|
|
||||||
* dBeans
|
|
||||||
* nickolas360
|
|
||||||
* yutyo
|
|
||||||
* ztianyang
|
|
||||||
* j45
|
|
||||||
|
|
||||||
## MineClone5
|
|
||||||
* kay27
|
|
||||||
* Debiankaios
|
|
||||||
* epCode
|
|
||||||
* NO11
|
|
||||||
* j45
|
|
||||||
|
|
||||||
## Original Mod Authors
|
|
||||||
* Wuzzy
|
|
||||||
* Fleckenstein
|
|
||||||
* BlockMen
|
|
||||||
* TenPlus1
|
|
||||||
* PilzAdam
|
|
||||||
* ryvnf
|
|
||||||
* stujones11
|
|
||||||
* Arcelmi
|
|
||||||
* celeron55
|
|
||||||
* maikerumine
|
|
||||||
* GunshipPenguin
|
|
||||||
* Qwertymine3
|
|
||||||
* Rochambeau
|
|
||||||
* rubenwardy
|
|
||||||
* stu
|
|
||||||
* oilboi
|
|
||||||
* 4aiman
|
|
||||||
* Kahrl
|
|
||||||
* Krock
|
|
||||||
* UgnilJoZ
|
|
||||||
* lordfingle
|
|
||||||
* 22i
|
|
||||||
* bzoss
|
|
||||||
* kilbith
|
|
||||||
* xeranas
|
|
||||||
* kddekadenz
|
|
||||||
* sofar
|
|
||||||
* 4Evergreen4
|
|
||||||
* jordan4ibanez
|
|
||||||
* paramat
|
|
||||||
|
|
||||||
## 3D Models
|
|
||||||
* 22i
|
|
||||||
* tobyplowy
|
|
||||||
* epCode
|
|
||||||
|
|
||||||
## Textures
|
|
||||||
* XSSheep
|
|
||||||
* Wuzzy
|
|
||||||
* kingoscargames
|
|
||||||
* leorockway
|
|
||||||
* xMrVizzy
|
|
||||||
* yutyo
|
|
||||||
|
|
||||||
## Translations
|
|
||||||
* Wuzzy
|
|
||||||
* Rocher Laurent
|
|
||||||
* wuniversales
|
|
||||||
* kay27
|
|
||||||
* pitchum
|
|
||||||
|
|
||||||
## Special thanks
|
|
||||||
* celeron55 for creating Minetest
|
|
||||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
|
||||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
|
||||||
* Notch and Jeb for being the major forces behind Minecraft
|
|
52
LEGAL.md
52
LEGAL.md
|
@ -1,52 +0,0 @@
|
||||||
# Legal information
|
|
||||||
This is a fan game, not developed or endorsed by Mojang AB.
|
|
||||||
|
|
||||||
Copying is an act of love. Please copy and share! <3
|
|
||||||
Here's the detailed legalese for those who need it:
|
|
||||||
|
|
||||||
## License of source code
|
|
||||||
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
|
||||||
is an imitation of Minecraft.
|
|
||||||
|
|
||||||
MineClone 2 is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License (in the LICENSE.txt file) for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
In the mods you might find in the read-me or license
|
|
||||||
text files a different license. This counts as dual-licensing.
|
|
||||||
You can choose which license applies to you: Either the
|
|
||||||
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
|
||||||
|
|
||||||
MineClone 2 is a direct continuation of the discontinued MineClone
|
|
||||||
project by davedevils.
|
|
||||||
|
|
||||||
Mod credits:
|
|
||||||
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
|
||||||
For mods that do not have such a file, the license is the source code license
|
|
||||||
of MineClone 2 and the author is Wuzzy.
|
|
||||||
|
|
||||||
## License of media (textures and sounds)
|
|
||||||
No non-free licenses are used anywhere.
|
|
||||||
|
|
||||||
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
|
||||||
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
|
||||||
from scratch.
|
|
||||||
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
|
||||||
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
|
||||||
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
|
||||||
|
|
||||||
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
|
||||||
|
|
||||||
All other files, unless mentioned otherwise, fall under:
|
|
||||||
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
|
||||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
||||||
|
|
||||||
See README.txt in each mod directory for detailed information about other authors.
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Missing features in Minetest to recreate Minecraft features
|
||||||
|
|
||||||
|
A side goal of the MineClone 2 project is to find any shortcomings of Minetest which make it impossible to recreate a Minecraft feature exactly.
|
||||||
|
This file lists some of the missing features in Minetest which MineClone 2 would require.
|
||||||
|
|
||||||
|
## No workaround possible
|
||||||
|
For these features, no easy Lua workaround could be found.
|
||||||
|
|
||||||
|
### Lua API
|
||||||
|
#### Tools/wielded item
|
||||||
|
- “Lock” hotbar for a brief time after using an item, making it impossible to switch item or to attach/mine/build until the delay is over (For eating with delay)
|
||||||
|
- Tool charging: Holding down the mouse and releasing it, applying a “power level” (For bow and arrows, more charge = higher arrow range) ([issue 5212](https://github.com/minetest/minetest/issues/5212))
|
||||||
|
- [Dual Wielding](http://minecraft.gamepedia.com/Dual_wield)
|
||||||
|
- Eating/drinking animation ([issue 2811](https://github.com/minetest/minetest/issues/2811))
|
||||||
|
|
||||||
|
#### Nodes
|
||||||
|
- Light level 15 for nodes (not sunlight)
|
||||||
|
- Nodes makes light level drop by 2 or or more per node ([issue 5209](https://github.com/minetest/minetest/issues/5209))
|
||||||
|
|
||||||
|
## Interface
|
||||||
|
- Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster
|
||||||
|
- Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211))
|
||||||
|
- Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots
|
||||||
|
|
||||||
|
## Workaround theoretically possible
|
||||||
|
For these features, a workaround (or hack ;-)) by using Lua is theoretically possible. But engine support would be clearly better, more performant, more reliable, etc.
|
||||||
|
|
||||||
|
### Lua API
|
||||||
|
#### Nodes
|
||||||
|
- Change walking speed on block (soul sand)
|
||||||
|
- Change jumping height on block (soul sand),
|
||||||
|
- Change object movement speed *through* a block, but for non-liquids (for cobweb)
|
||||||
|
- Add `on_walk_over` event
|
||||||
|
- Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second
|
||||||
|
- Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second
|
||||||
|
- Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation
|
||||||
|
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests
|
||||||
|
|
||||||
|
#### Nice-to-haye
|
||||||
|
- Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos
|
194
README.md
194
README.md
|
@ -2,7 +2,7 @@
|
||||||
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
||||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
Version: 0.72.0 (in development)
|
Version: 0.71.0
|
||||||
|
|
||||||
### Gameplay
|
### Gameplay
|
||||||
You start in a randomly-generated world made entirely of cubes. You can explore
|
You start in a randomly-generated world made entirely of cubes. You can explore
|
||||||
|
@ -65,8 +65,16 @@ map builders. They can not be obtained in-game or in the creative inventory.
|
||||||
Use the `/giveme` chat command to obtain them. See the in-game help for
|
Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||||
an explanation.
|
an explanation.
|
||||||
|
|
||||||
|
#### Incomplete items
|
||||||
|
These items do not work yet, but you can get them with `/giveme` for testing:
|
||||||
|
|
||||||
|
* Minecart with Chest: `mcl_minecarts:chest_minecart`
|
||||||
|
* Minecart with Furnace: `mcl_minecarts:furnace_minecart`
|
||||||
|
* Minecart with Hopper: `mcl_minecarts:hopper_minecart`
|
||||||
|
* Minecart with Command Block: `mcl_minecarts:command_block_minecart`
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
This game requires [Minetest](http://minetest.net) to run (version 5.0.0 or
|
||||||
later). So you need to install Minetest first. Only stable versions of Minetest
|
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||||
are officially supported.
|
are officially supported.
|
||||||
There is no support for running MineClone 2 in development versions of Minetest.
|
There is no support for running MineClone 2 in development versions of Minetest.
|
||||||
|
@ -75,37 +83,23 @@ To install MineClone 2 (if you haven't already), move this directory into the
|
||||||
“games” directory of your Minetest data directory. Consult the help of
|
“games” directory of your Minetest data directory. Consult the help of
|
||||||
Minetest to learn more.
|
Minetest to learn more.
|
||||||
|
|
||||||
## Reporting bugs
|
|
||||||
Please report all bugs and missing Minecraft features here:
|
|
||||||
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
|
||||||
|
|
||||||
## Chating with the community
|
|
||||||
Join our discord server at:
|
|
||||||
|
|
||||||
<https://discord.gg/84GKcxczG3>
|
|
||||||
|
|
||||||
## Project description
|
## Project description
|
||||||
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
||||||
|
|
||||||
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
||||||
* MineClone2 also includes Optifine features supported by the Minetest
|
* MineClone2 also includes Optifine features supported by the Minetest
|
||||||
* In general, Minecraft is aimed to be cloned as good as possible
|
* Features of later Minecraft versions might sneak in, but they have a low priority
|
||||||
|
* In general, Minecraft is aimed to be cloned as good as Minetest currently permits (no hacks)
|
||||||
* Cloning the gameplay has highest priority
|
* Cloning the gameplay has highest priority
|
||||||
* MineClone 2 will use different assets, but with a similar style
|
* MineClone 2 will use different graphics and sounds, but with a similar style
|
||||||
* Limitations found in Minetest will be documented in the course of development
|
* Cloning the interface has no priority. It will only be roughly imitated
|
||||||
* Features of later Minecraft versions are collected in the mineclone5 branch
|
* Limitations found in Minetest will be written down and reported in the course of development
|
||||||
|
|
||||||
## Using features from newer versions of Minecraft
|
|
||||||
For > 1.12 features, checkout MineClone5. It includes features from newer Minecraft versions.
|
|
||||||
Download it here: https://git.minetest.land/MineClone2/MineClone2/src/branch/mineclone5
|
|
||||||
|
|
||||||
## Completion status
|
## Completion status
|
||||||
This game is currently in **beta** stage.
|
This game is currently in **alpha** stage.
|
||||||
It is playable, but not yet feature-complete.
|
It is playable, but unfinished, many bugs are to be expected.
|
||||||
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
Backwards-compability is *not* guaranteed, updating your world might cause small and
|
||||||
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
big bugs (such as “missing node” errors or even crashes).
|
||||||
It is updated weekly and contains relatively stable code for servers.
|
|
||||||
|
|
||||||
The following main features are available:
|
The following main features are available:
|
||||||
|
|
||||||
|
@ -134,7 +128,7 @@ The following main features are available:
|
||||||
* Clock
|
* Clock
|
||||||
* Compass
|
* Compass
|
||||||
* Sponge
|
* Sponge
|
||||||
* Slime block
|
* Slime block (does not interact with redstone)
|
||||||
* Small plants and saplings
|
* Small plants and saplings
|
||||||
* Dyes
|
* Dyes
|
||||||
* Banners
|
* Banners
|
||||||
|
@ -146,19 +140,19 @@ The following main features are available:
|
||||||
* Creative inventory
|
* Creative inventory
|
||||||
* Farming
|
* Farming
|
||||||
* Writable books
|
* Writable books
|
||||||
* Commands
|
* A few server commands
|
||||||
* Villages
|
|
||||||
* The End
|
|
||||||
* And more!
|
* And more!
|
||||||
|
|
||||||
The following features are incomplete:
|
The following features are incomplete:
|
||||||
|
|
||||||
|
* Generated structures (especially villages)
|
||||||
* Some monsters and animals
|
* Some monsters and animals
|
||||||
* Redstone-related things
|
* Redstone-related things
|
||||||
|
* The End
|
||||||
* Special minecarts
|
* Special minecarts
|
||||||
* A couple of non-trivial blocks and items
|
* A couple of non-trivial blocks and items
|
||||||
|
|
||||||
Bonus features (not found in Minecraft 1.12):
|
Bonus features (not found in Minecraft 1.11):
|
||||||
|
|
||||||
* Built-in crafting guide which shows you crafting and smelting recipes
|
* 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
|
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
||||||
|
@ -183,14 +177,148 @@ Technical differences from Minecraft:
|
||||||
* Different textures (Pixel Perfection)
|
* Different textures (Pixel Perfection)
|
||||||
* Different sounds (various sources)
|
* Different sounds (various sources)
|
||||||
* Different engine (Minetest)
|
* Different engine (Minetest)
|
||||||
* Different easter eggs
|
|
||||||
|
|
||||||
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
|
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
|
||||||
|
|
||||||
|
## Reporting bugs
|
||||||
|
Please report all bugs and missing Minecraft features here:
|
||||||
|
|
||||||
|
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
||||||
|
|
||||||
|
## Chating with the community
|
||||||
|
Join our discord server at:
|
||||||
|
|
||||||
|
<https://discord.gg/84GKcxczG3>
|
||||||
|
|
||||||
## Other readme files
|
## Other readme files
|
||||||
|
|
||||||
* `LICENSE.txt`: The GPLv3 license text
|
* `LICENSE.txt`: The GPLv3 license text
|
||||||
* `CONTRIBUTING.md`: Information for those who want to contribute
|
* `CONTRIBUTING.md`: Information for those who want to contribute
|
||||||
|
* `MISSING_ENGINE_FEATURES.md`: List of missing features in Minetest which MineClone 2 would need for improvement
|
||||||
* `API.md`: For Minetest modders who want to mod this game
|
* `API.md`: For Minetest modders who want to mod this game
|
||||||
* `LEGAL.md`: Legal information
|
|
||||||
* `CREDITS.md`: List of everyone who contributed
|
## Credits
|
||||||
|
There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game.
|
||||||
|
|
||||||
|
### Coding
|
||||||
|
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired)
|
||||||
|
* davedevils: Creator of MineClone on which MineClone 2 is based on
|
||||||
|
* [ex-bart](https://github.com/ex-bart): Redstone comparators
|
||||||
|
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes
|
||||||
|
* [aligator](https://github.com/aligator): Improvement of doors
|
||||||
|
* [ryvnf](https://github.com/ryvnf): Explosion mechanics
|
||||||
|
* MysticTempest: Bugfixes
|
||||||
|
* [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand
|
||||||
|
* kay27 <kay27@bk.ru>: Experience system, bugfixes, optimizations (Current maintainer)
|
||||||
|
* [EliasFleckenstein03](https://github.com/EliasFleckenstein03): End crystals, enchanting, burning mobs / players, animated chests, bugfixes (Current maintainer)
|
||||||
|
* epCode: Better player animations, new logo
|
||||||
|
* 2mac: Fix bug with powered rail
|
||||||
|
* Lots of other people: TO BE WRITTEN (see mod directories for details)
|
||||||
|
|
||||||
|
#### Mod credits (summary)
|
||||||
|
|
||||||
|
* `controls`: Arcelmi
|
||||||
|
* `flowlib`: Qwertymine13
|
||||||
|
* `walkover`: lordfingle
|
||||||
|
* `drippingwater`: kddekadenz
|
||||||
|
* `mobs_mc`: maikerumine, 22i and others
|
||||||
|
* `awards`: rubenwardy
|
||||||
|
* `screwdriver`: RealBadAngel, Maciej Kastakin, Minetest contributors
|
||||||
|
* `xpanes`: Minetest contributors
|
||||||
|
* `mesecons` mods: Jeija and contributors
|
||||||
|
* `wieldview`: Stuart Jones
|
||||||
|
* `mcl_meshhand`: Based on `newhand` by jordan4ibanez
|
||||||
|
* `mcl_mobs`: Based on Mobs Redo [`mobs`] by TenPlus1 and contributors
|
||||||
|
* Most other mods: Wuzzy
|
||||||
|
|
||||||
|
Detailed credits for each mod can be found in the individual mod directories.
|
||||||
|
|
||||||
|
### Graphics
|
||||||
|
* [XSSheep](http://www.minecraftforum.net/members/XSSheep): Main author; creator of the Pixel Perfection resource pack of Minecraft 1.11
|
||||||
|
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main menu imagery and various edits and additions of texture pack
|
||||||
|
* [kingoscargames](https://github.com/kingoscargames): Various edits and additions of existing textures
|
||||||
|
* [leorockway](https://github.com/leorockway): Some edits of mob textures
|
||||||
|
* [xMrVizzy](https://minecraft.curseforge.com/members/xMrVizzy): Glazed terracotta (textures are subject to be replaced later)
|
||||||
|
* yutyo <tanakinci2002@gmail.com>: MineClone 2 logo
|
||||||
|
* Other authors: GUI images
|
||||||
|
|
||||||
|
### Translations
|
||||||
|
* Wuzzy: German
|
||||||
|
* Rocher Laurent <rocherl@club-internet.fr>: French
|
||||||
|
* wuniversales: Spanish
|
||||||
|
* kay27 <kay27@bk.ru>: Russian
|
||||||
|
|
||||||
|
### Models
|
||||||
|
* [22i](https://github.com/22i): Creator of all models
|
||||||
|
* [tobyplowy](https://github.com/tobyplowy): UV-mapping fixes to said models
|
||||||
|
|
||||||
|
### Sounds and music
|
||||||
|
Various sources. See the respective mod directories for details.
|
||||||
|
|
||||||
|
### Special thanks
|
||||||
|
|
||||||
|
* davedevils for starting MineClone, the original version of this game
|
||||||
|
* Wuzzy for starting and maintaining MineClone2 for several years
|
||||||
|
* celeron55 for creating Minetest
|
||||||
|
* Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2
|
||||||
|
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||||
|
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||||
|
* Notch and Jeb for being the major forces behind Minecraft
|
||||||
|
* XSSheep for creating the Pixel Perfection resource pack
|
||||||
|
* [22i](https://github.com/22i) for providing great models and support
|
||||||
|
* [maikerumine](http://github.com/maikerumine) for kicking off mobs and biomes
|
||||||
|
|
||||||
|
## Info for programmers
|
||||||
|
You find interesting and useful infos in `API.md`.
|
||||||
|
|
||||||
|
## Legal information
|
||||||
|
This is a fan game, not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
|
Copying is an act of love. Please copy and share! <3
|
||||||
|
Here's the detailed legalese for those who need it:
|
||||||
|
|
||||||
|
### License of source code
|
||||||
|
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
||||||
|
is an imitation of Minecraft.
|
||||||
|
|
||||||
|
MineClone 2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License (in the LICENSE.txt file) for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
In the mods you might find in the read-me or license
|
||||||
|
text files a different license. This counts as dual-licensing.
|
||||||
|
You can choose which license applies to you: Either the
|
||||||
|
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
||||||
|
|
||||||
|
MineClone 2 is a direct continuation of the discontinued MineClone
|
||||||
|
project by davedevils.
|
||||||
|
|
||||||
|
Mod credits:
|
||||||
|
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
||||||
|
For mods that do not have such a file, the license is the source code license
|
||||||
|
of MineClone 2 and the author is Wuzzy.
|
||||||
|
|
||||||
|
### License of media (textures and sounds)
|
||||||
|
No non-free licenses are used anywhere.
|
||||||
|
|
||||||
|
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
||||||
|
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
||||||
|
from scratch.
|
||||||
|
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
||||||
|
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
||||||
|
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||||
|
|
||||||
|
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||||
|
|
||||||
|
All other files, unless mentioned otherwise, fall under:
|
||||||
|
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||||
|
http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
||||||
|
See README.txt in each mod directory for detailed information about other authors.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
# This is a game specific minetest.conf file, do not edit
|
# This is a game specific minetest.conf file, do not edit
|
||||||
|
|
||||||
# If any of these settings are set in your minetest.conf file in ~/.minetest (Linux) or in the root directory of the game (Run in place/Windows)
|
|
||||||
# They will override these settings
|
|
||||||
|
|
||||||
# Basic game rules
|
# Basic game rules
|
||||||
time_speed = 72
|
time_speed = 72
|
||||||
|
|
||||||
|
@ -36,10 +33,10 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
|
||||||
keepInventory = false
|
keepInventory = false
|
||||||
|
|
||||||
# Performance settings
|
# Performance settings
|
||||||
dedicated_server_step = 0.05 #tick rate
|
dedicated_server_step = 0.001
|
||||||
# abm_interval = 0.25
|
abm_interval = 0.25
|
||||||
# max_objects_per_block = 4096
|
max_objects_per_block = 4096
|
||||||
# max_packets_per_iteration = 10096
|
max_packets_per_iteration = 10096
|
||||||
|
|
||||||
# Clientmodding to support official client
|
# Clientmodding to support official client
|
||||||
enable_client_modding = true
|
enable_client_modding = true
|
||||||
|
|
|
@ -4,11 +4,6 @@ Specifically, this mod has 2 purposes:
|
||||||
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
|
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
|
||||||
2) Generating digging time group for all nodes based on node metadata (it's complicated)
|
2) Generating digging time group for all nodes based on node metadata (it's complicated)
|
||||||
|
|
||||||
This mod also requires another mod called “mcl_autogroup” to function properly.
|
|
||||||
“mcl_autogroup” exposes the API used to register digging groups, while this mod
|
|
||||||
uses those digging groups to set the digging time groups for all the nodes and
|
|
||||||
tools.
|
|
||||||
|
|
||||||
See init.lua for more infos.
|
See init.lua for more infos.
|
||||||
|
|
||||||
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.
|
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.
|
||||||
|
|
|
@ -1,361 +1,169 @@
|
||||||
--[[
|
--[[ Mining times. Yeah, mining times … Alright, this is going to be FUN!
|
||||||
This mod implements a HACK to make 100% sure the digging times of all tools
|
|
||||||
match Minecraft's perfectly. The digging times system of Minetest is very
|
|
||||||
different, so this weird group trickery has to be used. In Minecraft, each
|
|
||||||
block has a hardness and the actual Minecraft digging time is determined by
|
|
||||||
this:
|
|
||||||
|
|
||||||
|
This mod does include a HACK to make 100% sure the digging times of all tools match Minecraft's perfectly.
|
||||||
|
The digging times system of Minetest is very different, so this weird group trickery has to be used.
|
||||||
|
In Minecraft, each block has a hardness and the actual Minecraft digging time is determined by this:
|
||||||
1) The block's hardness
|
1) The block's hardness
|
||||||
2) The tool being used (the tool speed and its efficiency level)
|
2) The tool being used
|
||||||
3) Whether the tool is considered as "eligible" for the block
|
3) Whether the tool is considered as “eligible” for the block
|
||||||
(e.g. only diamond pick eligible for obsidian)
|
(e.g. only diamond pick eligible for obsidian)
|
||||||
|
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more information.
|
||||||
|
|
||||||
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more
|
In MineClone 2, all diggable node have the hardness set in the custom field “_mcl_hardness” (0 by default).
|
||||||
information.
|
The nodes are also required to specify the “eligible” tools in groups like “pickaxey”, “shovely”, etc.
|
||||||
|
This mod then calculates the real digging time based on the node meta data. The real digging times
|
||||||
|
are then added into mcl_autogroup.digtimes where the table indices are group rating and the values are the
|
||||||
|
digging times in seconds. These digging times can be then added verbatim into the tool definitions.
|
||||||
|
|
||||||
How the mod is used
|
Example:
|
||||||
===================
|
mcl_autogroup.digtimes.pickaxey_dig_diamond[1] = 0.2
|
||||||
|
|
||||||
In MineClone 2, all diggable nodes have the hardness set in the custom field
|
→ This means that when a node has been assigned the group “pickaxey_dig_diamond=1”, it can be dug by the
|
||||||
"_mcl_hardness" (0 by default). These values are used together with digging
|
diamond pickaxe in 0.2 seconds.
|
||||||
groups by this mod to create the correct digging times for nodes. Digging
|
|
||||||
groups are registered using the following code:
|
|
||||||
|
|
||||||
mcl_autogroup.register_diggroup("shovely")
|
|
||||||
mcl_autogroup.register_diggroup("pickaxey", {
|
|
||||||
levels = { "wood", "gold", "stone", "iron", "diamond" }
|
|
||||||
})
|
|
||||||
|
|
||||||
The first line registers a simple digging group. The second line registers a
|
|
||||||
digging group with 5 different levels (in this case one for each material of a
|
|
||||||
pickaxes).
|
|
||||||
|
|
||||||
Nodes indicate that they belong to a particular digging group by being member of
|
This strange setup with mcl_autogroup has been done to minimize the amount of required digging times
|
||||||
the digging group in their node definition. "mcl_core:dirt" for example has
|
a single tool needs to use. If this is not being done, the loading time will increase considerably
|
||||||
shovely=1 in its groups. If the digging group has multiple levels the value of
|
(>10s).
|
||||||
the group indicates which digging level the node requires.
|
|
||||||
"mcl_core:stone_with_gold" for example has pickaxey=4 because it requires a
|
|
||||||
pickaxe of level 4 be mined.
|
|
||||||
|
|
||||||
For tools to be able to dig nodes of digging groups they need to use the have
|
]]
|
||||||
the custom field "_mcl_diggroups" function to get the groupcaps. The value of
|
|
||||||
this field is a table which defines which groups the tool can dig and how
|
|
||||||
efficiently.
|
|
||||||
|
|
||||||
_mcl_diggroups = {
|
local materials = { "wood", "gold", "stone", "iron", "diamond" }
|
||||||
handy = { speed = 1, level = 1, uses = 0 },
|
local basegroups = { "pickaxey", "axey", "shovely" }
|
||||||
pickaxey = { speed = 1, level = 0, uses = 0 },
|
local minigroups = { "handy", "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
|
||||||
}
|
local divisors = {
|
||||||
|
["wood"] = 2,
|
||||||
|
["gold"] = 12,
|
||||||
|
["stone"] = 4,
|
||||||
|
["iron"] = 6,
|
||||||
|
["diamond"] = 8,
|
||||||
|
["handy"] = 1,
|
||||||
|
["shearsy"] = 15,
|
||||||
|
["swordy"] = 1.5,
|
||||||
|
["shearsy_wool"] = 5,
|
||||||
|
["swordy_cobweb"] = 15,
|
||||||
|
}
|
||||||
|
local max_efficiency_level = 5
|
||||||
|
|
||||||
The "uses" field indicate how many uses (0 for infinite) a tool has when used on
|
mcl_autogroup = {}
|
||||||
the specified digging group. The "speed" field is a multiplier to the dig speed
|
mcl_autogroup.digtimes = {}
|
||||||
on that digging group.
|
mcl_autogroup.creativetimes = {} -- Copy of digtimes, except that all values are 0. Used for creative mode
|
||||||
|
|
||||||
The "level" field indicates which levels of the group the tool can harvest. A
|
for m=1, #materials do
|
||||||
level of 0 means that the tool cannot harvest blocks of that node. A level of 1
|
for g=1, #basegroups do
|
||||||
or above means that the tool can harvest nodes with that level or below. See
|
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||||
"mcl_tools/init.lua" for examples on how "_mcl_diggroups" is used in practice.
|
mcl_autogroup.creativetimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||||
|
for e=1, max_efficiency_level do
|
||||||
Information about the mod
|
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m].."_efficiency_"..e] = {}
|
||||||
=========================
|
|
||||||
|
|
||||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
|
||||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
|
||||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
|
||||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
|
||||||
as possible. Minetest loads mods in reverse alphabetical order.
|
|
||||||
|
|
||||||
This also means that it is very important that no mod adds _mcl_autogroup as a
|
|
||||||
dependency.
|
|
||||||
--]]
|
|
||||||
|
|
||||||
assert(minetest.get_modpath("mcl_autogroup"), "This mod requires the mod mcl_autogroup to function")
|
|
||||||
|
|
||||||
-- Returns a table containing the unique "_mcl_hardness" for nodes belonging to
|
|
||||||
-- each diggroup.
|
|
||||||
local function get_hardness_values_for_groups()
|
|
||||||
local maps = {}
|
|
||||||
local values = {}
|
|
||||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
|
||||||
maps[g] = {}
|
|
||||||
values[g] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, ndef in pairs(minetest.registered_nodes) do
|
|
||||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
|
||||||
if ndef.groups[g] ~= nil then
|
|
||||||
maps[g][ndef._mcl_hardness or 0] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for g, map in pairs(maps) do
|
|
||||||
for k, _ in pairs(map) do
|
|
||||||
table.insert(values[g], k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
|
||||||
table.sort(values[g])
|
|
||||||
end
|
|
||||||
return values
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns a table containing a table indexed by "_mcl_hardness_value" to get
|
|
||||||
-- its index in the list of unique hardnesses for each diggroup.
|
|
||||||
local function get_hardness_lookup_for_groups(hardness_values)
|
|
||||||
local map = {}
|
|
||||||
for g, values in pairs(hardness_values) do
|
|
||||||
map[g] = {}
|
|
||||||
for k, v in pairs(values) do
|
|
||||||
map[g][v] = k
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return map
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Array of unique hardness values for each group which affects dig time.
|
|
||||||
local hardness_values = get_hardness_values_for_groups()
|
|
||||||
|
|
||||||
-- Map indexed by hardness values which return the index of that value in
|
|
||||||
-- hardness_value. Used for quick lookup.
|
|
||||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
|
||||||
|
|
||||||
local function compute_creativetimes(group)
|
|
||||||
local creativetimes = {}
|
|
||||||
|
|
||||||
for index, hardness in pairs(hardness_values[group]) do
|
|
||||||
table.insert(creativetimes, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
return creativetimes
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the list of digging times for using a specific tool on a specific
|
|
||||||
-- diggroup.
|
|
||||||
--
|
|
||||||
-- Parameters:
|
|
||||||
-- group - the group which it is digging
|
|
||||||
-- can_harvest - if the tool can harvest the block
|
|
||||||
-- speed - dig speed multiplier for tool (default 1)
|
|
||||||
-- efficiency - efficiency level for the tool if applicable
|
|
||||||
local function get_digtimes(group, can_harvest, speed, efficiency)
|
|
||||||
local speed = speed or 1
|
|
||||||
if efficiency then
|
|
||||||
speed = speed + efficiency * efficiency + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local digtimes = {}
|
|
||||||
|
|
||||||
for index, hardness in pairs(hardness_values[group]) do
|
|
||||||
local digtime = (hardness or 0) / speed
|
|
||||||
if can_harvest then
|
|
||||||
digtime = digtime * 1.5
|
|
||||||
else
|
|
||||||
digtime = digtime * 5
|
|
||||||
end
|
|
||||||
|
|
||||||
if digtime <= 0.05 then
|
|
||||||
digtime = 0
|
|
||||||
else
|
|
||||||
digtime = math.ceil(digtime * 20) / 20
|
|
||||||
end
|
|
||||||
table.insert(digtimes, digtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
return digtimes
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get one groupcap field for using a specific tool on a specific group.
|
|
||||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
|
||||||
return {
|
|
||||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
|
||||||
uses = uses,
|
|
||||||
maxlevel = 0,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add the groupcaps from a field in "_mcl_diggroups" to the groupcaps of a
|
|
||||||
-- tool.
|
|
||||||
local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
|
|
||||||
if not groupcaps_def then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for g, capsdef in pairs(groupcaps_def) do
|
|
||||||
local mult = capsdef.speed or 1
|
|
||||||
local uses = capsdef.uses
|
|
||||||
local def = mcl_autogroup.registered_diggroups[g]
|
|
||||||
local max_level = def.levels and #def.levels or 1
|
|
||||||
|
|
||||||
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
|
|
||||||
local level = math.min(capsdef.level, max_level)
|
|
||||||
|
|
||||||
if def.levels then
|
|
||||||
groupcaps[g .. "_dig_default"] = get_groupcap(g, false, mult, efficiency, uses)
|
|
||||||
if level > 0 then
|
|
||||||
groupcaps[g .. "_dig_" .. def.levels[level]] = get_groupcap(g, true, mult, efficiency, uses)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
groupcaps[g .. "_dig"] = get_groupcap(g, level > 0, mult, efficiency, uses)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
for g=1, #minigroups do
|
||||||
-- Checks if the given node would drop its useful drop if dug by a given tool.
|
mcl_autogroup.digtimes[minigroups[g].."_dig"] = {}
|
||||||
-- Returns true if it will yield its useful drop, false otherwise.
|
mcl_autogroup.creativetimes[minigroups[g].."_dig"] = {}
|
||||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
for e=1, max_efficiency_level do
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
mcl_autogroup.digtimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||||
|
mcl_autogroup.creativetimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if it can be dug by tool
|
|
||||||
local tdef = minetest.registered_tools[toolname]
|
|
||||||
if tdef and tdef._mcl_diggroups then
|
|
||||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
|
||||||
if ndef.groups[g] then
|
|
||||||
if ndef.groups[g] <= gdef.level then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if it can be dug by hand
|
|
||||||
local tdef = minetest.registered_tools[""]
|
|
||||||
if tdef then
|
|
||||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
|
||||||
if ndef.groups[g] then
|
|
||||||
if ndef.groups[g] <= gdef.level then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get one groupcap field for using a specific tool on a specific group.
|
|
||||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
|
||||||
return {
|
|
||||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
|
||||||
uses = uses,
|
|
||||||
maxlevel = 0,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
|
||||||
-- tool_capabilities
|
|
||||||
local function get_tool_capabilities(tdef)
|
|
||||||
if tdef.tool_capabilities then
|
|
||||||
return tdef.tool_capabilities
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If the damage group and punch interval from hand is not included,
|
|
||||||
-- then the user will not be able to attack with the tool.
|
|
||||||
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
|
|
||||||
return {
|
|
||||||
full_punch_interval = hand_toolcaps.full_punch_interval,
|
|
||||||
damage_groups = hand_toolcaps.damage_groups
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the groupcaps for a tool. This function returns "groupcaps" table of
|
|
||||||
-- digging which should be put in the "tool_capabilities" of the tool definition
|
|
||||||
-- or in the metadata of an enchanted tool.
|
|
||||||
--
|
|
||||||
-- Parameters:
|
|
||||||
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
|
||||||
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
|
||||||
--
|
|
||||||
-- NOTE:
|
|
||||||
-- This function can only be called after mod initialization. Otherwise a mod
|
|
||||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
|
||||||
-- loading order.
|
|
||||||
function mcl_autogroup.get_groupcaps(toolname, efficiency)
|
|
||||||
local tdef = minetest.registered_tools[toolname]
|
|
||||||
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
|
|
||||||
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
|
|
||||||
return groupcaps
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the wear from using a tool on a digging group.
|
|
||||||
--
|
|
||||||
-- Parameters
|
|
||||||
-- toolname - Name of the tool used
|
|
||||||
-- diggroup - The name of the diggroup the tool is used on
|
|
||||||
--
|
|
||||||
-- NOTE:
|
|
||||||
-- This function can only be called after mod initialization. Otherwise a mod
|
|
||||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
|
||||||
-- loading order.
|
|
||||||
function mcl_autogroup.get_wear(toolname, diggroup)
|
|
||||||
local tdef = minetest.registered_tools[toolname]
|
|
||||||
local uses = tdef._mcl_diggroups[diggroup].uses
|
|
||||||
return math.ceil(65535 / uses)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local overwrite = function()
|
local overwrite = function()
|
||||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||||
|
local groups_changed = false
|
||||||
local newgroups = table.copy(ndef.groups)
|
local newgroups = table.copy(ndef.groups)
|
||||||
if (nname ~= "ignore" and ndef.diggable) then
|
if (nname ~= "ignore" and ndef.diggable) then
|
||||||
-- Automatically assign the "solid" group for solid nodes
|
-- Automatically assign the “solid” group for solid nodes
|
||||||
if (ndef.walkable == nil or ndef.walkable == true)
|
if (ndef.walkable == nil or ndef.walkable == true)
|
||||||
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
|
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
|
||||||
and (ndef.node_box == nil or ndef.node_box.type == "regular")
|
and (ndef.node_box == nil or ndef.node_box.type == "regular")
|
||||||
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
|
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
|
||||||
newgroups.solid = 1
|
newgroups.solid = 1
|
||||||
|
groups_changed = true
|
||||||
end
|
end
|
||||||
-- Automatically assign the "opaque" group for opaque nodes
|
-- Automatically assign the “opaque” group for opaque nodes
|
||||||
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
|
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
|
||||||
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
|
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
|
||||||
newgroups.opaque = 1
|
newgroups.opaque = 1
|
||||||
|
groups_changed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local creative_breakable = false
|
local function calculate_group(hardness, material, diggroup, newgroups, actual_rating, expected_rating, efficiency)
|
||||||
|
local time, validity_factor
|
||||||
|
if actual_rating >= expected_rating then
|
||||||
|
-- Valid tool
|
||||||
|
validity_factor = 1.5
|
||||||
|
else
|
||||||
|
-- Wrong tool (higher digging time)
|
||||||
|
validity_factor = 5
|
||||||
|
end
|
||||||
|
local speed_multiplier = divisors[material]
|
||||||
|
if efficiency then
|
||||||
|
speed_multiplier = speed_multiplier + efficiency * efficiency + 1
|
||||||
|
end
|
||||||
|
time = (hardness * validity_factor) / speed_multiplier
|
||||||
|
if time <= 0.05 then
|
||||||
|
time = 0
|
||||||
|
else
|
||||||
|
time = math.ceil(time * 20) / 20
|
||||||
|
end
|
||||||
|
table.insert(mcl_autogroup.digtimes[diggroup], time)
|
||||||
|
if not efficiency then
|
||||||
|
table.insert(mcl_autogroup.creativetimes[diggroup], 0)
|
||||||
|
end
|
||||||
|
newgroups[diggroup] = #mcl_autogroup.digtimes[diggroup]
|
||||||
|
return newgroups
|
||||||
|
end
|
||||||
|
|
||||||
-- Assign groups used for digging this node depending on
|
-- Hack in digging times
|
||||||
-- the registered digging groups
|
local hardness = ndef._mcl_hardness
|
||||||
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
if not hardness then
|
||||||
creative_breakable = true
|
hardness = 0
|
||||||
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
end
|
||||||
if ndef.groups[g] then
|
|
||||||
if gdef.levels then
|
|
||||||
newgroups[g .. "_dig_default"] = index
|
|
||||||
|
|
||||||
for i = ndef.groups[g], #gdef.levels do
|
-- Handle pickaxey, axey and shovely
|
||||||
newgroups[g .. "_dig_" .. gdef.levels[i]] = index
|
for _, basegroup in pairs(basegroups) do
|
||||||
|
if (hardness ~= -1 and ndef.groups[basegroup]) then
|
||||||
|
for g=1,#materials do
|
||||||
|
local diggroup = basegroup.."_dig_"..materials[g]
|
||||||
|
newgroups = calculate_group(hardness, materials[g], diggroup, newgroups, g, ndef.groups[basegroup])
|
||||||
|
for e=1,max_efficiency_level do
|
||||||
|
newgroups = calculate_group(hardness, materials[g], diggroup .. "_efficiency_" .. e, newgroups, g, ndef.groups[basegroup], e)
|
||||||
end
|
end
|
||||||
else
|
groups_changed = true
|
||||||
newgroups[g .. "_dig"] = index
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for m=1, #minigroups do
|
||||||
|
local minigroup = minigroups[m]
|
||||||
|
if hardness ~= -1 then
|
||||||
|
local diggroup = minigroup.."_dig"
|
||||||
|
-- actual rating
|
||||||
|
local ar = ndef.groups[minigroup]
|
||||||
|
if ar == nil then
|
||||||
|
ar = 0
|
||||||
|
end
|
||||||
|
if (minigroup == "handy")
|
||||||
|
or
|
||||||
|
(ndef.groups.shearsy_wool and minigroup == "shearsy_wool" and ndef.groups.wool)
|
||||||
|
or
|
||||||
|
(ndef.groups.swordy_cobweb and minigroup == "swordy_cobweb" and nname == "mcl_core:cobweb")
|
||||||
|
or
|
||||||
|
(ndef.groups[minigroup] and minigroup ~= "swordy_cobweb" and minigroup ~= "shearsy_wool") then
|
||||||
|
newgroups = calculate_group(hardness, minigroup, diggroup, newgroups, ar, 1)
|
||||||
|
for e=1,max_efficiency_level do
|
||||||
|
newgroups = calculate_group(hardness, minigroup, diggroup .. "_efficiency_" .. e, newgroups, ar, 1, e)
|
||||||
|
end
|
||||||
|
groups_changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Automatically assign the node to the
|
if groups_changed then
|
||||||
-- creative_breakable group if it belongs to any digging
|
minetest.override_item(nname, {
|
||||||
-- group.
|
groups = newgroups
|
||||||
newgroups["creative_breakable"] = 1
|
})
|
||||||
|
end
|
||||||
minetest.override_item(nname, {
|
|
||||||
groups = newgroups
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for tname, tdef in pairs(minetest.registered_tools) do
|
|
||||||
-- Assign groupcaps for digging the registered digging groups
|
|
||||||
-- depending on the _mcl_diggroups in the tool definition
|
|
||||||
if tdef._mcl_diggroups then
|
|
||||||
local toolcaps = table.copy(get_tool_capabilities(tdef))
|
|
||||||
toolcaps.groupcaps = mcl_autogroup.get_groupcaps(tname)
|
|
||||||
|
|
||||||
minetest.override_item(tname, {
|
|
||||||
tool_capabilities = toolcaps
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name = _mcl_autogroup
|
name = _mcl_autogroup
|
||||||
author = ryvnf
|
author = Wuzzy
|
||||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
local Object = {}
|
||||||
|
|
||||||
|
-- Define a getter that caches the result for the next time it is called
|
||||||
|
-- This is a static method (self = the class); in this class system static methods start with __ by convention
|
||||||
|
function Object:__cache_getter(name, func)
|
||||||
|
-- cache key: prevent overriding the getter function itself
|
||||||
|
local key = "_" .. name
|
||||||
|
-- add a function to the class
|
||||||
|
self[name] = function(self)
|
||||||
|
-- check if the value is present in the cache
|
||||||
|
local value = self[key]
|
||||||
|
|
||||||
|
-- `== nil` instead of `not value` to allow caching boolean values
|
||||||
|
if value == nil then
|
||||||
|
-- call the getter function
|
||||||
|
value = func(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- store result in cache
|
||||||
|
self[key] = value
|
||||||
|
|
||||||
|
-- return result
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Define a getter / setter
|
||||||
|
-- If no argument is specified, it will act as a getter, else as a setter
|
||||||
|
-- The specified function MUST return the new value, if it returns nil, nil will be used as new value
|
||||||
|
-- Optionally works in combination with a previously defined cache getter and only really makes sense in that context
|
||||||
|
function Object:__setter(name, func)
|
||||||
|
-- since the function is overridden, we need to store the old one in case a cache getter is defined
|
||||||
|
local cache_getter = self[name]
|
||||||
|
-- use same key as cache getter to modify getter cache if present
|
||||||
|
local key = "_" .. name
|
||||||
|
|
||||||
|
self[name] = function(self, new)
|
||||||
|
-- check whether an argument was specified
|
||||||
|
if new == nil then
|
||||||
|
if cache_getter then
|
||||||
|
-- call the cache getter if present
|
||||||
|
return cache_getter(self)
|
||||||
|
else
|
||||||
|
-- return the value else
|
||||||
|
return self[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- call the setter and set the new value to the result
|
||||||
|
self[key] = func(self, new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Define a comparator function
|
||||||
|
-- Acts like a setter, except that it does not set the new value but rather compares the present and specified values and returns whether they are equal or not
|
||||||
|
-- Incompatible with setter
|
||||||
|
-- The function is optional. The == operator is used else.
|
||||||
|
function Object:__comparator(name, func)
|
||||||
|
local cache_getter = self[name]
|
||||||
|
local key = "_" .. name
|
||||||
|
|
||||||
|
self[name] = function(self, expected)
|
||||||
|
-- the current value is needed everytime, no matter whether there is an argument or not
|
||||||
|
local actual
|
||||||
|
|
||||||
|
if cache_getter then
|
||||||
|
-- call the cache getter if present
|
||||||
|
actual = cache_getter(self)
|
||||||
|
else
|
||||||
|
-- use the value else
|
||||||
|
actual = self[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- act as a getter if there is no argument
|
||||||
|
if expected == nil then
|
||||||
|
return actual
|
||||||
|
end
|
||||||
|
|
||||||
|
if func then
|
||||||
|
-- if a function as specified, call it
|
||||||
|
return func(actual, expected)
|
||||||
|
else
|
||||||
|
-- else, use the == operator to compare the expected value to the actual
|
||||||
|
return actual == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Override an already existing function in a way that the old function is called
|
||||||
|
-- If nil is returned, the old function is called. Else the return value is returned. (Only the first return value is taken into concern here, multiple are supported tho)
|
||||||
|
-- This works even if it is applied to the instance of a class when the function is defined by the class
|
||||||
|
-- It also works with overriding functions that are located in superclasses
|
||||||
|
function Object:__override(name, func)
|
||||||
|
-- store the old function
|
||||||
|
local old_func = self[name]
|
||||||
|
|
||||||
|
-- redefine the function with variable arguments
|
||||||
|
self[name] = function(...)
|
||||||
|
-- call the new function and store the return values in a table
|
||||||
|
local rvals = {func(...)}
|
||||||
|
-- if nil was returned, fall back to the old function
|
||||||
|
if rvals[1] == nil then
|
||||||
|
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||||
|
return old_func(...)
|
||||||
|
else
|
||||||
|
-- return the values from the new function else
|
||||||
|
return unpack(rvals)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Works like override except that the new function does not modify the output of the old function but rather the input
|
||||||
|
-- The new function can decide with what arguments by returing them, including the `self` reference
|
||||||
|
-- If the "self" arg is not returned the old function is not called
|
||||||
|
-- Note that this way the new function cannot change the return value of the old function
|
||||||
|
function Object:__pipe(name, func)
|
||||||
|
local old_func = self[name]
|
||||||
|
|
||||||
|
self[name] = function(self, ...)
|
||||||
|
local rvals = {func(self, ...)}
|
||||||
|
-- check if self was returned properly
|
||||||
|
if rvals[1] then
|
||||||
|
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||||
|
return old_func(unpack(rvals))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make class available as table to distribute the Object table
|
||||||
|
class = setmetatable({Object = Object}, {
|
||||||
|
-- Create a new class by calling class() with an optional superclass argument
|
||||||
|
__call = function(super)
|
||||||
|
return setmetatable({}, {
|
||||||
|
-- Create a new instance of the class when the class is called
|
||||||
|
__call = function(_class, ...)
|
||||||
|
-- Check whether the first argument is an instance of the class
|
||||||
|
-- If that is the case, just return it - this is to allow "making sure something is the instance of a class" by calling the constructor
|
||||||
|
local argtbl = {...}
|
||||||
|
local first_arg = args[1]
|
||||||
|
if first_arg and type(first_arg) == "table" and inst.CLASS = _class then
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
-- set the metatable and remember which class the object belongs to
|
||||||
|
local instance = setmetatable({CLASS = _class}, {
|
||||||
|
__index = _class,
|
||||||
|
})
|
||||||
|
-- call the constructor if present
|
||||||
|
if instance.constructor then
|
||||||
|
instance:constructor(...)
|
||||||
|
end
|
||||||
|
-- return the created instance
|
||||||
|
return instance
|
||||||
|
end,
|
||||||
|
-- Object as superclass of all classes that dont have a different one
|
||||||
|
__index = super or Object,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = class
|
||||||
|
author = Fleckenstein
|
||||||
|
description = A class system for MineClone2
|
|
@ -1,23 +0,0 @@
|
||||||
# controls
|
|
||||||
|
|
||||||
## controls.players
|
|
||||||
Table containing player controls at runtime.
|
|
||||||
WARNING: Never use this table in writing
|
|
||||||
|
|
||||||
## controls.register_on_press(func)
|
|
||||||
Register a function that will be executed with (player, keyname) every time a player press a key.
|
|
||||||
|
|
||||||
## controls.registered_on_press
|
|
||||||
Table containing functions registered with controls.register_on_press().
|
|
||||||
|
|
||||||
## controls.register_on_release(func)
|
|
||||||
Register a function that will be executed with (player, keyname, clock_from_last_press) every time a player release a key.
|
|
||||||
|
|
||||||
## controls.registered_on_release
|
|
||||||
Table containing functions registered with controls.register_on_release().
|
|
||||||
|
|
||||||
## controls.register_on_hold(func)
|
|
||||||
Register a function that will be executed with (player, keyname, clock_from_start_hold) every time a player hold a key.
|
|
||||||
|
|
||||||
## controls.registered_on_hold
|
|
||||||
Table containing functions registered with controls.register_on_hold().
|
|
|
@ -1,45 +0,0 @@
|
||||||
# flowlib
|
|
||||||
Simple flow functions.
|
|
||||||
|
|
||||||
## flowlib.is_touching(realpos, nodepos, radius)
|
|
||||||
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
|
|
||||||
* realpos: position
|
|
||||||
* nodepos: position
|
|
||||||
* radius: number
|
|
||||||
|
|
||||||
## flowlib.is_water(pos)
|
|
||||||
Return true if node at <pos> is water, false overwise.
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## flowlib.node_is_water(node)
|
|
||||||
Return true if <node> is water, false overwise.
|
|
||||||
* node: node
|
|
||||||
|
|
||||||
## flowlib.is_lava(pos)
|
|
||||||
Return true if node at <pos> is lava, false overwise.
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## flowlib.node_is_lava(node)
|
|
||||||
Return true if <node> is lava, false overwise.
|
|
||||||
* node: node
|
|
||||||
|
|
||||||
## flowlib.is_liquid(pos)
|
|
||||||
Return true if node at <pos> is liquid, false overwise.
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## flowlib.node_is_liquid(node)
|
|
||||||
Return true if <node> is liquid, false overwise.
|
|
||||||
* node: node
|
|
||||||
|
|
||||||
## flowlib.quick_flow(pos, node)
|
|
||||||
Return direction where the water is flowing (to be use to push mobs, items...).
|
|
||||||
* pos: position
|
|
||||||
* node: node
|
|
||||||
|
|
||||||
## flowlib.move_centre(pos, realpos, node, radius)
|
|
||||||
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
|
|
||||||
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
|
|
||||||
* pos: position
|
|
||||||
* realpos: position, position of the entity
|
|
||||||
* node: node
|
|
||||||
* radius: number
|
|
|
@ -1,27 +0,0 @@
|
||||||
# mcl_autogroup
|
|
||||||
This mod emulate digging times from mc.
|
|
||||||
|
|
||||||
## mcl_autogroup.can_harvest(nodename, toolname)
|
|
||||||
Return true if <nodename> can be dig with <toolname>.
|
|
||||||
* nodename: string, valid nodename
|
|
||||||
* toolname: (optional) string, valid toolname
|
|
||||||
|
|
||||||
## mcl_autogroup.get_groupcaps(toolname, efficiency)
|
|
||||||
This function is used to calculate diggroups for tools.
|
|
||||||
WARNING: This function can only be called after mod initialization.
|
|
||||||
* toolname: string, name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
|
||||||
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
|
|
||||||
|
|
||||||
## mcl_autogroup.get_wear(toolname, diggroup)
|
|
||||||
Return the max wear of <toolname> with <diggroup>
|
|
||||||
WARNING: This function can only be called after mod initialization.
|
|
||||||
* toolname: string, name of the tool used
|
|
||||||
* diggroup: string, the name of the diggroup the tool is used on
|
|
||||||
|
|
||||||
## mcl_autogroup.register_diggroup(group, def)
|
|
||||||
* group: string, name of the group to register as a digging group
|
|
||||||
* def: (optional) table, table with information about the diggroup (defaults to {} if unspecified)
|
|
||||||
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
|
|
||||||
|
|
||||||
## mcl_autogroup.registered_diggroups
|
|
||||||
List of registered diggroups, indexed by name.
|
|
|
@ -1,28 +0,0 @@
|
||||||
--[[
|
|
||||||
This is one part of a mod to replicate the digging times from Minecraft. This
|
|
||||||
part only exposes a function to register digging groups. The rest of the mod is
|
|
||||||
implemented and documented in the _mcl_autogroup.
|
|
||||||
|
|
||||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
|
||||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
|
||||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
|
||||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
|
||||||
as possible. Minetest loads mods in reverse alphabetical order.
|
|
||||||
--]]
|
|
||||||
mcl_autogroup = {}
|
|
||||||
mcl_autogroup.registered_diggroups = {}
|
|
||||||
|
|
||||||
assert(minetest.get_modpath("_mcl_autogroup"), "This mod requires the mod _mcl_autogroup to function")
|
|
||||||
|
|
||||||
-- Register a group as a digging group.
|
|
||||||
--
|
|
||||||
-- Parameters:
|
|
||||||
-- group - Name of the group to register as a digging group
|
|
||||||
-- def - Table with information about the diggroup (defaults to {} if unspecified)
|
|
||||||
--
|
|
||||||
-- Values in def:
|
|
||||||
-- level - If specified it is an array containing the names of the different
|
|
||||||
-- digging levels the digging group supports.
|
|
||||||
function mcl_autogroup.register_diggroup(group, def)
|
|
||||||
mcl_autogroup.registered_diggroups[group] = def or {}
|
|
||||||
end
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = mcl_autogroup
|
|
||||||
author = ryvnf
|
|
||||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
|
|
@ -117,10 +117,6 @@ function mcl_burning.damage(obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
if obj:get_hp() < 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local luaentity = obj:get_luaentity()
|
local luaentity = obj:get_luaentity()
|
||||||
if luaentity and luaentity.fire_resistant then
|
if luaentity and luaentity.fire_resistant then
|
||||||
return
|
return
|
||||||
|
@ -149,7 +145,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
end
|
end
|
||||||
|
|
||||||
if old_burn_time <= burn_time then
|
if old_burn_time <= burn_time then
|
||||||
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
||||||
if sound_id == 0 then
|
if sound_id == 0 then
|
||||||
sound_id = minetest.sound_play("fire_fire", {
|
sound_id = minetest.sound_play("fire_fire", {
|
||||||
object = obj,
|
object = obj,
|
||||||
|
@ -157,7 +153,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
max_hear_distance = 16,
|
max_hear_distance = 16,
|
||||||
loop = true,
|
loop = true,
|
||||||
}) + 1
|
}) + 1
|
||||||
end]]--
|
end
|
||||||
|
|
||||||
local hud_id
|
local hud_id
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
|
@ -167,7 +163,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
hud_elem_type = "image",
|
hud_elem_type = "image",
|
||||||
position = {x = 0.5, y = 0.5},
|
position = {x = 0.5, y = 0.5},
|
||||||
scale = {x = -100, y = -100},
|
scale = {x = -100, y = -100},
|
||||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
text = "fire_basic_flame.png",
|
||||||
z_index = 1000,
|
z_index = 1000,
|
||||||
}) + 1
|
}) + 1
|
||||||
end
|
end
|
||||||
|
@ -175,7 +171,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||||
mcl_burning.set(obj, "string", "reason", reason)
|
mcl_burning.set(obj, "string", "reason", reason)
|
||||||
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
||||||
--mcl_burning.set(obj, "int", "sound_id", sound_id)
|
mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||||
|
|
||||||
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(obj)
|
local minp, maxp = mcl_burning.get_collisionbox(obj)
|
||||||
|
@ -198,8 +194,8 @@ end
|
||||||
|
|
||||||
function mcl_burning.extinguish(obj)
|
function mcl_burning.extinguish(obj)
|
||||||
if mcl_burning.is_burning(obj) then
|
if mcl_burning.is_burning(obj) then
|
||||||
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||||
--minetest.sound_stop(sound_id)
|
minetest.sound_stop(sound_id)
|
||||||
|
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||||
|
@ -210,7 +206,7 @@ function mcl_burning.extinguish(obj)
|
||||||
mcl_burning.set(obj, "float", "burn_time")
|
mcl_burning.set(obj, "float", "burn_time")
|
||||||
mcl_burning.set(obj, "float", "damage_timer")
|
mcl_burning.set(obj, "float", "damage_timer")
|
||||||
mcl_burning.set(obj, "int", "hud_id")
|
mcl_burning.set(obj, "int", "hud_id")
|
||||||
--mcl_burning.set(obj, "int", "sound_id")
|
mcl_burning.set(obj, "int", "sound_id")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
# mcl_colors
|
|
||||||
Mod providing global table containing legacity minecraft colors to be used in mods.
|
|
||||||
|
|
||||||
## mcl_colors.*
|
|
||||||
Colors by upper name, in hex value.
|
|
||||||
|
|
||||||
## mcl_colors.background.*
|
|
||||||
Background colors by upper name, in hex value.
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
MCLDamageSource = class()
|
||||||
|
|
||||||
|
function MCLDamageSource:constructor(tbl)
|
||||||
|
if tbl then
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLDamageSource:from_mt(reason)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLDamageSource:__getter("direct_object", function(self)
|
||||||
|
local obj = self.raw_source_object
|
||||||
|
if not obj then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return mcl_object_mgr.get(obj)
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLDamageSource:__getter("source_object", function(self)
|
||||||
|
local direct = self:direct_object()
|
||||||
|
if not direct then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return direct.source_object or direct
|
||||||
|
end)
|
|
@ -0,0 +1,44 @@
|
||||||
|
MCLEntity = class(MCLObject)
|
||||||
|
|
||||||
|
MCLEntity:__getter("meta", MCLMetadata)
|
||||||
|
|
||||||
|
local last_inv_id = 0
|
||||||
|
|
||||||
|
MCLEntity:__getter("inventory", function(self)
|
||||||
|
local info = self.inventory_info
|
||||||
|
if not info then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.inventory_id = "mcl_entity:" .. last_inv_id
|
||||||
|
last_inv_id = last_inv_id + 1
|
||||||
|
local inv = minetest.create_detached_inventory(self.inventory_id, self.inventory_callbacks)
|
||||||
|
for list, size in pairs(data.sizes) do
|
||||||
|
inv:set_size(list, size)
|
||||||
|
end
|
||||||
|
for list, liststr in pairs(data.lists) do
|
||||||
|
inv:set_list(list, liststr)
|
||||||
|
end
|
||||||
|
return inv
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEntity:on_activate(staticdata)
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if data then
|
||||||
|
self:meta():from_table(data)
|
||||||
|
self.inventory_info = data.inventory
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEntity:get_staticdata()
|
||||||
|
local data = self:meta():to_table()
|
||||||
|
local inventory_info = self.inventory_info
|
||||||
|
|
||||||
|
if inventory_info then
|
||||||
|
data.inventory = {
|
||||||
|
sizes = inventory_info.sizes,
|
||||||
|
lists = self:inventory():get_lists()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest.serialize(data)
|
||||||
|
end
|
|
@ -0,0 +1,103 @@
|
||||||
|
MCLEquipment = class()
|
||||||
|
|
||||||
|
function MCLEquipment:constructor(inv, idx)
|
||||||
|
self.inv = inv
|
||||||
|
self.idx = idx
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_main", function(self)
|
||||||
|
return self.inv and self.idx and self.inv:get_list("main") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_right", function(self)
|
||||||
|
return self.inv and self.inv:get_list("right_hand") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_left", function(self)
|
||||||
|
return self.inv and self.inv:get_list("left_hand") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_armor", function(self)
|
||||||
|
return self.inv and self.inv:get_list("armor") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:mainhand()
|
||||||
|
if self:has_main() then
|
||||||
|
return self.inv:get_stack("main", self.idx)
|
||||||
|
elseif self:has_right() then
|
||||||
|
return self.inv:get_stack("right_hand", 1)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__setter("mainhand", function(self, new)
|
||||||
|
if self:has_main() then
|
||||||
|
self.inv:set_stack("main", self.idx, stack)
|
||||||
|
elseif self:has_right() then
|
||||||
|
self.inv:set_stack("right_hand", 1, stack)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:offhand()
|
||||||
|
if self:has_left() then
|
||||||
|
return self.inv:get_stack("left_hand", 1)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__setter("offhand", function(self, new)
|
||||||
|
if self:has_left() then
|
||||||
|
self.inv:set_stack("left_hand", 1, new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:__armor(idx, name)
|
||||||
|
self[name] = function(self)
|
||||||
|
if self:has_armor() then
|
||||||
|
return self.inv:get_stack("armor", idx)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:__setter(name, function(self, new)
|
||||||
|
if self:has_armor() then
|
||||||
|
self.inv:set_stack("armor", idx, new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local armor_slots = {"helmet", "chestplace", "leggings", "boots"}
|
||||||
|
|
||||||
|
for i, name in ipairs(armor_slots) do
|
||||||
|
MCLEquipment:__armor(idx, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function insert(tbl, key, stack)
|
||||||
|
if stack:get_name() ~= "" then
|
||||||
|
tbl[key] = stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEquipment:get_armor()
|
||||||
|
local tbl = {}
|
||||||
|
if self:has_armor() then
|
||||||
|
for i, name in ipairs(armor_slots) do
|
||||||
|
insert(tbl, name, self.inv:get_stack("armor", i))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEquipment:get_all()
|
||||||
|
local tbl = {}
|
||||||
|
insert(tbl, "mainhand", self:mainhand())
|
||||||
|
insert(tbl, "offhand", self:offhand())
|
||||||
|
for k, v in pairs(self:get_armor()) do
|
||||||
|
tbl[k] = v
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# mcl_explosions
|
|
||||||
This mod provide helper functions to create explosions.
|
|
||||||
|
|
||||||
## mcl_explosions.explode(pos, strength, info, puncher)
|
|
||||||
* pos: position, initial position of the explosion
|
|
||||||
* strenght: number, radius of the explosion
|
|
||||||
* info: table, explosion informations:
|
|
||||||
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
|
|
||||||
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
|
|
||||||
* sound: bool, if true, the explosion will play a sound (default: true)
|
|
||||||
* particles: bool, if true, the explosion will create particles (default: true)
|
|
||||||
* fire: bool, if true, 1/3 nodes become fire (default: false)
|
|
||||||
* griefing: bool, if true, the explosion will destroy nodes (default: true)
|
|
||||||
* grief_protected: bool, if true, the explosion will also destroy nodes which have been protected (default: false)
|
|
||||||
* puncher: (optional) entity, will be used as source for damage done by the explosion
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
mcl_gamerules = {
|
||||||
|
__defaults = {},
|
||||||
|
__rules = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(mcl_gamerules, {__index = mcl_gamerules.__rules})
|
||||||
|
|
||||||
|
local worldpath = minetest.get_worldpath()
|
||||||
|
|
||||||
|
function mcl_gamerules.__load()
|
||||||
|
local file = io.open(worldpath .. "gamerules.json", "r")
|
||||||
|
if file then
|
||||||
|
local contents = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
local data = minetest.parse_json(contents)
|
||||||
|
local rules = mcl_gamerules.__rules
|
||||||
|
for rule, default in pairs(mcl_gamerules.__defaults) do
|
||||||
|
local value = data[rule]
|
||||||
|
if value == nil then
|
||||||
|
value = default
|
||||||
|
end
|
||||||
|
rules[rule] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__save()
|
||||||
|
local file = io.open(worldpath .. "gamerules.json", "w")
|
||||||
|
file:write(minetest.write_json(mcl_gamerules.__rules, true))
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__set(rule, value)
|
||||||
|
if not mcl_gamerules.__defaults[rule] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
mcl_gamerules.__rules[rule] = value
|
||||||
|
mcl_gamerules.__save()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__register(rule, default)
|
||||||
|
mcl_gamerules.__defaults[rule] = default
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_gamerules.__register("announceAdvancements", true)
|
||||||
|
mcl_gamerules.__register("commandBlockOutput", true)
|
||||||
|
mcl_gamerules.__register("doDaylightCycle", true)
|
||||||
|
mcl_gamerules.__register("doFireTick", true)
|
||||||
|
mcl_gamerules.__register("doImmediateRespawn", false)
|
||||||
|
mcl_gamerules.__register("doMobLoot", true)
|
||||||
|
mcl_gamerules.__register("doMobSpawning", true)
|
||||||
|
mcl_gamerules.__register("doTileDrops", true)
|
||||||
|
mcl_gamerules.__register("doWeatherCycle", true)
|
||||||
|
mcl_gamerules.__register("drowningDamage", true)
|
||||||
|
mcl_gamerules.__register("fallDamage", true)
|
||||||
|
mcl_gamerules.__register("fireDamage", true)
|
||||||
|
mcl_gamerules.__register("keepInventory", false)
|
||||||
|
mcl_gamerules.__register("logAdminCommands", true)
|
||||||
|
mcl_gamerules.__register("mobGriefing", true)
|
||||||
|
mcl_gamerules.__register("naturalRegeneration", true)
|
||||||
|
mcl_gamerules.__register("pvp", true)
|
||||||
|
mcl_gamerules.__register("showDeathMessages", true)
|
||||||
|
mcl_gamerules.__register("tntExplodes", true)
|
||||||
|
|
||||||
|
minetest.register_on_mods_loaded(mcl_gamerules.__load)
|
|
@ -0,0 +1,32 @@
|
||||||
|
mcl_groupcache = {
|
||||||
|
cache = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local function check_insert(item, group, cache)
|
||||||
|
if minetest.get_item_group(item, group) ~= 0 then
|
||||||
|
table.insert(cache, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_register_item = minetest.register_item
|
||||||
|
|
||||||
|
function minetest.register_item(name, def)
|
||||||
|
old_register_item(name, def)
|
||||||
|
for group, cache in pairs(mcl_groupcache.cache) do
|
||||||
|
check_insert(item, group, cache)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_groupcache.init_cache(group)
|
||||||
|
local cache = {}
|
||||||
|
for item in pairs(minetest.registered_items) do
|
||||||
|
check_insert(item, group, cache)
|
||||||
|
end
|
||||||
|
return cache
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_groupcache.get_items_in_group(group)
|
||||||
|
local cache = mcl_groupcache.cache[group] or mcl_groupcache.init_cache(group)
|
||||||
|
mcl_groupcache.cache[group] = cache
|
||||||
|
return cache
|
||||||
|
end
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = mcl_groupcache
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Keep track of items with certain groups
|
|
@ -21,9 +21,6 @@ mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;tru
|
||||||
-- Legacy
|
-- Legacy
|
||||||
mcl_vars.inventory_header = ""
|
mcl_vars.inventory_header = ""
|
||||||
|
|
||||||
-- Tool wield size
|
|
||||||
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
|
|
||||||
|
|
||||||
-- Mapgen variables
|
-- Mapgen variables
|
||||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||||
local minecraft_height_limit = 256
|
local minecraft_height_limit = 256
|
||||||
|
@ -36,26 +33,25 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, core.MAP_BLOCKSIZE or 16)
|
||||||
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
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, core.MAX_MAP_GENERATION_LIMIT or 31000)
|
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000)
|
||||||
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
||||||
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
local chunk_size_in_nodes = mcl_vars.chunksize * 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_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 central_chunk_max_pos = central_chunk_min_pos + chunk_size_in_nodes - 1
|
||||||
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
|
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 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_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_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
|
||||||
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
|
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 numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / 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.
|
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / 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_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
|
||||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
|
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
|
||||||
|
|
||||||
local function coordinate_to_block(x)
|
local function coordinate_to_block(x)
|
||||||
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function coordinate_to_chunk(x)
|
local function coordinate_to_chunk(x)
|
||||||
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_vars.pos_to_block(pos)
|
function mcl_vars.pos_to_block(pos)
|
||||||
|
@ -74,7 +70,7 @@ function mcl_vars.pos_to_chunk(pos)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes)
|
||||||
local k_positive_z = k_positive * 2
|
local k_positive_z = k_positive * 2
|
||||||
local k_positive_y = k_positive_z * k_positive_z
|
local k_positive_y = k_positive_z * k_positive_z
|
||||||
|
|
||||||
|
@ -178,86 +174,3 @@ minetest.craftitemdef_default.stack_max = 64
|
||||||
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
|
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
|
||||||
math.randomseed(os.time())
|
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,43 @@
|
||||||
|
MCLItemStack = class()
|
||||||
|
|
||||||
|
function MCLItemStack:constructor(stack)
|
||||||
|
self.stack = stack
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLItemStack:__getter("enchantments", function(self)
|
||||||
|
return mcl_enchanting.get_enchantments(self.stack)
|
||||||
|
end)
|
||||||
|
MCLItemStack:__comparator("enchantments", mcl_types.match_enchantments)
|
||||||
|
|
||||||
|
function MCLItemStack:meta()
|
||||||
|
return self.stack:get_meta()
|
||||||
|
end
|
||||||
|
MCLItemStack:__comparator("meta", mcl_types.match_meta)
|
||||||
|
|
||||||
|
function MCLItemStack:get_enchantment(name)
|
||||||
|
return self:enchantments()[name] or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:has_enchantment(name)
|
||||||
|
return self:get_enchantment(name) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:total_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:durability()
|
||||||
|
local def = self.stack:get_definition()
|
||||||
|
if def then
|
||||||
|
local base_uses = def._durability
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:use_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:restore_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:get_group()
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
MCLMetadata = class()
|
||||||
|
|
||||||
|
function MCLMetadata:constructor()
|
||||||
|
self.fields = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _type, default in pairs({string = "", float = 0.0, int = 0}) do
|
||||||
|
MCLMetadata["set_" .. _type] = function(name, value) do
|
||||||
|
if value == default then
|
||||||
|
value = nil
|
||||||
|
end
|
||||||
|
self.fields[name] = value
|
||||||
|
end
|
||||||
|
MCLMetadata["get_" .. _type] = function(name) do
|
||||||
|
return self.fields[name] or default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMetadata:to_table()
|
||||||
|
return table.copy(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMetadata:from_table(tbl)
|
||||||
|
self.fields = table.copy(tbl.fields)
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
MCLMob = class(MCLEntity)
|
||||||
|
|
||||||
|
function MCLMob:get_hp()
|
||||||
|
self:meta():get_float("hp")
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMob:set_hp()
|
||||||
|
self:meta():set_float("hp", hp)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMob:on_damage(damage, source)
|
||||||
|
MCLEntity.on_damage(self, damage, source)
|
||||||
|
|
||||||
|
local new_hp = self:get_hp()
|
||||||
|
if new_hp <= 0 and new_hp + damage > 0 then
|
||||||
|
self:on_death(source)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,121 @@
|
||||||
|
MCLObject = class()
|
||||||
|
|
||||||
|
function MCLObject:constructor(obj)
|
||||||
|
self.object = obj.object or obj
|
||||||
|
self.IS_MCL_OBJECT = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_punch(hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||||
|
local source = MCLDamageSource({is_punch = true, raw_source_object = hitter})
|
||||||
|
|
||||||
|
local knockback = {
|
||||||
|
hitter = hitter,
|
||||||
|
time_from_last_punch = time_from_last_punch,
|
||||||
|
tool_capabilities = tool_capabilities,
|
||||||
|
dir = dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
self:damage(damage, source, knockback)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:apply_knockback(strength, dir)
|
||||||
|
local oldvel = self.object:get_velocity()
|
||||||
|
local vel = vector.multiply(vector.normalize(vector.new(dir.x, 0, dir.z)), strength)
|
||||||
|
|
||||||
|
if self:is_on_ground() then
|
||||||
|
local old_y = oldvel.y / 2
|
||||||
|
|
||||||
|
y = math.min(0.4, old_y / 2 + strenth)
|
||||||
|
vel.y
|
||||||
|
end
|
||||||
|
|
||||||
|
vel = vector.subtract(vel, vector.divide(vector.new(oldvel.x, 0, oldvel.z), 2))
|
||||||
|
|
||||||
|
self.object:add_velocity(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
|
||||||
|
function MCLObject:damage(damage, source, knockback)
|
||||||
|
damage = self:damage_modifier(damage, source) or damage
|
||||||
|
self:set_hp(self:get_hp() - damage)
|
||||||
|
|
||||||
|
if type(knockback) == "table" then
|
||||||
|
knockback = self:calculate_knockback(
|
||||||
|
knockback.hitter,
|
||||||
|
knockback.time_from_last_punch,
|
||||||
|
knockback.tool_capabilities,
|
||||||
|
knockback.dir or vector.direction(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||||
|
knockback.distance or vector.distance(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||||
|
damage,
|
||||||
|
source)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self.damage_info, {
|
||||||
|
damage = damage,
|
||||||
|
source = source,
|
||||||
|
knockback = knockback,
|
||||||
|
})
|
||||||
|
|
||||||
|
return damage
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:wield_index()
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLObject:__getter("equipment", function(self)
|
||||||
|
return MCLEquipment(self:inventory(), self:wield_index())
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLObject:get_hp()
|
||||||
|
return self.object:get_hp()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:set_hp(hp)
|
||||||
|
self.object:set_hp(hp)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:death_drop(inventory, listname, index, stack)
|
||||||
|
minetest.add_item(self.object:get_pos(), stack)
|
||||||
|
inventory:set_stack(listname, index, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_death(source)
|
||||||
|
local inventory = self:inventory()
|
||||||
|
if inventory then
|
||||||
|
for listname, list in pairs(inventory:get_lists()) do
|
||||||
|
for index, stack in pairs(list) do
|
||||||
|
if stack:get_name() ~= "" and then
|
||||||
|
self:death_drop(inventory, listname, index, stack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:damage_modifier(damage, source)
|
||||||
|
if self.invulnerable and not source.bypasses_invulnerability then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_damage(damage, source, knockback)
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLObject.calculate_knockback = minetest.calculate_knockback
|
||||||
|
|
||||||
|
function minetest.calculate_knockback()
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_step()
|
||||||
|
local damage_info = self.damage_info
|
||||||
|
if damage_info then
|
||||||
|
self.damage_info = nil
|
||||||
|
self:on_damage(damage_info.damage, damage_info.source)
|
||||||
|
|
||||||
|
if damage_info.knockback then
|
||||||
|
self.object:add_velocity(damage_info.knockback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,121 @@
|
||||||
|
mcl_object_mgr = {
|
||||||
|
players = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- functions
|
||||||
|
|
||||||
|
function mcl_object_mgr.get(obj)
|
||||||
|
local rval
|
||||||
|
|
||||||
|
if mcl_object_mgr.is_mcl_object(obj) then
|
||||||
|
rval = obj
|
||||||
|
elseif mcl_object_mgr.is_player(obj) then
|
||||||
|
rval = mcl_object_mgr.get_player(obj)
|
||||||
|
elseif mcl_object_mgr.is_entity(obj) then
|
||||||
|
rval = mcl_object_mgr.get_entity(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
return assert(rval, "No matching MCLObject found. This is most likely an error caused by custom mods.")
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_mcl_object(obj)
|
||||||
|
return type(obj) == "table" and obj.IS_MCL_OBJECT
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_player(obj)
|
||||||
|
return type(obj) == "string" or type(obj) == "userdata" and obj:is_player()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_is_entity(obj)
|
||||||
|
return type(obj) == "table" and obj.object or type(obj) == "userdata" and obj:get_luaentity()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.get_entity(ent)
|
||||||
|
if type(ent) == "userdata" then
|
||||||
|
ent = ent:get_luaentity()
|
||||||
|
end
|
||||||
|
return ent.mcl_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.get_player(name)
|
||||||
|
if type(name) == "userdata" then
|
||||||
|
name = name:get_player_name()
|
||||||
|
end
|
||||||
|
return mcl_player_mgr.players[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- entity wrappers
|
||||||
|
|
||||||
|
local function add_entity_wrapper(def, name)
|
||||||
|
def[name] = function(luaentity, ...)
|
||||||
|
local func = self.mcl_entity[name]
|
||||||
|
if func then
|
||||||
|
return func(self.mcl_entity, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.register_entity(name, initial_properties, base_class)
|
||||||
|
local def = {
|
||||||
|
initial_properties = initial_properties,
|
||||||
|
|
||||||
|
on_activate = function(self, ...)
|
||||||
|
local entity = base_class(self.object)
|
||||||
|
self.mcl_entity = entity
|
||||||
|
if entity.on_activate then
|
||||||
|
entity:on_activate(...)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
add_entity_wrapper(def, "on_deactivate")
|
||||||
|
add_entity_wrapper(def, "on_step")
|
||||||
|
add_entity_wrapper(def, "on_punch")
|
||||||
|
add_entity_wrapper(def, "on_death")
|
||||||
|
add_entity_wrapper(def, "on_rightclick")
|
||||||
|
add_entity_wrapper(def, "on_attach_child")
|
||||||
|
add_entity_wrapper(def, "on_detach_child")
|
||||||
|
add_entity_wrapper(def, "on_detach")
|
||||||
|
add_entity_wrapper(def, "get_staticdata")
|
||||||
|
|
||||||
|
minetest.register_entity(name, def)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- player wrappers
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
mcl_player_mgr.players[name] = MCLPlayer(player)
|
||||||
|
mcl_player_mgr.players[name]:on_join()
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
mcl_player_mgr.players[name]:on_leave()
|
||||||
|
mcl_player_mgr.players[name] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function add_player_wrapper(wrapper, regfunc)
|
||||||
|
minetest[regfunc or "register_" .. wrapper .. "player"](function(player, ...)
|
||||||
|
local mclplayer = mcl_player_mgr.players[player:get_player_name()]
|
||||||
|
local func = mclplayer[funcname or wrapper]
|
||||||
|
if func then
|
||||||
|
func(mclplayer, ...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
add_player_wrapper("on_punch")
|
||||||
|
add_player_wrapper("on_rightclick")
|
||||||
|
add_player_wrapper("on_death", "register_on_dieplayer")
|
||||||
|
add_player_wrapper("on_respawn")
|
||||||
|
|
||||||
|
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||||
|
if not reason.auto then
|
||||||
|
local mclplayer = mcl_object_mgr.get(player)
|
||||||
|
local source = MCLDamageSource():from_mt(reason)
|
||||||
|
|
||||||
|
mclplayer:damage(hp_change, source)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end, true)
|
|
@ -66,58 +66,3 @@ function mcl_particles.delete_node_particlespawners(pos)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 3 exptime variants because the animation is not tied to particle expiration time.
|
|
||||||
-- 3 colorized variants to imitate minecraft's
|
|
||||||
local smoke_pdef_cached = {}
|
|
||||||
|
|
||||||
function mcl_particles.spawn_smoke(pos, name, smoke_pdef_base)
|
|
||||||
local min = math.min
|
|
||||||
local new_minpos = vector.add(pos, smoke_pdef_base.minrelpos)
|
|
||||||
local new_maxpos = vector.add(pos, smoke_pdef_base.maxrelpos)
|
|
||||||
|
|
||||||
-- populate the cache
|
|
||||||
if smoke_pdef_cached[name] then
|
|
||||||
for i, smoke_pdef in ipairs(smoke_pdef_cached[name]) do
|
|
||||||
smoke_pdef.minpos = new_minpos
|
|
||||||
smoke_pdef.maxpos = new_maxpos
|
|
||||||
mcl_particles.add_node_particlespawner(pos, smoke_pdef, "high")
|
|
||||||
end
|
|
||||||
-- cache already populated
|
|
||||||
else
|
|
||||||
smoke_pdef_cached[name] = {}
|
|
||||||
|
|
||||||
local smoke_pdef = table.copy(smoke_pdef_base)
|
|
||||||
smoke_pdef.amount = smoke_pdef_base.amount / 9
|
|
||||||
smoke_pdef.time = 0
|
|
||||||
smoke_pdef.animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 8,
|
|
||||||
aspect_h = 8,
|
|
||||||
-- length = 3 exptime variants
|
|
||||||
}
|
|
||||||
smoke_pdef.collisiondetection = true
|
|
||||||
smoke_pdef.minpos = new_minpos
|
|
||||||
smoke_pdef.maxpos = new_maxpos
|
|
||||||
|
|
||||||
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
|
|
||||||
-- to have varying exptime for each variant.
|
|
||||||
local exptimes = { 0.175, 0.375, 1.0 }
|
|
||||||
local colorizes = { "199", "209", "243" } -- round(78%, 82%, 90% of 256) - 1
|
|
||||||
|
|
||||||
for _,exptime in ipairs(exptimes) do
|
|
||||||
for _,colorize in ipairs(colorizes) do
|
|
||||||
smoke_pdef.maxexptime = exptime * smoke_pdef_base.maxexptime
|
|
||||||
smoke_pdef.animation.length = exptime + 0.1
|
|
||||||
-- minexptime must be set such that the last frame is actully rendered,
|
|
||||||
-- even if its very short. Larger exptime -> larger range
|
|
||||||
smoke_pdef.minexptime = min(exptime, (7.0/8.0 * (exptime + 0.1) + 0.1))
|
|
||||||
smoke_pdef.texture = "mcl_particles_smoke_anim.png^[colorize:#000000:" ..colorize
|
|
||||||
|
|
||||||
mcl_particles.add_node_particlespawner(pos, smoke_pdef, "high")
|
|
||||||
|
|
||||||
table.insert(smoke_pdef_cached[name], table.copy(smoke_pdef))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
MCLPlayer = class(MCLObject)
|
||||||
|
|
||||||
|
MCLPlayer:__cache_getter("meta", function(self)
|
||||||
|
return self.object:get_meta()
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLPlayer:__cache_getter("inventory", function(self)
|
||||||
|
return self.object:get_inventory()
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
|
||||||
|
if not mcl_gamerules.keepInventory then
|
||||||
|
return self, inventory, listname, index, stack
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLPlayer:on_damage(damage, source, knockback)
|
||||||
|
MCLObject.on_damage(self, damage, source, knockback)
|
||||||
|
end
|
|
@ -410,7 +410,7 @@ function mcl_util.get_color(colorstr)
|
||||||
local mc_color = mcl_colors[colorstr:upper()]
|
local mc_color = mcl_colors[colorstr:upper()]
|
||||||
if mc_color then
|
if mc_color then
|
||||||
colorstr = mc_color
|
colorstr = mc_color
|
||||||
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#" then
|
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#"then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local hex = tonumber(colorstr:sub(2, 7), 16)
|
local hex = tonumber(colorstr:sub(2, 7), 16)
|
||||||
|
@ -418,3 +418,31 @@ function mcl_util.get_color(colorstr)
|
||||||
return colorstr, hex
|
return colorstr, hex
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mcl_util.registration_function(tbl, func)
|
||||||
|
return function(name, def)
|
||||||
|
if func then
|
||||||
|
local res = func(name, def)
|
||||||
|
if res == false then
|
||||||
|
return
|
||||||
|
elseif res ~= nil then
|
||||||
|
def = res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tbl[name] = def
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.rand(pr, ...)
|
||||||
|
return pr and pr:next(...) or math.random(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.rand_bool(probability, pr)
|
||||||
|
if probability >= 1 then
|
||||||
|
return true
|
||||||
|
elseif probability <= 0 then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return mcl_util.rand(pr, 0, 32767) < probability * 32768
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
# mcl_worlds
|
|
||||||
This mod provides utility functions about positions and dimensions.
|
|
||||||
|
|
||||||
## mcl_worlds.is_in_void(pos)
|
|
||||||
This function returns:
|
|
||||||
|
|
||||||
* true, true: if pos is in deep void (deadly)
|
|
||||||
* true, false: if the pos is in void (non deadly)
|
|
||||||
* false, false: owerwise
|
|
||||||
|
|
||||||
Params:
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.y_to_layer(y)
|
|
||||||
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
|
||||||
Mainly used for ore generation.
|
|
||||||
Takes an Y coordinate as input and returns:
|
|
||||||
|
|
||||||
* The corresponding Minecraft layer (can be nil if void)
|
|
||||||
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if <y> is in the void
|
|
||||||
If the Y coordinate is not located in any dimension, it will return: nil, "void"
|
|
||||||
|
|
||||||
Params:
|
|
||||||
|
|
||||||
* y: int
|
|
||||||
|
|
||||||
## mcl_worlds.pos_to_dimension(pos)
|
|
||||||
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.layer_to_y(layer, mc_dimension)
|
|
||||||
Takes a Minecraft layer and a “dimension” name and returns the corresponding Y coordinate for MineClone 2.
|
|
||||||
mc_dimension can be "overworld", "nether", "end" (default: "overworld").
|
|
||||||
|
|
||||||
* layer: int
|
|
||||||
* mc_dimension: string
|
|
||||||
|
|
||||||
## mcl_worlds.has_weather(pos)
|
|
||||||
Returns true if <pos> can have weather, false owerwise.
|
|
||||||
Weather can be only in the overworld.
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.has_dust(pos)
|
|
||||||
Returns true if <pos> can have nether dust, false owerwise.
|
|
||||||
Nether dust can be only in the nether.
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.compass_works(pos)
|
|
||||||
Returns true if compasses are working at <pos>, false owerwise.
|
|
||||||
In mc, you cant use compass in the nether and the end.
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.compass_works(pos)
|
|
||||||
Returns true if clock are working at <pos>, false owerwise.
|
|
||||||
In mc, you cant use clock in the nether and the end.
|
|
||||||
|
|
||||||
* pos: position
|
|
||||||
|
|
||||||
## mcl_worlds.register_on_dimension_change(function(player, dimension))
|
|
||||||
Register a callback function func(player, dimension).
|
|
||||||
It will be called whenever a player changes between dimensions.
|
|
||||||
The void counts as dimension.
|
|
||||||
|
|
||||||
* player: player, the player who changed the dimension
|
|
||||||
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
|
|
||||||
|
|
||||||
|
|
||||||
## mcl_worlds.registered_on_dimension_change
|
|
||||||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
|
||||||
|
|
||||||
## mcl_worlds.dimension_change(player, dimension)
|
|
||||||
Notify this mod of a dimmension change of <player> to <dimension>
|
|
||||||
|
|
||||||
* player: player, player who changed the dimension
|
|
||||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
|
@ -6,7 +6,7 @@ mcl_worlds = {}
|
||||||
function mcl_worlds.is_in_void(pos)
|
function mcl_worlds.is_in_void(pos)
|
||||||
local void =
|
local void =
|
||||||
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
|
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_nether_max and pos.y > mcl_vars.mg_nether_min) or
|
||||||
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
|
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
|
||||||
|
|
||||||
local void_deadly = false
|
local void_deadly = false
|
||||||
|
@ -15,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
|
||||||
-- Overworld → Void → End → Void → Nether → Void
|
-- Overworld → Void → End → Void → Nether → Void
|
||||||
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
|
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
|
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
|
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
|
||||||
-- The void between End and Nether. Like usual, but here, the void
|
-- The void between End and Nether. Like usual, but here, the void
|
||||||
-- *above* the Nether also has a small tolerance area, so player
|
-- *above* the Nether also has a small tolerance area, so player
|
||||||
-- can fly above the Nether without getting hurt instantly.
|
-- 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)
|
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
|
||||||
elseif pos.y < mcl_vars.mg_nether_min then
|
elseif pos.y < mcl_vars.mg_nether_min then
|
||||||
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
|
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
|
||||||
end
|
end
|
||||||
|
@ -35,7 +35,7 @@ end
|
||||||
function mcl_worlds.y_to_layer(y)
|
function mcl_worlds.y_to_layer(y)
|
||||||
if y >= mcl_vars.mg_overworld_min then
|
if y >= mcl_vars.mg_overworld_min then
|
||||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then
|
||||||
return y - mcl_vars.mg_nether_min, "nether"
|
return y - mcl_vars.mg_nether_min, "nether"
|
||||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||||
return y - mcl_vars.mg_end_min, "end"
|
return y - mcl_vars.mg_end_min, "end"
|
||||||
|
@ -73,7 +73,7 @@ end
|
||||||
-- Takes a position and returns true if this position can have Nether dust
|
-- Takes a position and returns true if this position can have Nether dust
|
||||||
function mcl_worlds.has_dust(pos)
|
function mcl_worlds.has_dust(pos)
|
||||||
-- Weather in the Overworld and the high part of the void below
|
-- 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 <= mcl_vars.mg_nether_max + 64 and pos.y >= mcl_vars.mg_nether_min - 64
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Takes a position (pos) and returns true if compasses are working here
|
-- Takes a position (pos) and returns true if compasses are working here
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
mcl_loottables.register_entry = mcl_util.registration_function(mcl_loottables.entries)
|
||||||
|
mcl_loottables.register_table = mcl_util.registration_function(mcl_loottables.tables, function(name, def)
|
||||||
|
local function set_parents(parent)
|
||||||
|
for _, child in ipairs(parent.children or parent.entries or parent.pools or {}) do
|
||||||
|
child.parent = parent
|
||||||
|
set_parents(child)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set_parents(def)
|
||||||
|
end)
|
||||||
|
|
||||||
|
function mcl_loottables.get_entry_type(entry)
|
||||||
|
return mcl_loottables.entries[entry.type]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.get_candidates(entries, data, func)
|
||||||
|
local candidates = {}
|
||||||
|
for _, entry in ipairs(entries) do
|
||||||
|
local success = mcl_predicates.do_predicates(entry.conditions, data)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
local children = entry.children
|
||||||
|
|
||||||
|
if children then
|
||||||
|
table.insert_all(candidates, mcl_loottables.get_candidates(children, data, mcl_loottables.get_entry_type(entry).preprocess))
|
||||||
|
else
|
||||||
|
table.insert(candidates, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if func and func(success, data) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return candidates
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.do_item_modifiers(itemstack, node, data)
|
||||||
|
if node then
|
||||||
|
mcl_item_modifiers.do_item_modifiers(itemstack, node.functions, data)
|
||||||
|
mcl_loottables.do_item_modifiers(itemstack, node.parent, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.do_pools(pools, functions, data)
|
||||||
|
local luck = data.luck or 0
|
||||||
|
local stacks = {}
|
||||||
|
for _, pool in ipairs(pools or {}) do
|
||||||
|
if mcl_predicates.do_predicates(pool.conditions, data) do
|
||||||
|
local rolls = mcl_numbers.get_number(pool.rolls, data) + mcl_numbers.get_number(pool.bonus_rolls, data) * luck
|
||||||
|
for i = 1, rolls do
|
||||||
|
local candidates = mcl_loottables.get_candidates(pool.entries, data)
|
||||||
|
|
||||||
|
if #candidates > 0 then
|
||||||
|
local total_weight = 0
|
||||||
|
local weights = {}
|
||||||
|
for _, candidate in ipairs(candidates)
|
||||||
|
total_weight = total_weight + math.floor((candidate.weight or 1) + (candidate.quality or 0) * luck)
|
||||||
|
table.insert(weights, total_weight)
|
||||||
|
end
|
||||||
|
|
||||||
|
local selected
|
||||||
|
local rnd = mcl_util.rand(data.pr, 0, weight - 1)
|
||||||
|
for i, w in ipairs(weights) do
|
||||||
|
if rnd < w then
|
||||||
|
selected = candidates[i]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local pool_stacks = mcl_loottables.get_entry_type(entry).process(selected, data)
|
||||||
|
|
||||||
|
for _, stack in ipairs(pool_stacks) do
|
||||||
|
mcl_item_modifiers.do_item_modifiers(stack, selected, data)
|
||||||
|
end
|
||||||
|
table.insert_all(stacks, pool_stacks)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return stacks
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.get_loot(def, data)
|
||||||
|
if type(def) == "string" then
|
||||||
|
def = mcl_loottables.tables[def]
|
||||||
|
end
|
||||||
|
return mcl_loottables.do_pools(def.pools)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.drop_loot(def, data)
|
||||||
|
local loot = mcl_loottables.get_loot(def, data)
|
||||||
|
local old_loot = table.copy(loot)
|
||||||
|
for _, stack in ipairs(old_loot) do
|
||||||
|
local max_stack = stack:get_stack_max()
|
||||||
|
while max_stack < stack:get_count() do
|
||||||
|
table.insert(loot, stack:take_items(max_stack))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return loot
|
||||||
|
end
|
|
@ -0,0 +1,44 @@
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:alternatives", {
|
||||||
|
preprocess = function(success, data)
|
||||||
|
return success
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:group", {
|
||||||
|
preprocess = function(success, data)
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:sequence", {
|
||||||
|
preprocess = function(success, data)
|
||||||
|
return not success
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:tag", function(entry, data)
|
||||||
|
local stacks = mcl_groupcache.get_items_in_group(entry.name)
|
||||||
|
if entry.expand then
|
||||||
|
stacks = {stacks[pr:next(1, #stacks)]}
|
||||||
|
end
|
||||||
|
return stacks
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:loot_table", {
|
||||||
|
process = function(entry, data)
|
||||||
|
return mcl_loottables.get_loot(entry.name, data)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:empty", {
|
||||||
|
process = function(entry, data)
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_loottables.register_entry("mcl_loottables:item", {
|
||||||
|
process = function(entry, data)
|
||||||
|
return {item = ItemStack(entry.name)}
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
mcl_loottables = {
|
||||||
|
tables = {},
|
||||||
|
entries = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath("mcl_loottables")
|
||||||
|
|
||||||
|
dofile(modpath .. "/api.lua")
|
||||||
|
dofile(modpath .. "/entries.lua")
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_loottables
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Provides Minecraft-like loot table definitions
|
||||||
|
depends = mcl_util, mcl_predicates, mcl_item_modifiers, mcl_groupcache
|
|
@ -0,0 +1,19 @@
|
||||||
|
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
|
||||||
|
|
||||||
|
function mcl_numbers.get_number(provider, data)
|
||||||
|
if type(provider) == "number" then
|
||||||
|
return provider
|
||||||
|
else
|
||||||
|
mcl_numbers.providers[data.type](provider, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_numbers.match_bounds(actual, expected, data)
|
||||||
|
if type(expected) == "table" then
|
||||||
|
expected = {
|
||||||
|
min = mcl_numbers.get_number(expected.min, data),
|
||||||
|
max = mcl_numbers.get_number(expected.max, data)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return mcl_util.match_bounds(actual, expected)
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
mcl_numbers = {
|
||||||
|
providers = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
dofile(minetest.get_modpath("mcl_numbers") .. "/api.lua")
|
||||||
|
|
||||||
|
mcl_numbers.register_provider("mcl_numbers:constant", function(provider)
|
||||||
|
return provider.value
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_numbers.register_provider("mcl_numbers:uniform", function(provider, data)
|
||||||
|
return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min, data), mcl_numbers.get_number(provider.max, data))
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data)
|
||||||
|
local n = mcl_numbers.get_number(provider.n, data)
|
||||||
|
local num = 0
|
||||||
|
for i = 1, n do
|
||||||
|
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return num
|
||||||
|
end)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_numbers
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Minecraft-like number providers
|
||||||
|
depends = mcl_util
|
|
@ -0,0 +1,15 @@
|
||||||
|
mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates)
|
||||||
|
|
||||||
|
function mcl_predicates.do_predicates(predicates, data, or_mode)
|
||||||
|
or_mode = or_mode or false
|
||||||
|
for _, func in ipairs(predicates) do
|
||||||
|
if type(func) == "string" then
|
||||||
|
func = mcl_predicates.predicates[func]
|
||||||
|
end
|
||||||
|
local failure = func and not func(def, data) or false
|
||||||
|
if or_mode ~= failure then
|
||||||
|
return or_mode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return not or_mode or #predicates == 0
|
||||||
|
end
|
|
@ -0,0 +1,104 @@
|
||||||
|
mcl_predicates = {
|
||||||
|
predicates = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath("mcl_predicates")
|
||||||
|
|
||||||
|
dofile(modpath .. "/api.lua")
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("alternative", function(predicate, data)
|
||||||
|
return mcl_predicates.do_predicates(predicate.terms, data, true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("match_node", function(predicate, data)
|
||||||
|
return mcl_types.match_node(data.node or minetest.get_node(data.pos), predicate, data.pos, data.nodemeta)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("damage_source_properties", function(predicate, data)
|
||||||
|
-- ToDo: damage source abstraction
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("entity_properties", function(predicate, data)
|
||||||
|
local entity = predicate.entity
|
||||||
|
if not entity or (entity ~= "this" and entity ~= "killer" and entity ~= "killer_player") then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local ref = data[entity]
|
||||||
|
if not ref then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ToDo: entity / player abstraction
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("entity_scores", function(predicate, data)
|
||||||
|
-- ToDo: scoreboards
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("inverted", function(predicate, data)
|
||||||
|
return not mcl_predicates.do_predicates({predicate.term}, data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("killed_by_player", function(predicate, data)
|
||||||
|
return not predicate.inverse ~= not data.killer_player
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("location_check", function(predicate, data)
|
||||||
|
local pos = vector.add(data.pos, vector.new(predicate.offset_x or 0, predicate.offset_y or 0, predicate.offset_z or 0))
|
||||||
|
return mcl_location(pos, data.nodemeta):match(predicate.predicate)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("match_tool", function(predicate, data)
|
||||||
|
return mcl_predicates.match_tool(data.tool, predicate.predicate)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("random_chance", function(predicate, data)
|
||||||
|
return mcl_util.rand_bool(predicate.chance, data.pr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("random_chance_with_looting", function(predicate, data)
|
||||||
|
local chance = predicate.chance -- + (looting_level * looting_multiplier)
|
||||||
|
|
||||||
|
-- ToDo: entity / player abstraction
|
||||||
|
return mcl_util.rand_bool(chance, data.pr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("reference", function(predicate, data)
|
||||||
|
-- ToDo: needs research
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("survives_explosion", function(predicate, data)
|
||||||
|
return mcl_util.rand_bool(data.drop_chance or 1, data.pr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("table_bonus", function(predicate, data)
|
||||||
|
-- ToDo: entity / player abstraction
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("time_check", function(predicate, data)
|
||||||
|
-- ToDo: needs research
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("weather_check", function(predicate, data)
|
||||||
|
local weather = mcl_weather.get_weather()
|
||||||
|
|
||||||
|
if predicate.thundering then
|
||||||
|
return weather == "thunder"
|
||||||
|
elseif predicate.raining then
|
||||||
|
return weather == "rain"
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
mcl_predicates.register_predicate("value_check", function(predicate, data)
|
||||||
|
local value = mcl_numbers.get_number(predicate.value, data)
|
||||||
|
return mcl_numbers.check_in_bounds(value, predicate.range, data)
|
||||||
|
end)
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_predicates
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Provides MC-like predicates
|
||||||
|
depends = mcl_util, mcl_numbers
|
|
@ -0,0 +1,88 @@
|
||||||
|
mcl_types = {}
|
||||||
|
|
||||||
|
function mcl_types.match_bounds(actual, expected)
|
||||||
|
if type(expected) == "table" then
|
||||||
|
return actual <= expected.max and actual >= expected.min
|
||||||
|
else
|
||||||
|
return actual == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_meta(actual, expected)
|
||||||
|
for k, v in pairs(expected) do
|
||||||
|
if actual:get_string(k) ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_vector(actual, expected)
|
||||||
|
for k, v in pairs(expected) do
|
||||||
|
if not mcl_types.match_bounds(actual[k], expected[k]) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_enchantments(actual, expected)
|
||||||
|
for _, v in ipairs(expected) do
|
||||||
|
if not mcl_types.match_bounds(actual[v.enchantment] or 0, v.levels or {min = 1, max = math.huge}) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_item(actual, expected)
|
||||||
|
actual = actual or ItemStack()
|
||||||
|
|
||||||
|
if expected.item and actual:get_name() ~= expected.item then
|
||||||
|
return false
|
||||||
|
elseif expected.group and minetest.get_item_group(actual:get_name(), expected.group) == 0 then
|
||||||
|
return false
|
||||||
|
elseif expected.count and not mcl_types.match_bounds(actual:get_count(), expected.count) then
|
||||||
|
return false
|
||||||
|
elseif expected.wear and not mcl_types.match_bounds(actual:get_wear(), expected.wear) then
|
||||||
|
return false
|
||||||
|
elseif expected.enchantments and not mcl_types.match_enchantments(mcl_enchanting.get_enchantments(actual), expected.enchantments) then
|
||||||
|
return false
|
||||||
|
elseif expected.meta and not mcl_types.match_meta(actual:get_meta(), expected.meta) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_node(actual, expected, pos, meta)
|
||||||
|
if expected.node and actual.name ~= expected.node then
|
||||||
|
return false
|
||||||
|
elseif expected.group and minetest.get_item_group(actual.name, expected.group) == 0 then
|
||||||
|
return false
|
||||||
|
elseif expected.param1 and actual.param1 ~= compare.param1 then
|
||||||
|
return false
|
||||||
|
elseif expected.param2 and actual.param2 ~= compare.param2 then
|
||||||
|
return false
|
||||||
|
elseif expected.meta and not mcl_types.match_meta(meta or minetest.get_meta(pos), expected.meta) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_pos(actual, expected, meta)
|
||||||
|
if expected.pos and not mcl_types.match_vector(actual, expected.pos) then
|
||||||
|
return false
|
||||||
|
elseif expected.dimension and mcl_worlds.pos_to_dimension(actual) ~= expected.dimension then
|
||||||
|
return false
|
||||||
|
elseif expected.biome and minetest.get_biome_name(minetest.get_biome_data(actual).biome) ~= expected.biome then
|
||||||
|
return false
|
||||||
|
elseif expected.node and not mcl_types.match_node(minetest.get_node(actual), expected.node, actual, meta) then
|
||||||
|
return false
|
||||||
|
elseif expected.light and not mcl_types.match_bounds(minetest.get_node_light(actual), expected.light) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_types
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Offers compare functions for many types, used for MineClone2 datapacks
|
||||||
|
depends =
|
|
@ -0,0 +1,2 @@
|
||||||
|
name = DATA
|
||||||
|
description = Meta-modpack containing the datapack system for MineClone 2
|
|
@ -1,6 +1,6 @@
|
||||||
local S = minetest.get_translator("mcl_boats")
|
local S = minetest.get_translator("mcl_boats")
|
||||||
|
|
||||||
local boat_visual_size = {x = 1, y = 1, z = 1}
|
local boat_visual_size = {x = 3, y = 3, z = 3}
|
||||||
local paddling_speed = 22
|
local paddling_speed = 22
|
||||||
local boat_y_offset = 0.35
|
local boat_y_offset = 0.35
|
||||||
local boat_y_offset_ground = boat_y_offset + 0.6
|
local boat_y_offset_ground = boat_y_offset + 0.6
|
||||||
|
@ -12,7 +12,9 @@ local function is_group(pos, group)
|
||||||
return minetest.get_item_group(nn, group) ~= 0
|
return minetest.get_item_group(nn, group) ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_water = flowlib.is_water
|
local function is_water(pos)
|
||||||
|
return is_group(pos, "water")
|
||||||
|
end
|
||||||
|
|
||||||
local function is_ice(pos)
|
local function is_ice(pos)
|
||||||
return is_group(pos, "ice")
|
return is_group(pos, "ice")
|
||||||
|
@ -245,7 +247,7 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
else
|
else
|
||||||
local ctrl = self._passenger:get_player_control()
|
local ctrl = self._passenger:get_player_control()
|
||||||
if ctrl and ctrl.sneak then
|
if ctrl and ctrl.sneak then
|
||||||
detach_object(self._passenger, true)
|
detach_player(self._passenger, true)
|
||||||
self._passenger = nil
|
self._passenger = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib
|
depends = mcl_player
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -1,8 +1,5 @@
|
||||||
local S = minetest.get_translator("mcl_falling_nodes")
|
local S = minetest.get_translator("mcl_falling_nodes")
|
||||||
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
|
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
|
||||||
local has_mcl_armor = minetest.get_modpath("mcl_armor")
|
|
||||||
|
|
||||||
local is_creative_enabled = minetest.is_creative_enabled
|
|
||||||
|
|
||||||
local get_falling_depth = function(self)
|
local get_falling_depth = function(self)
|
||||||
if not self._startpos then
|
if not self._startpos then
|
||||||
|
@ -16,8 +13,9 @@ local deal_falling_damage = function(self, dtime)
|
||||||
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Cause damage to any entity it hits.
|
-- Cause damage to any player it hits.
|
||||||
-- Algorithm based on MC anvils.
|
-- Algorithm based on MC anvils.
|
||||||
|
-- TODO: Support smashing other objects, too.
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not self._startpos then
|
if not self._startpos then
|
||||||
-- Fallback
|
-- Fallback
|
||||||
|
@ -25,39 +23,30 @@ local deal_falling_damage = function(self, dtime)
|
||||||
end
|
end
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 1)
|
local objs = minetest.get_objects_inside_radius(pos, 1)
|
||||||
for _,v in ipairs(objs) do
|
for _,v in ipairs(objs) do
|
||||||
if v:is_player() then
|
local hp = v:get_hp()
|
||||||
local hp = v:get_hp()
|
if v:is_player() and hp ~= 0 then
|
||||||
|
if not self._hit_players then
|
||||||
|
self._hit_players = {}
|
||||||
|
end
|
||||||
local name = v:get_player_name()
|
local name = v:get_player_name()
|
||||||
if hp ~= 0 then
|
local hit = false
|
||||||
if not self._hit_players then
|
for _,v in ipairs(self._hit_players) do
|
||||||
self._hit_players = {}
|
if name == v then
|
||||||
|
hit = true
|
||||||
end
|
end
|
||||||
local hit = false
|
end
|
||||||
for _,v in ipairs(self._hit_players) do
|
if not hit then
|
||||||
if name == v then
|
table.insert(self._hit_players, name)
|
||||||
hit = true
|
local way = self._startpos.y - pos.y
|
||||||
|
local damage = (way - 1) * 2
|
||||||
|
damage = math.min(40, math.max(0, damage))
|
||||||
|
if damage >= 1 then
|
||||||
|
hp = hp - damage
|
||||||
|
if hp < 0 then
|
||||||
|
hp = 0
|
||||||
end
|
end
|
||||||
end
|
if v:is_player() then
|
||||||
if not hit then
|
-- TODO: Reduce damage if wearing a helmet
|
||||||
table.insert(self._hit_players, name)
|
|
||||||
local way = self._startpos.y - pos.y
|
|
||||||
local damage = (way - 1) * 2
|
|
||||||
damage = math.min(40, math.max(0, damage))
|
|
||||||
if damage >= 1 then
|
|
||||||
hp = hp - damage
|
|
||||||
if hp < 0 then
|
|
||||||
hp = 0
|
|
||||||
end
|
|
||||||
-- Reduce damage if wearing a helmet
|
|
||||||
local inv = v:get_inventory()
|
|
||||||
local helmet = inv:get_stack("armor", 2)
|
|
||||||
if has_mcl_armor and not helmet:is_empty() then
|
|
||||||
hp = hp/4*3
|
|
||||||
if not is_creative_enabled(name) then
|
|
||||||
helmet:add_wear(65535/helmet:get_definition().groups.mcl_armor_uses) --TODO: be sure damage is exactly like mc (informations are missing in the mc wiki)
|
|
||||||
inv:set_stack("armor", 2, helmet)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local msg
|
local msg
|
||||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||||
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
||||||
|
@ -67,35 +56,8 @@ local deal_falling_damage = function(self, dtime)
|
||||||
if dmes then
|
if dmes then
|
||||||
mcl_death_messages.player_damage(v, msg)
|
mcl_death_messages.player_damage(v, msg)
|
||||||
end
|
end
|
||||||
v:set_hp(hp, { type = "punch", from = "mod" })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local hp = v:get_luaentity().health
|
|
||||||
if hp and hp ~= 0 then
|
|
||||||
if not self._hit_mobs then
|
|
||||||
self._hit_mobs = {}
|
|
||||||
end
|
|
||||||
local hit = false
|
|
||||||
for _,mob in ipairs(self._hit_mobs) do
|
|
||||||
if v == mob then
|
|
||||||
hit = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--TODO: reduce damage for mobs then they will be able to wear armor
|
|
||||||
if not hit then
|
|
||||||
table.insert(self._hit_mobs, v)
|
|
||||||
local way = self._startpos.y - pos.y
|
|
||||||
local damage = (way - 1) * 2
|
|
||||||
damage = math.min(40, math.max(0, damage))
|
|
||||||
if damage >= 1 then
|
|
||||||
hp = hp - damage
|
|
||||||
if hp < 0 then
|
|
||||||
hp = 0
|
|
||||||
end
|
|
||||||
v:get_luaentity().health = hp
|
|
||||||
end
|
end
|
||||||
|
v:set_hp(hp, { type = "punch", from = "mod" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +1,10 @@
|
||||||
--these are lua locals, used for higher performance
|
|
||||||
local minetest,math,vector,ipairs = minetest,math,vector,ipairs
|
|
||||||
|
|
||||||
--this is used for the player pool in the sound buffer
|
|
||||||
local pool = {}
|
|
||||||
|
|
||||||
local tick = false
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
local name
|
|
||||||
name = player:get_player_name()
|
|
||||||
pool[name] = 0
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
local name
|
|
||||||
name = player:get_player_name()
|
|
||||||
pool[name] = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
local has_awards = minetest.get_modpath("awards")
|
|
||||||
|
|
||||||
local mcl_item_entity = {}
|
|
||||||
|
|
||||||
--basic settings
|
--basic settings
|
||||||
local item_drop_settings = {} --settings table
|
local item_drop_settings = {} --settings table
|
||||||
item_drop_settings.dug_buffer = 0.65 -- the warm up period before a dug item can be collected
|
|
||||||
item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting
|
item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting
|
||||||
item_drop_settings.radius_magnet = 2.0 --radius of item magnet. MUST BE LARGER THAN radius_collect!
|
item_drop_settings.radius_magnet = 2.0 --radius of item magnet. MUST BE LARGER THAN radius_collect!
|
||||||
item_drop_settings.xp_radius_magnet = 7.25 --radius of xp magnet. MUST BE LARGER THAN radius_collect!
|
item_drop_settings.xp_radius_magnet = 7.25 --radius of xp magnet. MUST BE LARGER THAN radius_collect!
|
||||||
item_drop_settings.radius_collect = 0.2 --radius of collection
|
item_drop_settings.radius_collect = 0.2 --radius of collection
|
||||||
item_drop_settings.player_collect_height = 0.8 --added to their pos y value
|
item_drop_settings.player_collect_height = 1.0 --added to their pos y value
|
||||||
item_drop_settings.collection_safety = false --do this to prevent items from flying away on laggy servers
|
item_drop_settings.collection_safety = false --do this to prevent items from flying away on laggy servers
|
||||||
item_drop_settings.random_item_velocity = true --this sets random item velocity if velocity is 0
|
item_drop_settings.random_item_velocity = true --this sets random item velocity if velocity is 0
|
||||||
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
|
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
|
||||||
|
@ -42,33 +16,16 @@ local get_gravity = function()
|
||||||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||||
end
|
end
|
||||||
|
|
||||||
local registered_pickup_achievement = {}
|
|
||||||
|
|
||||||
--TODO: remove limitation of 1 award per itemname
|
|
||||||
function mcl_item_entity.register_pickup_achievement(itemname, award)
|
|
||||||
if not has_awards then
|
|
||||||
minetest.log("warning", "[mcl_item_entity] Trying to register pickup achievement ["..award.."] for ["..itemname.."] while awards missing")
|
|
||||||
elseif registered_pickup_achievement[itemname] then
|
|
||||||
minetest.log("error", "[mcl_item_entity] Trying to register already existing pickup achievement ["..award.."] for ["..itemname.."]")
|
|
||||||
else
|
|
||||||
registered_pickup_achievement[itemname] = award
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
local check_pickup_achievements = function(object, player)
|
local check_pickup_achievements = function(object, player)
|
||||||
if has_awards then
|
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
||||||
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
if minetest.get_item_group(itemname, "tree") ~= 0 then
|
||||||
local playername = player:get_player_name()
|
awards.unlock(player:get_player_name(), "mcl:mineWood")
|
||||||
for name,award in pairs(registered_pickup_achievement) do
|
elseif itemname == "mcl_mobitems:blaze_rod" then
|
||||||
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
|
awards.unlock(player:get_player_name(), "mcl:blazeRod")
|
||||||
awards.unlock(playername, award)
|
elseif itemname == "mcl_mobitems:leather" then
|
||||||
end
|
awards.unlock(player:get_player_name(), "mcl:killCow")
|
||||||
end
|
elseif itemname == "mcl_core:diamond" then
|
||||||
|
awards.unlock(player:get_player_name(), "mcl:diamonds")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,71 +53,103 @@ local disable_physics = function(object, luaentity, ignore_check, reset_movement
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
tick = not tick
|
|
||||||
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
||||||
|
|
||||||
|
|
||||||
local name = player:get_player_name()
|
|
||||||
|
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
|
|
||||||
if tick == true and pool[name] > 0 then
|
|
||||||
minetest.sound_play("item_drop_pickup", {
|
|
||||||
pos = pos,
|
|
||||||
gain = 0.7,
|
|
||||||
max_hear_distance = 16,
|
|
||||||
pitch = math.random(70,110)/100
|
|
||||||
})
|
|
||||||
if pool[name] > 6 then
|
|
||||||
pool[name] = 6
|
|
||||||
else
|
|
||||||
pool[name] = pool[name] - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local inv = player:get_inventory()
|
local inv = player:get_inventory()
|
||||||
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
|
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
|
||||||
|
|
||||||
--magnet and collection
|
--magnet and collection
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
||||||
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
||||||
|
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
|
||||||
|
local collected = false
|
||||||
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
||||||
|
|
||||||
-- Collection
|
-- Collection
|
||||||
if not object:get_luaentity()._removed then
|
if vector.distance(checkpos, object:get_pos()) <= item_drop_settings.radius_collect and not object:get_luaentity()._removed then
|
||||||
-- Ignore if itemstring is not set yet
|
-- Ignore if itemstring is not set yet
|
||||||
if object:get_luaentity().itemstring ~= "" then
|
if object:get_luaentity().itemstring ~= "" then
|
||||||
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
|
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
|
||||||
|
minetest.sound_play("item_drop_pickup", {
|
||||||
|
pos = pos,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
gain = 1.0,
|
||||||
|
}, true)
|
||||||
check_pickup_achievements(object, player)
|
check_pickup_achievements(object, player)
|
||||||
|
|
||||||
|
|
||||||
-- Destroy entity
|
-- Destroy entity
|
||||||
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
|
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
|
||||||
object:get_luaentity().target = checkpos
|
|
||||||
object:get_luaentity()._removed = true
|
object:get_luaentity()._removed = true
|
||||||
|
object:remove()
|
||||||
object:set_velocity({x=0,y=0,z=0})
|
collected = true
|
||||||
object:set_acceleration({x=0,y=0,z=0})
|
|
||||||
|
|
||||||
object:move_to(checkpos)
|
|
||||||
|
|
||||||
pool[name] = pool[name] + 1
|
|
||||||
|
|
||||||
minetest.after(0.25, function()
|
|
||||||
--safety check
|
|
||||||
if object and object:get_luaentity() then
|
|
||||||
object:remove()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Magnet
|
||||||
|
else
|
||||||
|
|
||||||
|
object:get_luaentity()._magnet_active = true
|
||||||
|
object:get_luaentity()._collector_timer = 0
|
||||||
|
|
||||||
|
-- Move object to player
|
||||||
|
disable_physics(object, object:get_luaentity())
|
||||||
|
|
||||||
|
local opos = object:get_pos()
|
||||||
|
local vec = vector.subtract(checkpos, opos)
|
||||||
|
vec = vector.add(opos, vector.divide(vec, 2))
|
||||||
|
object:move_to(vec)
|
||||||
|
|
||||||
|
|
||||||
|
--fix eternally falling items
|
||||||
|
minetest.after(0, function(object)
|
||||||
|
local lua = object:get_luaentity()
|
||||||
|
if lua then
|
||||||
|
object:set_acceleration({x=0, y=0, z=0})
|
||||||
|
end
|
||||||
|
end, object)
|
||||||
|
|
||||||
|
|
||||||
|
--this is a safety to prevent items flying away on laggy servers
|
||||||
|
if item_drop_settings.collection_safety == true then
|
||||||
|
if object:get_luaentity().init ~= true then
|
||||||
|
object:get_luaentity().init = true
|
||||||
|
minetest.after(1, function(args)
|
||||||
|
local playername = args[1]
|
||||||
|
local player = minetest.get_player_by_name(playername)
|
||||||
|
local object = args[2]
|
||||||
|
local lua = object:get_luaentity()
|
||||||
|
if player == nil or not player:is_player() or object == nil or lua == nil or lua.itemstring == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
||||||
|
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
|
||||||
|
if not object:get_luaentity()._removed then
|
||||||
|
minetest.sound_play("item_drop_pickup", {
|
||||||
|
pos = pos,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
gain = 1.0,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
check_pickup_achievements(object, player)
|
||||||
|
object:get_luaentity()._removed = true
|
||||||
|
object:remove()
|
||||||
|
else
|
||||||
|
enable_physics(object, object:get_luaentity())
|
||||||
|
end
|
||||||
|
end, {player:get_player_name(), object})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not collected then
|
||||||
|
if object:get_luaentity()._magnet_timer > 1 then
|
||||||
|
object:get_luaentity()._magnet_timer = -item_drop_settings.magnet_time
|
||||||
|
object:get_luaentity()._magnet_active = false
|
||||||
|
elseif object:get_luaentity()._magnet_timer < 0 then
|
||||||
|
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -176,6 +165,66 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local minigroups = { "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
|
||||||
|
local basegroups = { "pickaxey", "axey", "shovely" }
|
||||||
|
local materials = { "wood", "gold", "stone", "iron", "diamond" }
|
||||||
|
|
||||||
|
-- Checks if the given node would drop its useful drop if dug by a tool
|
||||||
|
-- with the given tool capabilities. Returns true if it will yield its useful
|
||||||
|
-- drop, false otherwise.
|
||||||
|
local check_can_drop = function(node_name, tool_capabilities)
|
||||||
|
local handy = minetest.get_item_group(node_name, "handy")
|
||||||
|
local dig_immediate = minetest.get_item_group(node_name, "dig_immediate")
|
||||||
|
if handy == 1 or dig_immediate == 2 or dig_immediate == 3 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local toolgroupcaps
|
||||||
|
if tool_capabilities then
|
||||||
|
toolgroupcaps = tool_capabilities.groupcaps
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Compare node groups with tool capabilities
|
||||||
|
for m=1, #minigroups do
|
||||||
|
local minigroup = minigroups[m]
|
||||||
|
local g = minetest.get_item_group(node_name, minigroup)
|
||||||
|
if g ~= 0 then
|
||||||
|
local plus = minigroup .. "_dig"
|
||||||
|
if toolgroupcaps[plus] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
for e=1,5 do
|
||||||
|
local effplus = plus .. "_efficiency_" .. e
|
||||||
|
if toolgroupcaps[effplus] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for b=1, #basegroups do
|
||||||
|
local basegroup = basegroups[b]
|
||||||
|
local g = minetest.get_item_group(node_name, basegroup)
|
||||||
|
if g ~= 0 then
|
||||||
|
for m=g, #materials do
|
||||||
|
local plus = basegroup .. "_dig_"..materials[m]
|
||||||
|
if toolgroupcaps[plus] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
for e=1,5 do
|
||||||
|
local effplus = plus .. "_efficiency_" .. e
|
||||||
|
if toolgroupcaps[effplus] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Stupid workaround to get drops from a drop table:
|
-- Stupid workaround to get drops from a drop table:
|
||||||
-- Create a temporary table in minetest.registered_nodes that contains the proper drops,
|
-- Create a temporary table in minetest.registered_nodes that contains the proper drops,
|
||||||
-- because unfortunately minetest.get_node_drops needs the drop table to be inside a registered node definition
|
-- because unfortunately minetest.get_node_drops needs the drop table to be inside a registered node definition
|
||||||
|
@ -220,33 +269,29 @@ local function get_fortune_drops(fortune_drops, fortune_level)
|
||||||
return drop or {}
|
return drop or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
|
|
||||||
|
|
||||||
function minetest.handle_node_drops(pos, drops, digger)
|
function minetest.handle_node_drops(pos, drops, digger)
|
||||||
-- NOTE: This function override allows digger to be nil.
|
-- NOTE: This function override allows digger to be nil.
|
||||||
-- This means there is no digger. This is a special case which allows this function to be called
|
-- This means there is no digger. This is a special case which allows this function to be called
|
||||||
-- by hand. Creative Mode is intentionally ignored in this case.
|
-- by hand. Creative Mode is intentionally ignored in this case.
|
||||||
|
|
||||||
|
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
|
||||||
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if node will yield its useful drop by the digger's tool
|
-- Check if node will yield its useful drop by the digger's tool
|
||||||
local dug_node = minetest.get_node(pos)
|
local dug_node = minetest.get_node(pos)
|
||||||
local tooldef
|
local toolcaps
|
||||||
local tool
|
local tool
|
||||||
if digger ~= nil then
|
if digger ~= nil then
|
||||||
tool = digger:get_wielded_item()
|
tool = digger:get_wielded_item()
|
||||||
tooldef = minetest.registered_tools[tool:get_name()]
|
toolcaps = tool:get_tool_capabilities()
|
||||||
|
|
||||||
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name()) then
|
if not check_can_drop(dug_node.name, toolcaps) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local diggroups = tooldef and tooldef._mcl_diggroups
|
|
||||||
local shearsy_level = diggroups and diggroups.shearsy and diggroups.shearsy.level
|
|
||||||
|
|
||||||
--[[ Special node drops when dug by shears by reading _mcl_shears_drop or with a silk touch tool reading _mcl_silk_touch_drop
|
--[[ Special node drops when dug by shears by reading _mcl_shears_drop or with a silk touch tool reading _mcl_silk_touch_drop
|
||||||
from the node definition.
|
from the node definition.
|
||||||
Definition of _mcl_shears_drop / _mcl_silk_touch_drop:
|
Definition of _mcl_shears_drop / _mcl_silk_touch_drop:
|
||||||
|
@ -258,7 +303,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
|
|
||||||
local silk_touch_drop = false
|
local silk_touch_drop = false
|
||||||
local nodedef = minetest.registered_nodes[dug_node.name]
|
local nodedef = minetest.registered_nodes[dug_node.name]
|
||||||
if shearsy_level and shearsy_level > 0 and nodedef._mcl_shears_drop then
|
if toolcaps ~= nil and toolcaps.groupcaps and toolcaps.groupcaps.shearsy_dig and nodedef._mcl_shears_drop then
|
||||||
if nodedef._mcl_shears_drop == true then
|
if nodedef._mcl_shears_drop == true then
|
||||||
drops = { dug_node.name }
|
drops = { dug_node.name }
|
||||||
else
|
else
|
||||||
|
@ -326,10 +371,6 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
z = -z
|
z = -z
|
||||||
end
|
end
|
||||||
obj:set_velocity({x=1/x, y=obj:get_velocity().y, z=1/z})
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -396,9 +437,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- Number of seconds this item entity has existed so far
|
-- Number of seconds this item entity has existed so far
|
||||||
age = 0,
|
age = 0,
|
||||||
|
|
||||||
-- How old it has become in the collection animation
|
|
||||||
collection_age = 0,
|
|
||||||
|
|
||||||
set_item = function(self, itemstring)
|
set_item = function(self, itemstring)
|
||||||
self.itemstring = itemstring
|
self.itemstring = itemstring
|
||||||
if self.itemstring == "" then
|
if self.itemstring == "" then
|
||||||
|
@ -564,11 +602,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
if self._removed then
|
if self._removed then
|
||||||
self.object:set_properties({
|
|
||||||
physical = false
|
|
||||||
})
|
|
||||||
self.object:set_velocity({x=0,y=0,z=0})
|
|
||||||
self.object:set_acceleration({x=0,y=0,z=0})
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.age = self.age + dtime
|
self.age = self.age + dtime
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Item_Drop_Pickup - https://freesound.org/people/benniknop/sounds/317848/ (License: CC0)
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,7 +1,5 @@
|
||||||
local S = minetest.get_translator("mcl_minecarts")
|
local S = minetest.get_translator("mcl_minecarts")
|
||||||
|
|
||||||
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
|
||||||
|
|
||||||
mcl_minecarts = {}
|
mcl_minecarts = {}
|
||||||
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
||||||
mcl_minecarts.speed_max = 10
|
mcl_minecarts.speed_max = 10
|
||||||
|
@ -665,6 +663,8 @@ register_minecart(
|
||||||
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
||||||
nil, nil, false)
|
nil, nil, false)
|
||||||
|
|
||||||
|
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||||
|
|
||||||
-- Minecart with Furnace
|
-- Minecart with Furnace
|
||||||
register_minecart(
|
register_minecart(
|
||||||
"mcl_minecarts:furnace_minecart",
|
"mcl_minecarts:furnace_minecart",
|
||||||
|
@ -719,6 +719,8 @@ register_minecart(
|
||||||
end, nil, false
|
end, nil, false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||||
|
|
||||||
-- Minecart with Command Block
|
-- Minecart with Command Block
|
||||||
register_minecart(
|
register_minecart(
|
||||||
"mcl_minecarts:command_block_minecart",
|
"mcl_minecarts:command_block_minecart",
|
||||||
|
@ -740,6 +742,8 @@ register_minecart(
|
||||||
nil, nil, false
|
nil, nil, false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||||
|
|
||||||
-- Minecart with Hopper
|
-- Minecart with Hopper
|
||||||
register_minecart(
|
register_minecart(
|
||||||
"mcl_minecarts:hopper_minecart",
|
"mcl_minecarts:hopper_minecart",
|
||||||
|
@ -758,6 +762,8 @@ register_minecart(
|
||||||
nil, nil, false
|
nil, nil, false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
||||||
|
|
||||||
-- Minecart with TNT
|
-- Minecart with TNT
|
||||||
register_minecart(
|
register_minecart(
|
||||||
"mcl_minecarts:tnt_minecart",
|
"mcl_minecarts:tnt_minecart",
|
||||||
|
@ -818,34 +824,29 @@ minetest.register_craft({
|
||||||
|
|
||||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||||
if false then
|
if false then
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_minecarts:furnace_minecart",
|
|
||||||
recipe = {
|
|
||||||
{"mcl_furnaces:furnace"},
|
|
||||||
{"mcl_minecarts:minecart"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:hopper_minecart",
|
output = "mcl_minecarts:furnace_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_hoppers:hopper"},
|
{"mcl_furnaces:furnace"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "mcl_minecarts:hopper_minecart",
|
||||||
|
recipe = {
|
||||||
|
{"mcl_hoppers:hopper"},
|
||||||
|
{"mcl_minecarts:minecart"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "mcl_minecarts:chest_minecart",
|
||||||
|
recipe = {
|
||||||
|
{"mcl_chests:chest"},
|
||||||
|
{"mcl_minecarts:minecart"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_minecarts:chest_minecart",
|
|
||||||
recipe = {
|
|
||||||
{"mcl_chests:chest"},
|
|
||||||
{"mcl_minecarts:minecart"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if has_mcl_wip then
|
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
|
||||||
end
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_wip
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,8 @@ end
|
||||||
|
|
||||||
-- Load settings
|
-- Load settings
|
||||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||||
|
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||||
|
|
||||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||||
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
|
@ -82,6 +84,11 @@ if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- calculate aoc range for mob count
|
||||||
|
local aosrb = tonumber(minetest.settings:get("active_object_send_range_blocks"))
|
||||||
|
local abr = tonumber(minetest.settings:get("active_block_range"))
|
||||||
|
local aoc_range = max(aosrb, abr) * 16
|
||||||
|
|
||||||
-- pathfinding settings
|
-- pathfinding settings
|
||||||
local enable_pathfinding = true
|
local enable_pathfinding = true
|
||||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||||
|
@ -276,33 +283,6 @@ local get_velocity = function(self)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_roll(self)
|
|
||||||
local is_Fleckenstein = self.nametag == "Fleckenstein"
|
|
||||||
local was_Fleckenstein = false
|
|
||||||
|
|
||||||
local rot = self.object:get_rotation()
|
|
||||||
rot.z = is_Fleckenstein and pi or 0
|
|
||||||
self.object:set_rotation(rot)
|
|
||||||
|
|
||||||
local cbox = table.copy(self.collisionbox)
|
|
||||||
local acbox = self.object:get_properties().collisionbox
|
|
||||||
|
|
||||||
if math.abs(cbox[2] - acbox[2]) > 0.1 then
|
|
||||||
was_Fleckenstein = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_Fleckenstein ~= was_Fleckenstein then
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = pos.y + (acbox[2] + acbox[5])
|
|
||||||
self.object:set_pos(pos)
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_Fleckenstein then
|
|
||||||
cbox[2], cbox[5] = -cbox[5], -cbox[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:set_properties({collisionbox = cbox})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set and return valid yaw
|
-- set and return valid yaw
|
||||||
local set_yaw = function(self, yaw, delay, dtime)
|
local set_yaw = function(self, yaw, delay, dtime)
|
||||||
|
@ -318,7 +298,6 @@ local set_yaw = function(self, yaw, delay, dtime)
|
||||||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
||||||
end
|
end
|
||||||
self.object:set_yaw(yaw)
|
self.object:set_yaw(yaw)
|
||||||
update_roll(self)
|
|
||||||
return yaw
|
return yaw
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -666,9 +645,9 @@ local update_tag = function(self)
|
||||||
nametag = tag,
|
nametag = tag,
|
||||||
})
|
})
|
||||||
|
|
||||||
update_roll(self)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- drop items
|
-- drop items
|
||||||
local item_drop = function(self, cooked, looting_level)
|
local item_drop = function(self, cooked, looting_level)
|
||||||
|
|
||||||
|
@ -728,9 +707,7 @@ local item_drop = function(self, cooked, looting_level)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add item if it exists
|
-- add item if it exists
|
||||||
for x = 1, num do
|
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
|
||||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if obj and obj:get_luaentity() then
|
if obj and obj:get_luaentity() then
|
||||||
|
|
||||||
|
@ -859,12 +836,10 @@ local check_for_death = function(self, cause, cmi_cause)
|
||||||
remove_texture_mod(self, "^[colorize:#FF000040")
|
remove_texture_mod(self, "^[colorize:#FF000040")
|
||||||
remove_texture_mod(self, "^[brighten")
|
remove_texture_mod(self, "^[brighten")
|
||||||
self.passive = true
|
self.passive = true
|
||||||
|
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
pointable = false,
|
pointable = false,
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
})
|
})
|
||||||
|
|
||||||
set_velocity(self, 0)
|
set_velocity(self, 0)
|
||||||
local acc = self.object:get_acceleration()
|
local acc = self.object:get_acceleration()
|
||||||
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
|
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
|
||||||
|
@ -2814,10 +2789,6 @@ local do_states = function(self, dtime)
|
||||||
local arrow, ent
|
local arrow, ent
|
||||||
local v = 1
|
local v = 1
|
||||||
if not self.shoot_arrow then
|
if not self.shoot_arrow then
|
||||||
self.firing = true
|
|
||||||
minetest.after(1, function()
|
|
||||||
self.firing = false
|
|
||||||
end)
|
|
||||||
arrow = minetest.add_entity(p, self.arrow)
|
arrow = minetest.add_entity(p, self.arrow)
|
||||||
ent = arrow:get_luaentity()
|
ent = arrow:get_luaentity()
|
||||||
if ent.velocity then
|
if ent.velocity then
|
||||||
|
@ -2855,7 +2826,7 @@ local falling = function(self, pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
if mcl_portals ~= nil then
|
if mcl_portals ~= nil then
|
||||||
if mcl_portals.nether_portal_cooloff(self.object) then
|
if mcl_portals.nether_portal_cooloff[self.object] then
|
||||||
return false -- mob has teleported through Nether portal - it's 99% not falling
|
return false -- mob has teleported through Nether portal - it's 99% not falling
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2885,18 +2856,6 @@ local falling = function(self, pos)
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
end
|
end
|
||||||
|
|
||||||
if minetest.registered_nodes[node_ok(pos).name].groups.lava then
|
|
||||||
|
|
||||||
if self.floats_on_lava == 1 then
|
|
||||||
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = 0,
|
|
||||||
y = -self.fall_speed / (max(1, v.y) ^ 2),
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- in water then float up
|
-- in water then float up
|
||||||
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
||||||
|
|
||||||
|
@ -3516,11 +3475,18 @@ local mob_step = function(self, dtime)
|
||||||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
||||||
end
|
end
|
||||||
self.object:set_yaw(yaw)
|
self.object:set_yaw(yaw)
|
||||||
update_roll(self)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- end rotation
|
-- end rotation
|
||||||
|
|
||||||
|
-- knockback timer
|
||||||
|
if self.pause_timer > 0 then
|
||||||
|
|
||||||
|
self.pause_timer = self.pause_timer - dtime
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- run custom function (defined in mob lua file)
|
-- run custom function (defined in mob lua file)
|
||||||
if self.do_custom then
|
if self.do_custom then
|
||||||
|
|
||||||
|
@ -3530,14 +3496,6 @@ local mob_step = function(self, dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- knockback timer
|
|
||||||
if self.pause_timer > 0 then
|
|
||||||
|
|
||||||
self.pause_timer = self.pause_timer - dtime
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- attack timer
|
-- attack timer
|
||||||
self.timer = self.timer + dtime
|
self.timer = self.timer + dtime
|
||||||
|
|
||||||
|
@ -3556,7 +3514,7 @@ local mob_step = function(self, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mob plays random sound at times
|
-- mob plays random sound at times
|
||||||
if random(1, 70) == 1 then
|
if random(1, 100) == 1 then
|
||||||
mob_sound(self, "random", true)
|
mob_sound(self, "random", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -3736,8 +3694,6 @@ function mobs:register_mob(name, def)
|
||||||
local can_despawn
|
local can_despawn
|
||||||
if def.can_despawn ~= nil then
|
if def.can_despawn ~= nil then
|
||||||
can_despawn = def.can_despawn
|
can_despawn = def.can_despawn
|
||||||
elseif def.spawn_class == "passive" then
|
|
||||||
can_despawn = false
|
|
||||||
else
|
else
|
||||||
can_despawn = true
|
can_despawn = true
|
||||||
end
|
end
|
||||||
|
@ -3817,7 +3773,6 @@ minetest.register_entity(name, {
|
||||||
knock_back = def.knock_back ~= false,
|
knock_back = def.knock_back ~= false,
|
||||||
shoot_offset = def.shoot_offset or 0,
|
shoot_offset = def.shoot_offset or 0,
|
||||||
floats = def.floats or 1, -- floats in water by default
|
floats = def.floats or 1, -- floats in water by default
|
||||||
floats_on_lava = def.floats_on_lava or 0,
|
|
||||||
replace_rate = def.replace_rate,
|
replace_rate = def.replace_rate,
|
||||||
replace_what = def.replace_what,
|
replace_what = def.replace_what,
|
||||||
replace_with = def.replace_with,
|
replace_with = def.replace_with,
|
||||||
|
@ -3901,12 +3856,6 @@ minetest.register_entity(name, {
|
||||||
on_detach_child = mob_detach_child,
|
on_detach_child = mob_detach_child,
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime)
|
on_activate = function(self, staticdata, dtime)
|
||||||
--this is a temporary hack so mobs stop
|
|
||||||
--glitching and acting really weird with the
|
|
||||||
--default built in engine collision detection
|
|
||||||
self.object:set_properties({
|
|
||||||
collide_with_objects = false,
|
|
||||||
})
|
|
||||||
return mob_activate(self, staticdata, def, dtime)
|
return mob_activate(self, staticdata, def, dtime)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -3925,6 +3874,289 @@ end
|
||||||
end -- END mobs:register_mob function
|
end -- END mobs:register_mob function
|
||||||
|
|
||||||
|
|
||||||
|
-- count how many mobs of one type are inside an area
|
||||||
|
local count_mobs = function(pos, mobtype)
|
||||||
|
|
||||||
|
local num = 0
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, aoc_range)
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
local obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj and obj.name and obj._cmi_is_mob then
|
||||||
|
|
||||||
|
-- count passive mobs only
|
||||||
|
if mobtype == "!passive" then
|
||||||
|
if obj.spawn_class == "passive" then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
-- count hostile mobs only
|
||||||
|
elseif mobtype == "!hostile" then
|
||||||
|
if obj.spawn_class == "hostile" then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
-- count ambient mobs only
|
||||||
|
elseif mobtype == "!ambient" then
|
||||||
|
if obj.spawn_class == "ambient" then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
-- count water mobs only
|
||||||
|
elseif mobtype == "!water" then
|
||||||
|
if obj.spawn_class == "water" then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
-- count mob type
|
||||||
|
elseif mobtype and obj.name == mobtype then
|
||||||
|
num = num + 1
|
||||||
|
-- count total mobs
|
||||||
|
elseif not mobtype then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return num
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- global functions
|
||||||
|
|
||||||
|
function mobs:spawn_abm_check(pos, node, name)
|
||||||
|
-- global function to add additional spawn checks
|
||||||
|
-- return true to stop spawning mob
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
|
||||||
|
interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||||
|
|
||||||
|
-- Do mobs spawn at all?
|
||||||
|
if not mobs_spawn then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- chance/spawn number override in minetest.conf for registered mob
|
||||||
|
local numbers = minetest.settings:get(name)
|
||||||
|
|
||||||
|
if numbers then
|
||||||
|
numbers = numbers:split(",")
|
||||||
|
chance = tonumber(numbers[1]) or chance
|
||||||
|
aoc = tonumber(numbers[2]) or aoc
|
||||||
|
|
||||||
|
if chance == 0 then
|
||||||
|
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.log("action",
|
||||||
|
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local spawn_action
|
||||||
|
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
|
||||||
|
|
||||||
|
local orig_pos = table.copy(pos)
|
||||||
|
-- is mob actually registered?
|
||||||
|
if not mobs.spawning_mobs[name]
|
||||||
|
or not minetest.registered_entities[name] then
|
||||||
|
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- additional custom checks for spawning mob
|
||||||
|
if mobs:spawn_abm_check(pos, node, name) == true then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- count nearby mobs in same spawn class
|
||||||
|
local entdef = minetest.registered_entities[name]
|
||||||
|
local spawn_class = entdef and entdef.spawn_class
|
||||||
|
if not spawn_class then
|
||||||
|
if entdef.type == "monster" then
|
||||||
|
spawn_class = "hostile"
|
||||||
|
else
|
||||||
|
spawn_class = "passive"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
||||||
|
-- do not spawn if too many of same mob in area
|
||||||
|
if active_object_count_wider >= max_per_block -- large-range mob cap
|
||||||
|
or (not in_class_cap) -- spawn class mob cap
|
||||||
|
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
||||||
|
-- too many entities
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if toggle set to nil then ignore day/night check
|
||||||
|
if day_toggle ~= nil then
|
||||||
|
|
||||||
|
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||||
|
|
||||||
|
if tod > 4500 and tod < 19500 then
|
||||||
|
-- daylight, but mob wants night
|
||||||
|
if day_toggle == false then
|
||||||
|
-- mob needs night
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- night time but mob wants day
|
||||||
|
if day_toggle == true then
|
||||||
|
-- mob needs day
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- spawn above node
|
||||||
|
pos.y = pos.y + 1
|
||||||
|
|
||||||
|
-- only spawn away from player
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, 24)
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
if objs[n]:is_player() then
|
||||||
|
-- player too close
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- mobs cannot spawn in protected areas when enabled
|
||||||
|
if not spawn_protected
|
||||||
|
and minetest.is_protected(pos, "") then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- are we spawning within height limits?
|
||||||
|
if pos.y > max_height
|
||||||
|
or pos.y < min_height then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- are light levels ok?
|
||||||
|
local light = minetest.get_node_light(pos)
|
||||||
|
if not light
|
||||||
|
or light > max_light
|
||||||
|
or light < min_light then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- do we have enough space to spawn mob?
|
||||||
|
local ent = minetest.registered_entities[name]
|
||||||
|
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
||||||
|
local min_x, max_x
|
||||||
|
if width_x % 2 == 0 then
|
||||||
|
max_x = math.floor(width_x/2)
|
||||||
|
min_x = -(max_x-1)
|
||||||
|
else
|
||||||
|
max_x = math.floor(width_x/2)
|
||||||
|
min_x = -max_x
|
||||||
|
end
|
||||||
|
|
||||||
|
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
||||||
|
local min_z, max_z
|
||||||
|
if width_z % 2 == 0 then
|
||||||
|
max_z = math.floor(width_z/2)
|
||||||
|
min_z = -(max_z-1)
|
||||||
|
else
|
||||||
|
max_z = math.floor(width_z/2)
|
||||||
|
min_z = -max_z
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
||||||
|
|
||||||
|
for y = 0, max_y do
|
||||||
|
for x = min_x, max_x do
|
||||||
|
for z = min_z, max_z do
|
||||||
|
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
||||||
|
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
||||||
|
-- inside block
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
||||||
|
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
||||||
|
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
||||||
|
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tweak X/Y/Z spawn pos
|
||||||
|
if width_x % 2 == 0 then
|
||||||
|
pos.x = pos.x + 0.5
|
||||||
|
end
|
||||||
|
if width_z % 2 == 0 then
|
||||||
|
pos.z = pos.z + 0.5
|
||||||
|
end
|
||||||
|
pos.y = pos.y - 0.5
|
||||||
|
|
||||||
|
local mob = minetest.add_entity(pos, name)
|
||||||
|
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
||||||
|
|
||||||
|
if on_spawn then
|
||||||
|
|
||||||
|
local ent = mob:get_luaentity()
|
||||||
|
|
||||||
|
on_spawn(ent, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
||||||
|
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_abm({
|
||||||
|
label = name .. " spawning",
|
||||||
|
nodenames = nodes,
|
||||||
|
neighbors = neighbors,
|
||||||
|
interval = interval,
|
||||||
|
chance = floor(max(1, chance * mobs_spawn_chance)),
|
||||||
|
catch_up = false,
|
||||||
|
action = spawn_abm_action,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- compatibility with older mob registration
|
||||||
|
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
||||||
|
|
||||||
|
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
||||||
|
chance, active_object_count, -31000, max_height, day_toggle)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- MarkBu's spawn function
|
||||||
|
function mobs:spawn(def)
|
||||||
|
|
||||||
|
local name = def.name
|
||||||
|
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||||
|
local neighbors = def.neighbors or {"air"}
|
||||||
|
local min_light = def.min_light or 0
|
||||||
|
local max_light = def.max_light or 15
|
||||||
|
local interval = def.interval or 30
|
||||||
|
local chance = def.chance or 5000
|
||||||
|
local active_object_count = def.active_object_count or 1
|
||||||
|
local min_height = def.min_height or -31000
|
||||||
|
local max_height = def.max_height or 31000
|
||||||
|
local day_toggle = def.day_toggle
|
||||||
|
local on_spawn = def.on_spawn
|
||||||
|
|
||||||
|
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||||
|
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- register arrow for shoot attack
|
-- register arrow for shoot attack
|
||||||
function mobs:register_arrow(name, def)
|
function mobs:register_arrow(name, def)
|
||||||
|
|
||||||
|
@ -3947,11 +4179,6 @@ function mobs:register_arrow(name, def)
|
||||||
switch = 0,
|
switch = 0,
|
||||||
owner_id = def.owner_id,
|
owner_id = def.owner_id,
|
||||||
rotate = def.rotate,
|
rotate = def.rotate,
|
||||||
on_punch = function(self)
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
self.object:set_velocity({x=vel.x * -1, y=vel.y * -1, z=vel.z * -1})
|
|
||||||
end,
|
|
||||||
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
|
||||||
automatic_face_movement_dir = def.rotate
|
automatic_face_movement_dir = def.rotate
|
||||||
and (def.rotate - (pi / 180)) or false,
|
and (def.rotate - (pi / 180)) or false,
|
||||||
|
|
||||||
|
@ -4014,7 +4241,7 @@ function mobs:register_arrow(name, def)
|
||||||
|
|
||||||
if self.hit_player or self.hit_mob or self.hit_object then
|
if self.hit_player or self.hit_mob or self.hit_object then
|
||||||
|
|
||||||
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do
|
||||||
|
|
||||||
if self.hit_player
|
if self.hit_player
|
||||||
and player:is_player() then
|
and player:is_player() then
|
||||||
|
@ -4069,7 +4296,7 @@ end
|
||||||
|
|
||||||
-- make explosion with protection and tnt mod check
|
-- make explosion with protection and tnt mod check
|
||||||
function mobs:boom(self, pos, strength, fire)
|
function mobs:boom(self, pos, strength, fire)
|
||||||
self.object:remove()
|
|
||||||
if mod_explosions then
|
if mod_explosions then
|
||||||
if mobs_griefing and not minetest.is_protected(pos, "") then
|
if mobs_griefing and not minetest.is_protected(pos, "") then
|
||||||
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
|
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
|
||||||
|
@ -4350,7 +4577,6 @@ function mobs:alias_mob(old_name, new_name)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
|
||||||
local timer = 0
|
local timer = 0
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
timer = timer + dtime
|
timer = timer + dtime
|
||||||
|
@ -4367,4 +4593,3 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
timer = 0
|
timer = 0
|
||||||
end)
|
end)
|
||||||
]]--
|
|
|
@ -4,9 +4,6 @@ local path = minetest.get_modpath(minetest.get_current_modname())
|
||||||
-- Mob API
|
-- Mob API
|
||||||
dofile(path .. "/api.lua")
|
dofile(path .. "/api.lua")
|
||||||
|
|
||||||
-- Spawning Algorithm
|
|
||||||
dofile(path .. "/spawning.lua")
|
|
||||||
|
|
||||||
-- Rideable Mobs
|
-- Rideable Mobs
|
||||||
dofile(path .. "/mount.lua")
|
dofile(path .. "/mount.lua")
|
||||||
|
|
||||||
|
|
|
@ -168,20 +168,16 @@ function mobs.detach(player, offset)
|
||||||
|
|
||||||
mcl_player.player_set_animation(player, "stand" , 30)
|
mcl_player.player_set_animation(player, "stand" , 30)
|
||||||
|
|
||||||
--local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
|
|
||||||
--pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
||||||
|
|
||||||
player:add_velocity(vector.new(math.random(-6,6),math.random(5,8),math.random(-6,6))) --throw the rider off
|
|
||||||
|
|
||||||
--[[
|
|
||||||
minetest.after(0.1, function(name, pos)
|
minetest.after(0.1, function(name, pos)
|
||||||
local player = minetest.get_player_by_name(name)
|
local player = minetest.get_player_by_name(name)
|
||||||
if player then
|
if player then
|
||||||
player:set_pos(pos)
|
player:set_pos(pos)
|
||||||
end
|
end
|
||||||
end, player:get_player_name(), pos)
|
end, player:get_player_name(), pos)
|
||||||
]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,647 +0,0 @@
|
||||||
--lua locals
|
|
||||||
local get_node = minetest.get_node
|
|
||||||
local get_item_group = minetest.get_item_group
|
|
||||||
local get_node_light = minetest.get_node_light
|
|
||||||
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
|
||||||
local new_vector = vector.new
|
|
||||||
local math_random = math.random
|
|
||||||
local get_biome_name = minetest.get_biome_name
|
|
||||||
local max = math.max
|
|
||||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
||||||
local vector_distance = vector.distance
|
|
||||||
|
|
||||||
-- range for mob count
|
|
||||||
local aoc_range = 32
|
|
||||||
--[[
|
|
||||||
|
|
||||||
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
|
||||||
|
|
||||||
underground:
|
|
||||||
"FlowerForest_underground",
|
|
||||||
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
|
|
||||||
"ColdTaiga_underground",
|
|
||||||
"IcePlains_underground",
|
|
||||||
"IcePlainsSpikes_underground",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
|
|
||||||
ocean:
|
|
||||||
"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",
|
|
||||||
|
|
||||||
water or beach?
|
|
||||||
"MesaPlateauFM_sandlevel",
|
|
||||||
"MesaPlateauF_sandlevel",
|
|
||||||
"MesaBryce_sandlevel",
|
|
||||||
"Mesa_sandlevel",
|
|
||||||
|
|
||||||
beach:
|
|
||||||
"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",
|
|
||||||
|
|
||||||
dimension biome:
|
|
||||||
"Nether",
|
|
||||||
"End",
|
|
||||||
|
|
||||||
Overworld regular:
|
|
||||||
"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",
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
|
||||||
-- count how many mobs of one type are inside an area
|
|
||||||
|
|
||||||
local count_mobs = function(pos,mobtype)
|
|
||||||
local num = 0
|
|
||||||
local objs = get_objects_inside_radius(pos, aoc_range)
|
|
||||||
for n = 1, #objs do
|
|
||||||
local obj = objs[n]:get_luaentity()
|
|
||||||
if obj and obj.name and obj._cmi_is_mob then
|
|
||||||
-- count hostile mobs only
|
|
||||||
if mobtype == "hostile" then
|
|
||||||
if obj.spawn_class == "hostile" then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
-- count passive mobs only
|
|
||||||
else
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return num
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- global functions
|
|
||||||
|
|
||||||
function mobs:spawn_abm_check(pos, node, name)
|
|
||||||
-- global function to add additional spawn checks
|
|
||||||
-- return true to stop spawning mob
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Custom elements changed:
|
|
||||||
|
|
||||||
name:
|
|
||||||
the mobs name
|
|
||||||
|
|
||||||
dimension:
|
|
||||||
"overworld"
|
|
||||||
"nether"
|
|
||||||
"end"
|
|
||||||
|
|
||||||
types of spawning:
|
|
||||||
"water"
|
|
||||||
"ground"
|
|
||||||
"lava"
|
|
||||||
|
|
||||||
biomes: tells the spawner to allow certain mobs to spawn in certain biomes
|
|
||||||
{"this", "that", "grasslands", "whatever"}
|
|
||||||
|
|
||||||
|
|
||||||
what is aoc??? objects in area
|
|
||||||
|
|
||||||
WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
--this is where all of the spawning information is kept
|
|
||||||
local spawn_dictionary = {}
|
|
||||||
|
|
||||||
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
|
||||||
|
|
||||||
--print(dump(biomes))
|
|
||||||
|
|
||||||
-- Do mobs spawn at all?
|
|
||||||
if not mobs_spawn then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- chance/spawn number override in minetest.conf for registered mob
|
|
||||||
local numbers = minetest.settings:get(name)
|
|
||||||
|
|
||||||
if numbers then
|
|
||||||
numbers = numbers:split(",")
|
|
||||||
chance = tonumber(numbers[1]) or chance
|
|
||||||
aoc = tonumber(numbers[2]) or aoc
|
|
||||||
|
|
||||||
if chance == 0 then
|
|
||||||
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.log("action",
|
|
||||||
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local spawn_action
|
|
||||||
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
|
|
||||||
|
|
||||||
local orig_pos = table.copy(pos)
|
|
||||||
-- is mob actually registered?
|
|
||||||
if not mobs.spawning_mobs[name]
|
|
||||||
or not minetest.registered_entities[name] then
|
|
||||||
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- additional custom checks for spawning mob
|
|
||||||
if mobs:spawn_abm_check(pos, node, name) == true then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- count nearby mobs in same spawn class
|
|
||||||
local entdef = minetest.registered_entities[name]
|
|
||||||
local spawn_class = entdef and entdef.spawn_class
|
|
||||||
if not spawn_class then
|
|
||||||
if entdef.type == "monster" then
|
|
||||||
spawn_class = "hostile"
|
|
||||||
else
|
|
||||||
spawn_class = "passive"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
|
||||||
-- do not spawn if too many of same mob in area
|
|
||||||
if active_object_count_wider >= max_per_block -- large-range mob cap
|
|
||||||
or (not in_class_cap) -- spawn class mob cap
|
|
||||||
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
|
||||||
-- too many entities
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if toggle set to nil then ignore day/night check
|
|
||||||
if day_toggle ~= nil then
|
|
||||||
|
|
||||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
|
||||||
|
|
||||||
if tod > 4500 and tod < 19500 then
|
|
||||||
-- daylight, but mob wants night
|
|
||||||
if day_toggle == false then
|
|
||||||
-- mob needs night
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- night time but mob wants day
|
|
||||||
if day_toggle == true then
|
|
||||||
-- mob needs day
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- spawn above node
|
|
||||||
pos.y = pos.y + 1
|
|
||||||
|
|
||||||
-- only spawn away from player
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 24)
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
if objs[n]:is_player() then
|
|
||||||
-- player too close
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- mobs cannot spawn in protected areas when enabled
|
|
||||||
if not spawn_protected
|
|
||||||
and minetest.is_protected(pos, "") then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- are we spawning within height limits?
|
|
||||||
if pos.y > max_height
|
|
||||||
or pos.y < min_height then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- are light levels ok?
|
|
||||||
local light = minetest.get_node_light(pos)
|
|
||||||
if not light
|
|
||||||
or light > max_light
|
|
||||||
or light < min_light then
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- do we have enough space to spawn mob?
|
|
||||||
local ent = minetest.registered_entities[name]
|
|
||||||
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
|
||||||
local min_x, max_x
|
|
||||||
if width_x % 2 == 0 then
|
|
||||||
max_x = math.floor(width_x/2)
|
|
||||||
min_x = -(max_x-1)
|
|
||||||
else
|
|
||||||
max_x = math.floor(width_x/2)
|
|
||||||
min_x = -max_x
|
|
||||||
end
|
|
||||||
|
|
||||||
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
|
||||||
local min_z, max_z
|
|
||||||
if width_z % 2 == 0 then
|
|
||||||
max_z = math.floor(width_z/2)
|
|
||||||
min_z = -(max_z-1)
|
|
||||||
else
|
|
||||||
max_z = math.floor(width_z/2)
|
|
||||||
min_z = -max_z
|
|
||||||
end
|
|
||||||
|
|
||||||
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
|
||||||
|
|
||||||
for y = 0, max_y do
|
|
||||||
for x = min_x, max_x do
|
|
||||||
for z = min_z, max_z do
|
|
||||||
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
|
||||||
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
|
||||||
-- inside block
|
|
||||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
|
||||||
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
|
||||||
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
|
||||||
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- tweak X/Y/Z spawn pos
|
|
||||||
if width_x % 2 == 0 then
|
|
||||||
pos.x = pos.x + 0.5
|
|
||||||
end
|
|
||||||
if width_z % 2 == 0 then
|
|
||||||
pos.z = pos.z + 0.5
|
|
||||||
end
|
|
||||||
pos.y = pos.y - 0.5
|
|
||||||
|
|
||||||
local mob = minetest.add_entity(pos, name)
|
|
||||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
|
||||||
|
|
||||||
if on_spawn then
|
|
||||||
|
|
||||||
local ent = mob:get_luaentity()
|
|
||||||
|
|
||||||
on_spawn(ent, pos)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
|
||||||
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local entdef = minetest.registered_entities[name]
|
|
||||||
local spawn_class
|
|
||||||
if entdef.type == "monster" then
|
|
||||||
spawn_class = "hostile"
|
|
||||||
else
|
|
||||||
spawn_class = "passive"
|
|
||||||
end
|
|
||||||
|
|
||||||
--load information into the spawn dictionary
|
|
||||||
local key = #spawn_dictionary + 1
|
|
||||||
spawn_dictionary[key] = {}
|
|
||||||
spawn_dictionary[key]["name"] = name
|
|
||||||
spawn_dictionary[key]["dimension"] = dimension
|
|
||||||
spawn_dictionary[key]["type_of_spawning"] = type_of_spawning
|
|
||||||
spawn_dictionary[key]["biomes"] = biomes
|
|
||||||
spawn_dictionary[key]["min_light"] = min_light
|
|
||||||
spawn_dictionary[key]["max_light"] = max_light
|
|
||||||
spawn_dictionary[key]["interval"] = interval
|
|
||||||
spawn_dictionary[key]["chance"] = chance
|
|
||||||
spawn_dictionary[key]["aoc"] = aoc
|
|
||||||
spawn_dictionary[key]["min_height"] = min_height
|
|
||||||
spawn_dictionary[key]["max_height"] = max_height
|
|
||||||
spawn_dictionary[key]["day_toggle"] = day_toggle
|
|
||||||
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
|
|
||||||
spawn_dictionary[key]["spawn_class"] = spawn_class
|
|
||||||
|
|
||||||
--[[
|
|
||||||
minetest.register_abm({
|
|
||||||
label = name .. " spawning",
|
|
||||||
nodenames = nodes,
|
|
||||||
neighbors = neighbors,
|
|
||||||
interval = interval,
|
|
||||||
chance = floor(max(1, chance * mobs_spawn_chance)),
|
|
||||||
catch_up = false,
|
|
||||||
action = spawn_abm_action,
|
|
||||||
})
|
|
||||||
]]--
|
|
||||||
end
|
|
||||||
|
|
||||||
-- compatibility with older mob registration
|
|
||||||
-- we're going to forget about this for now -j4i
|
|
||||||
--[[
|
|
||||||
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
|
||||||
|
|
||||||
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
|
||||||
chance, active_object_count, -31000, max_height, day_toggle)
|
|
||||||
end
|
|
||||||
]]--
|
|
||||||
|
|
||||||
|
|
||||||
--Don't disable this yet-j4i
|
|
||||||
-- MarkBu's spawn function
|
|
||||||
|
|
||||||
function mobs:spawn(def)
|
|
||||||
--does nothing for now
|
|
||||||
--[[
|
|
||||||
local name = def.name
|
|
||||||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
|
||||||
local neighbors = def.neighbors or {"air"}
|
|
||||||
local min_light = def.min_light or 0
|
|
||||||
local max_light = def.max_light or 15
|
|
||||||
local interval = def.interval or 30
|
|
||||||
local chance = def.chance or 5000
|
|
||||||
local active_object_count = def.active_object_count or 1
|
|
||||||
local min_height = def.min_height or -31000
|
|
||||||
local max_height = def.max_height or 31000
|
|
||||||
local day_toggle = def.day_toggle
|
|
||||||
local on_spawn = def.on_spawn
|
|
||||||
|
|
||||||
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
|
||||||
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
|
||||||
]]--
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local axis
|
|
||||||
--inner and outer part of square donut radius
|
|
||||||
local inner = 1
|
|
||||||
local outer = 65
|
|
||||||
local int = {-1,1}
|
|
||||||
local position_calculation = function(pos)
|
|
||||||
|
|
||||||
pos = vector.floor(pos)
|
|
||||||
|
|
||||||
--this is used to determine the axis buffer from the player
|
|
||||||
axis = math.random(0,1)
|
|
||||||
|
|
||||||
--cast towards the direction
|
|
||||||
if axis == 0 then --x
|
|
||||||
pos.x = pos.x + math.random(inner,outer)*int[math.random(1,2)]
|
|
||||||
pos.z = pos.z + math.random(-outer,outer)
|
|
||||||
else --z
|
|
||||||
pos.z = pos.z + math.random(inner,outer)*int[math.random(1,2)]
|
|
||||||
pos.x = pos.x + math.random(-outer,outer)
|
|
||||||
end
|
|
||||||
return(pos)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local decypher_limits_dictionary = {
|
|
||||||
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
|
|
||||||
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
|
|
||||||
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
|
|
||||||
}
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local function decypher_limits(posy)
|
|
||||||
--local min_max_table = decypher_limits_dictionary[dimension]
|
|
||||||
--return min_max_table[1],min_max_table[2]
|
|
||||||
posy = math.floor(posy)
|
|
||||||
return posy - 32, posy + 32
|
|
||||||
end
|
|
||||||
|
|
||||||
--a simple helper function for mob_spawn
|
|
||||||
local function biome_check(biome_list, biome_goal)
|
|
||||||
for _,data in ipairs(biome_list) do
|
|
||||||
if data == biome_goal then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--todo mob limiting
|
|
||||||
--MAIN LOOP
|
|
||||||
|
|
||||||
if mobs_spawn then
|
|
||||||
local timer = 0
|
|
||||||
minetest.register_globalstep(function(dtime)
|
|
||||||
timer = timer + dtime
|
|
||||||
if timer >= 8 then
|
|
||||||
timer = 0
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
for i = 1,math_random(3,8) do
|
|
||||||
repeat -- after this line each "break" means "continue"
|
|
||||||
local player_pos = player:get_pos()
|
|
||||||
|
|
||||||
local _,dimension = mcl_worlds.y_to_layer(player_pos.y)
|
|
||||||
|
|
||||||
if dimension == "void" or dimension == "default" then
|
|
||||||
break -- ignore void and unloaded area
|
|
||||||
end
|
|
||||||
|
|
||||||
local min,max = decypher_limits(player_pos.y)
|
|
||||||
|
|
||||||
local goal_pos = position_calculation(player_pos)
|
|
||||||
|
|
||||||
local spawning_position_list = find_nodes_in_area_under_air(new_vector(goal_pos.x,min,goal_pos.z), vector.new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
|
|
||||||
|
|
||||||
--couldn't find node
|
|
||||||
if #spawning_position_list <= 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
|
|
||||||
|
|
||||||
--Prevent strange behavior/too close to player
|
|
||||||
if not spawning_position or vector_distance(player_pos, spawning_position) < 15 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local gotten_node = get_node(spawning_position).name
|
|
||||||
|
|
||||||
if not gotten_node or gotten_node == "air" then --skip air nodes
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
|
||||||
|
|
||||||
if not gotten_biome then
|
|
||||||
break --skip if in unloaded area
|
|
||||||
end
|
|
||||||
|
|
||||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
|
||||||
|
|
||||||
--grab random mob
|
|
||||||
local mob_def = spawn_dictionary[math.random(1,#spawn_dictionary)]
|
|
||||||
|
|
||||||
if not mob_def then
|
|
||||||
break --skip if something ridiculous happens (nil mob def)
|
|
||||||
end
|
|
||||||
|
|
||||||
--skip if not correct dimension
|
|
||||||
if mob_def.dimension ~= dimension then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--skip if not in correct biome
|
|
||||||
if not biome_check(mob_def.biomes, gotten_biome) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--add this so mobs don't spawn inside nodes
|
|
||||||
spawning_position.y = spawning_position.y + 1
|
|
||||||
|
|
||||||
if spawning_position.y < mob_def.min_height or spawning_position.y > mob_def.max_height then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--only need to poll for node light if everything else worked
|
|
||||||
local gotten_light = get_node_light(spawning_position)
|
|
||||||
|
|
||||||
--don't spawn if not in light limits
|
|
||||||
if gotten_light < mob_def.min_light or gotten_light > mob_def.max_light then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
|
||||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
|
||||||
|
|
||||||
if mob_def.type_of_spawning == "ground" and is_water then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if mob_def.type_of_spawning == "ground" and is_lava then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--finally do the heavy check (for now) of mobs in area
|
|
||||||
if count_mobs(spawning_position, mob_def.spawn_class) >= mob_def.aoc then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
--adjust the position for water and lava mobs
|
|
||||||
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
|
|
||||||
spawning_position.y = spawning_position.y - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
--everything is correct, spawn mob
|
|
||||||
minetest.add_entity(spawning_position, mob_def.name)
|
|
||||||
until true --this is a safety catch
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
|
@ -290,13 +290,13 @@ mobs_mc.spawn = {
|
||||||
mobs_mc.spawn_height = {
|
mobs_mc.spawn_height = {
|
||||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
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 boundaries (inclusive)
|
||||||
overworld_min = -64,-- -2999,
|
overworld_min = -2999,
|
||||||
overworld_max = 31000,
|
overworld_max = 31000,
|
||||||
|
|
||||||
-- Nether boundaries (inclusive)
|
-- Nether boundaries (inclusive)
|
||||||
nether_min = -29067,-- -3369,
|
nether_min = -3369,
|
||||||
nether_max = -28939,-- -3000,
|
nether_max = -3000,
|
||||||
|
|
||||||
-- End boundaries (inclusive)
|
-- End boundaries (inclusive)
|
||||||
end_min = -6200,
|
end_min = -6200,
|
||||||
|
|
|
@ -521,7 +521,7 @@ if c("totem") then
|
||||||
-- Totem of Undying
|
-- Totem of Undying
|
||||||
minetest.register_craftitem("mobs_mc:totem", {
|
minetest.register_craftitem("mobs_mc:totem", {
|
||||||
description = S("Totem of Undying"),
|
description = S("Totem of Undying"),
|
||||||
_tt_help = minetest.colorize(mcl_colors.GREEN, S("Protects you from death while wielding it")),
|
_tt_help = minetest.colorize("#00FF00", 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_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."),
|
_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",
|
inventory_image = "mcl_totems_totem.png",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--###################
|
--###################
|
||||||
--################### AGENT - seemingly unused
|
--################### AGENT
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
|
@ -64,81 +64,7 @@ else
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawn on solid blocks at or below Sea level and the selected light level
|
-- Spawn on solid blocks at or below Sea level and the selected light level
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:bat", mobs_mc.spawn.solid, {"air"}, 0, maxlight, 20, 5000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-1)
|
||||||
"mobs_mc:bat",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"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",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
maxlight,
|
|
||||||
20,
|
|
||||||
5000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.water-1)
|
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
-- daufinsyd
|
-- daufinsyd
|
||||||
-- My work is under the LGPL terms
|
-- My work is under the LGPL terms
|
||||||
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models -hi 22i ~jordan4ibanez
|
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models
|
||||||
-- blaze.lua partial copy of mobs_mc/ghast.lua
|
-- blaze.lua partial copy of mobs_mc/ghast.lua
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
@ -75,71 +75,9 @@ mobs:register_mob("mobs_mc:blaze", {
|
||||||
fear_height = 0,
|
fear_height = 0,
|
||||||
glow = 14,
|
glow = 14,
|
||||||
fire_resistant = true,
|
fire_resistant = true,
|
||||||
do_custom = function(self)
|
|
||||||
if self.state == "attack" and vector.distance(self.object:get_pos(), self.attack:get_pos()) < 1.2 then
|
|
||||||
mcl_burning.set_on_fire(self.attack, 5)
|
|
||||||
end
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
|
||||||
velocity = {x=0, y=math.random(1,1), z=0},
|
|
||||||
expirationtime = math.random(),
|
|
||||||
size = math.random(1, 4),
|
|
||||||
collisiondetection = true,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_smoke_anim.png^[colorize:#2c2c2c:255",
|
|
||||||
animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 8,
|
|
||||||
aspect_h = 8,
|
|
||||||
length = 2.05,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
|
||||||
velocity = {x=0, y=math.random(1,1), z=0},
|
|
||||||
expirationtime = math.random(),
|
|
||||||
size = math.random(1, 4),
|
|
||||||
collisiondetection = true,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_smoke_anim.png^[colorize:#424242:255",
|
|
||||||
animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 8,
|
|
||||||
aspect_h = 8,
|
|
||||||
length = 2.05,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
|
||||||
velocity = {x=0, y=math.random(1,1), z=0},
|
|
||||||
expirationtime = math.random(),
|
|
||||||
size = math.random(1, 4),
|
|
||||||
collisiondetection = true,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_smoke_anim.png^[colorize:#0f0f0f:255",
|
|
||||||
animation = {
|
|
||||||
type = "vertical_frames",
|
|
||||||
aspect_w = 8,
|
|
||||||
aspect_h = 8,
|
|
||||||
length = 2.05,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:blaze", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||||
"mobs_mc:blaze",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{"Nether"},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
5000,
|
|
||||||
3,
|
|
||||||
mobs_mc.spawn_height.nether_min,
|
|
||||||
mobs_mc.spawn_height.nether_max)
|
|
||||||
|
|
||||||
-- Blaze fireball
|
-- Blaze fireball
|
||||||
mobs:register_arrow("mobs_mc:blaze_fireball", {
|
mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||||
|
|
|
@ -100,34 +100,7 @@ mobs:register_mob("mobs_mc:chicken", {
|
||||||
})
|
})
|
||||||
|
|
||||||
--spawn
|
--spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:chicken", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:chicken",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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.spawn_height.water,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)
|
mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)
|
||||||
|
|
|
@ -145,53 +145,8 @@ mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
||||||
|
|
||||||
|
|
||||||
-- Spawning
|
-- Spawning
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:cow", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 10, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:cow",
|
mobs:spawn_specific("mobs_mc:mooshroom", mobs_mc.spawn.mushroom_island, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 5, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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.spawn_height.water,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:mooshroom",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"MushroomIsland"
|
|
||||||
},
|
|
||||||
9,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
17000,
|
|
||||||
5,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)
|
mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)
|
||||||
|
|
|
@ -39,8 +39,6 @@ mobs:register_mob("mobs_mc:creeper", {
|
||||||
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
|
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
|
||||||
attack_type = "explode",
|
attack_type = "explode",
|
||||||
|
|
||||||
--hssssssssssss
|
|
||||||
|
|
||||||
explosion_strength = 3,
|
explosion_strength = 3,
|
||||||
explosion_radius = 3.5,
|
explosion_radius = 3.5,
|
||||||
explosion_damage_radius = 3.5,
|
explosion_damage_radius = 3.5,
|
||||||
|
@ -78,6 +76,7 @@ mobs:register_mob("mobs_mc:creeper", {
|
||||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||||
if self._forced_explosion_countdown_timer <= 0 then
|
if self._forced_explosion_countdown_timer <= 0 then
|
||||||
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)
|
||||||
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -140,9 +139,6 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_creeper.b3d",
|
mesh = "mobs_mc_creeper.b3d",
|
||||||
|
|
||||||
--BOOM
|
|
||||||
|
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_creeper.png",
|
{"mobs_mc_creeper.png",
|
||||||
"mobs_mc_creeper_charge.png"},
|
"mobs_mc_creeper_charge.png"},
|
||||||
|
@ -199,6 +195,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||||
if self._forced_explosion_countdown_timer <= 0 then
|
if self._forced_explosion_countdown_timer <= 0 then
|
||||||
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)
|
||||||
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -253,158 +250,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
glow = 3,
|
glow = 3,
|
||||||
})
|
})
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:creeper", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 16500, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:creeper",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
7,
|
|
||||||
20,
|
|
||||||
16500,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)
|
mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
mcl_mobs
|
|
|
@ -50,8 +50,8 @@ mobs:register_mob("mobs_mc:enderdragon", {
|
||||||
arrow = "mobs_mc:dragon_fireball",
|
arrow = "mobs_mc:dragon_fireball",
|
||||||
shoot_interval = 0.5,
|
shoot_interval = 0.5,
|
||||||
shoot_offset = -1.0,
|
shoot_offset = -1.0,
|
||||||
xp_min = 500,
|
xp_min = 12000,
|
||||||
xp_max = 500,
|
xp_max = 12000,
|
||||||
animation = {
|
animation = {
|
||||||
fly_speed = 8, stand_speed = 8,
|
fly_speed = 8, stand_speed = 8,
|
||||||
stand_start = 0, stand_end = 20,
|
stand_start = 0, stand_end = 20,
|
||||||
|
@ -59,47 +59,15 @@ mobs:register_mob("mobs_mc:enderdragon", {
|
||||||
run_start = 0, run_end = 20,
|
run_start = 0, run_end = 20,
|
||||||
},
|
},
|
||||||
ignores_nametag = true,
|
ignores_nametag = true,
|
||||||
do_custom = function(self)
|
on_die = function(self, own_pos)
|
||||||
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
|
if self._egg_spawn_pos then
|
||||||
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
local pos = minetest.string_to_pos(self._egg_spawn_pos)
|
||||||
local luaentity = obj:get_luaentity()
|
--if minetest.get_node(pos).buildable_to then
|
||||||
if luaentity and luaentity.name == "mcl_end:crystal" then
|
minetest.set_node(pos, {name = mobs_mc.items.dragon_egg})
|
||||||
if luaentity.beam then
|
return
|
||||||
if luaentity.beam == self.beam then
|
--end
|
||||||
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
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_die = function(self, pos)
|
|
||||||
if self._portal_pos then
|
|
||||||
mcl_portals.spawn_gateway_portal()
|
|
||||||
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
|
|
||||||
if self._initial then
|
|
||||||
mcl_experience.throw_experience(pos, 11500) -- 500 + 11500 = 12000
|
|
||||||
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
minetest.add_item(own_pos, mobs_mc.items.dragon_egg)
|
||||||
end,
|
end,
|
||||||
fire_resistant = true,
|
fire_resistant = true,
|
||||||
})
|
})
|
||||||
|
|
|
@ -295,8 +295,7 @@ mobs:register_mob("mobs_mc:enderman", {
|
||||||
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
||||||
-- Check for arrows and people nearby.
|
-- Check for arrows and people nearby.
|
||||||
local 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, 4)
|
||||||
local objs = minetest.get_objects_inside_radius(enderpos, 2)
|
|
||||||
for n = 1, #objs do
|
for n = 1, #objs do
|
||||||
local obj = objs[n]
|
local obj = objs[n]
|
||||||
if obj then
|
if obj then
|
||||||
|
@ -308,7 +307,7 @@ mobs:register_mob("mobs_mc:enderman", {
|
||||||
else
|
else
|
||||||
local lua = obj:get_luaentity()
|
local lua = obj:get_luaentity()
|
||||||
if lua then
|
if lua then
|
||||||
if lua.name == "mcl_bows:arrow_entity" or lua.name == "mcl_throwing:snowball_entity" then
|
if lua.name == "mcl_bows:arrow_entity" then
|
||||||
self:teleport(nil)
|
self:teleport(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -323,50 +322,41 @@ mobs:register_mob("mobs_mc:enderman", {
|
||||||
-- self:teleport(nil)
|
-- self:teleport(nil)
|
||||||
-- self.state = ""
|
-- self.state = ""
|
||||||
--else
|
--else
|
||||||
if self.attack ~= nil and not minetest.settings:get_bool("creative_mode") then
|
if self.attack ~= nil then
|
||||||
self.state = 'attack'
|
self.state = 'attack'
|
||||||
end
|
end
|
||||||
--end
|
--end
|
||||||
end
|
end
|
||||||
-- Check to see if people are near by enough to look at us.
|
-- Check to see if people are near by enough to look at us.
|
||||||
for _,obj in pairs(minetest.get_connected_players()) do
|
local objs = minetest.get_objects_inside_radius(enderpos, 64)
|
||||||
|
local obj
|
||||||
--check if they are within radius
|
for n = 1, #objs do
|
||||||
local player_pos = obj:get_pos()
|
obj = objs[n]
|
||||||
if player_pos then -- prevent crashing in 1 in a million scenario
|
if obj then
|
||||||
|
if minetest.is_player(obj) then
|
||||||
local ender_distance = vector.distance(enderpos, player_pos)
|
|
||||||
if ender_distance <= 64 then
|
|
||||||
|
|
||||||
-- Check if they are looking at us.
|
-- Check if they are looking at us.
|
||||||
|
local player_pos = obj:get_pos()
|
||||||
local look_dir_not_normalized = obj:get_look_dir()
|
local look_dir_not_normalized = obj:get_look_dir()
|
||||||
local look_dir = vector.normalize(look_dir_not_normalized)
|
local look_dir = vector.normalize(look_dir_not_normalized)
|
||||||
local player_eye_height = obj:get_properties().eye_height
|
local look_pos = vector.new({x = look_dir.x+player_pos.x, y = look_dir.y+player_pos.y + 1.5, z = look_dir.z+player_pos.z}) -- Arbitrary value (1.5) is head level according to player info mod.
|
||||||
|
-- Cast up to 64 to see if player is looking at enderman.
|
||||||
--skip player if they have no data - log it
|
for n = 1,64,.25 do
|
||||||
if not player_eye_height then
|
local node = minetest.get_node(look_pos)
|
||||||
minetest.log("error", "Enderman at location: ".. dump(enderpos).." has indexed a null player!")
|
if node.name ~= "air" then
|
||||||
else
|
break
|
||||||
|
end
|
||||||
--calculate very quickly the exact location the player is looking
|
if look_pos.x-1<enderpos.x and look_pos.x+1>enderpos.x and look_pos.y-2.89<enderpos.y and look_pos.y-2>enderpos.y and look_pos.z-1<enderpos.z and look_pos.z+1>enderpos.z then
|
||||||
--within the distance between the two "heads" (player and enderman)
|
|
||||||
local look_pos = vector.new(player_pos.x, player_pos.y + player_eye_height, player_pos.z)
|
|
||||||
local look_pos_base = look_pos
|
|
||||||
local ender_eye_pos = vector.new(enderpos.x, enderpos.y + 2.75, enderpos.z)
|
|
||||||
local eye_distance_from_player = vector.distance(ender_eye_pos, look_pos)
|
|
||||||
look_pos = vector.add(look_pos, vector.multiply(look_dir, eye_distance_from_player))
|
|
||||||
|
|
||||||
--if looking in general head position, turn hostile
|
|
||||||
if minetest.line_of_sight(ender_eye_pos, look_pos_base) and vector.distance(look_pos, ender_eye_pos) <= 0.4 then
|
|
||||||
self.provoked = "staring"
|
self.provoked = "staring"
|
||||||
self.attack = minetest.get_player_by_name(obj:get_player_name())
|
self.attack = minetest.get_player_by_name(obj:get_player_name())
|
||||||
break
|
break
|
||||||
else -- I'm not sure what this part does, but I don't want to break anything - jordan4ibanez
|
else
|
||||||
if self.provoked == "staring" then
|
if self.provoked == "staring" then
|
||||||
self.provoked = "broke_contact"
|
self.provoked = "broke_contact"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
look_pos.x = look_pos.x + (.25 * look_dir.x)
|
||||||
|
look_pos.y = look_pos.y + (.25 * look_dir.y)
|
||||||
|
look_pos.z = look_pos.z + (.25 * look_dir.z)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -544,11 +534,9 @@ mobs:register_mob("mobs_mc:enderman", {
|
||||||
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
|
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
|
||||||
-- self:teleport(nil)
|
-- self:teleport(nil)
|
||||||
--else
|
--else
|
||||||
if pr:next(1, 8) == 8 then --FIXME: real mc rate
|
|
||||||
self:teleport(hitter)
|
self:teleport(hitter)
|
||||||
end
|
self.attack=hitter
|
||||||
self.attack=hitter
|
self.state="attack"
|
||||||
self.state="attack"
|
|
||||||
--end
|
--end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -561,189 +549,11 @@ mobs:register_mob("mobs_mc:enderman", {
|
||||||
|
|
||||||
|
|
||||||
-- End spawn
|
-- End spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 3000, 12, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
|
||||||
"mobs_mc:enderman",
|
|
||||||
"end",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"End"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
3000,
|
|
||||||
12,
|
|
||||||
mobs_mc.spawn_height.end_min,
|
|
||||||
mobs_mc.spawn_height.end_max)
|
|
||||||
-- Overworld spawn
|
-- Overworld spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 19000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:enderman",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
7,
|
|
||||||
30,
|
|
||||||
19000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- Nether spawn (rare)
|
-- Nether spawn (rare)
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 27500, 4, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||||
"mobs_mc:enderman",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
7,
|
|
||||||
30,
|
|
||||||
27500,
|
|
||||||
4,
|
|
||||||
mobs_mc.spawn_height.nether_min,
|
|
||||||
mobs_mc.spawn_height.nether_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
|
mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
|
||||||
|
|
|
@ -63,32 +63,10 @@ mobs:register_mob("mobs_mc:ghast", {
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
instant_death = true,
|
instant_death = true,
|
||||||
fire_resistant = true,
|
fire_resistant = true,
|
||||||
do_custom = function(self)
|
|
||||||
if self.firing == true then
|
|
||||||
self.base_texture = {"mobs_mc_ghast_firing.png"}
|
|
||||||
self.object:set_properties({textures=self.base_texture})
|
|
||||||
else
|
|
||||||
self.base_texture = {"mobs_mc_ghast.png"}
|
|
||||||
self.object:set_properties({textures=self.base_texture})
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:ghast", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 18000, 2, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||||
"mobs_mc:ghast",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
18000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.nether_min,
|
|
||||||
mobs_mc.spawn_height.nether_max)
|
|
||||||
|
|
||||||
-- fireball (projectile)
|
-- fireball (projectile)
|
||||||
mobs:register_arrow("mobs_mc:fireball", {
|
mobs:register_arrow("mobs_mc:fireball", {
|
||||||
|
@ -96,7 +74,6 @@ mobs:register_arrow("mobs_mc:fireball", {
|
||||||
visual_size = {x = 1, y = 1},
|
visual_size = {x = 1, y = 1},
|
||||||
textures = {"mcl_fire_fire_charge.png"},
|
textures = {"mcl_fire_fire_charge.png"},
|
||||||
velocity = 15,
|
velocity = 15,
|
||||||
collisionbox = {-.5, -.5, -.5, .5, .5, .5},
|
|
||||||
|
|
||||||
hit_player = function(self, player)
|
hit_player = function(self, player)
|
||||||
if rawget(_G, "armor") and armor.last_damage_types then
|
if rawget(_G, "armor") and armor.last_damage_types then
|
||||||
|
|
|
@ -106,7 +106,7 @@ mobs:register_mob("mobs_mc:guardian_elder", {
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Spawning disabled due to size issues <- what do you mean? -j4i
|
-- Spawning disabled due to size issues
|
||||||
-- TODO: Re-enable spawning
|
-- TODO: Re-enable spawning
|
||||||
-- 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)
|
-- 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)
|
||||||
|
|
||||||
|
|
|
@ -157,29 +157,8 @@ local horse = {
|
||||||
self._regentimer = 0
|
self._regentimer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Some weird human is riding. Buck them off?
|
-- if driver present allow control of horse
|
||||||
if self.driver and not self.tamed and self.buck_off_time <= 0 then
|
if self.driver then
|
||||||
if math.random() < 0.2 then
|
|
||||||
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
|
|
||||||
self.buck_off_time = 20
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Tick the timer for trying to buck the player off
|
|
||||||
if self.buck_off_time then
|
|
||||||
if self.driver then
|
|
||||||
self.buck_off_time = self.buck_off_time - 1
|
|
||||||
else
|
|
||||||
-- Player isn't riding anymore so no need to count
|
|
||||||
self.buck_off_time = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if driver present and horse has a saddle allow control of horse
|
|
||||||
if self.driver and self._saddle then
|
|
||||||
|
|
||||||
mobs.drive(self, "walk", "stand", false, dtime)
|
mobs.drive(self, "walk", "stand", false, dtime)
|
||||||
|
|
||||||
|
@ -212,49 +191,6 @@ local horse = {
|
||||||
local item = clicker:get_wielded_item()
|
local item = clicker:get_wielded_item()
|
||||||
local iname = item:get_name()
|
local iname = item:get_name()
|
||||||
local heal = 0
|
local heal = 0
|
||||||
|
|
||||||
-- Taming
|
|
||||||
self.temper = self.temper or (math.random(1,100))
|
|
||||||
|
|
||||||
if not self.tamed then
|
|
||||||
local temper_increase = 0
|
|
||||||
|
|
||||||
-- Feeding, intentionally not using mobs:feed_tame because horse taming is
|
|
||||||
-- different and more complicated
|
|
||||||
if (iname == mobs_mc.items.sugar) then
|
|
||||||
temper_increase = 3
|
|
||||||
elseif (iname == mobs_mc.items.wheat) then
|
|
||||||
temper_increase = 3
|
|
||||||
elseif (iname == mobs_mc.items.apple) then
|
|
||||||
temper_increase = 3
|
|
||||||
elseif (iname == mobs_mc.items.golden_carrot) then
|
|
||||||
temper_increase = 5
|
|
||||||
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})
|
|
||||||
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
|
|
||||||
if not self.owner or self.owner == "" then
|
|
||||||
self.owner = clicker:get_player_name()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
temper_increase = 5
|
|
||||||
|
|
||||||
-- Clicking on the horse while riding ==> unmount
|
|
||||||
elseif self.driver and self.driver == clicker then
|
|
||||||
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If nothing happened temper_increase = 0 and addition does nothing
|
|
||||||
self.temper = self.temper + temper_increase
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if can_breed(self.name) then
|
if can_breed(self.name) then
|
||||||
-- Breed horse with golden apple or golden carrot
|
-- Breed horse with golden apple or golden carrot
|
||||||
if (iname == mobs_mc.items.golden_apple) then
|
if (iname == mobs_mc.items.golden_apple) then
|
||||||
|
@ -266,8 +202,7 @@ local horse = {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Feed with anything else
|
-- Feed/tame with anything else
|
||||||
-- TODO heal amounts don't work
|
|
||||||
if (iname == mobs_mc.items.sugar) then
|
if (iname == mobs_mc.items.sugar) then
|
||||||
heal = 1
|
heal = 1
|
||||||
elseif (iname == mobs_mc.items.wheat) then
|
elseif (iname == mobs_mc.items.wheat) then
|
||||||
|
@ -277,7 +212,7 @@ local horse = {
|
||||||
elseif (iname == mobs_mc.items.hay_bale) then
|
elseif (iname == mobs_mc.items.hay_bale) then
|
||||||
heal = 20
|
heal = 20
|
||||||
end
|
end
|
||||||
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, false) then
|
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, true) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -510,60 +445,12 @@ mobs:register_mob("mobs_mc:mule", mule)
|
||||||
|
|
||||||
--===========================
|
--===========================
|
||||||
--Spawn Function
|
--Spawn Function
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:horse", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:horse",
|
mobs:spawn_specific("mobs_mc:donkey", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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.spawn_height.water+3,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:donkey",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Mesa",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
4,
|
|
||||||
mobs_mc.spawn_height.water+3,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 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: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: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: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)
|
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)
|
||||||
|
|
|
@ -217,25 +217,7 @@ mobs:register_mob("mobs_mc:llama", {
|
||||||
})
|
})
|
||||||
|
|
||||||
--spawn
|
--spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:llama", mobs_mc.spawn.savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 5, mobs_mc.spawn_height.water+15, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:llama",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Mesa",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
5,
|
|
||||||
mobs_mc.spawn_height.water+15,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)
|
mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name = mobs_mc
|
name = mobs_mc
|
||||||
author = maikerumine
|
author = maikerumine
|
||||||
description = Adds Minecraft-like monsters and animals.
|
description = Adds Minecraft-like monsters and animals.
|
||||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_colors
|
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
|
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -152,25 +152,6 @@ mobs:register_mob("mobs_mc:cat", cat)
|
||||||
local base_spawn_chance = 5000
|
local base_spawn_chance = 5000
|
||||||
|
|
||||||
-- Spawn ocelot
|
-- Spawn ocelot
|
||||||
--they get the same as the llama because I'm trying to rework so much of this code right now -j4i
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:ocelot",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Jungle",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"JungleM",
|
|
||||||
"JungleEdge",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
5,
|
|
||||||
mobs_mc.spawn_height.water+15,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
--[[
|
|
||||||
mobs:spawn({
|
mobs:spawn({
|
||||||
name = "mobs_mc:ocelot",
|
name = "mobs_mc:ocelot",
|
||||||
nodes = mobs_mc.spawn.jungle,
|
nodes = mobs_mc.spawn.jungle,
|
||||||
|
@ -182,8 +163,8 @@ mobs:spawn({
|
||||||
min_height = mobs_mc.spawn_height.water+1, -- Right above ocean level
|
min_height = mobs_mc.spawn_height.water+1, -- Right above ocean level
|
||||||
max_height = mobs_mc.spawn_height.overworld_max,
|
max_height = mobs_mc.spawn_height.overworld_max,
|
||||||
on_spawn = function(self, pos)
|
on_spawn = function(self, pos)
|
||||||
Note: Minecraft has a 1/3 spawn failure rate.
|
--[[ Note: Minecraft has a 1/3 spawn failure rate.
|
||||||
In this mod it is emulated by reducing the spawn rate accordingly (see above).
|
In this mod it is emulated by reducing the spawn rate accordingly (see above). ]]
|
||||||
|
|
||||||
-- 1/7 chance to spawn 2 ocelot kittens
|
-- 1/7 chance to spawn 2 ocelot kittens
|
||||||
if pr:next(1,7) == 1 then
|
if pr:next(1,7) == 1 then
|
||||||
|
@ -226,7 +207,6 @@ mobs:spawn({
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
]]--
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
-- FIXME: The spawn icon shows a cat texture, not an ocelot texture
|
-- FIXME: The spawn icon shows a cat texture, not an ocelot texture
|
||||||
|
|
|
@ -90,24 +90,8 @@ mobs:register_mob("mobs_mc:parrot", {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome* <- I'll get to this eventually -j4i
|
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome*
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:parrot", {"mcl_core:jungletree", "mcl_core:jungleleaves"}, {"air"}, 0, minetest.LIGHT_MAX+1, 7, 30000, 1, mobs_mc.spawn_height.water+7, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:parrot",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Jungle",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"JungleM",
|
|
||||||
"JungleEdge",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
7,
|
|
||||||
30000,
|
|
||||||
1,
|
|
||||||
mobs_mc.spawn_height.water+7,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:parrot", S("Parrot"), "mobs_mc_spawn_icon_parrot.png", 0)
|
mobs:register_egg("mobs_mc:parrot", S("Parrot"), "mobs_mc_spawn_icon_parrot.png", 0)
|
||||||
|
|
|
@ -182,35 +182,7 @@ mobs:register_mob("mobs_mc:pig", {
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:pig", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 15000, 8, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:pig",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"ColdTaiga",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"BirchForestM",
|
|
||||||
},
|
|
||||||
9,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
8,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:pig", S("Pig"), "mobs_mc_spawn_icon_pig.png", 0)
|
mobs:register_egg("mobs_mc:pig", S("Pig"), "mobs_mc_spawn_icon_pig.png", 0)
|
||||||
|
|
|
@ -67,23 +67,7 @@ mobs:register_mob("mobs_mc:polar_bear", {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:polar_bear", mobs_mc.spawn.snow, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 7000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:polar_bear",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"ColdTaiga",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"IcePlains",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
7000,
|
|
||||||
3,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mobs:register_egg("mobs_mc:polar_bear", S("Polar Bear"), "mobs_mc_spawn_icon_polarbear.png", 0)
|
mobs:register_egg("mobs_mc:polar_bear", S("Polar Bear"), "mobs_mc_spawn_icon_polarbear.png", 0)
|
||||||
|
|
|
@ -107,39 +107,8 @@ end
|
||||||
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||||
|
|
||||||
-- Mob spawning rules.
|
-- Mob spawning rules.
|
||||||
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
-- Different skins depending on spawn location
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:rabbit",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"ColdTaiga",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"BirchForestM",
|
|
||||||
},
|
|
||||||
9,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
8,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local spawn = {
|
local spawn = {
|
||||||
name = "mobs_mc:rabbit",
|
name = "mobs_mc:rabbit",
|
||||||
neighbors = {"air"},
|
neighbors = {"air"},
|
||||||
|
@ -196,7 +165,6 @@ spawn_grass.on_spawn = function(self, pos)
|
||||||
self.object:set_properties({textures = self.base_texture})
|
self.object:set_properties({textures = self.base_texture})
|
||||||
end
|
end
|
||||||
mobs:spawn(spawn_grass)
|
mobs:spawn(spawn_grass)
|
||||||
]]--
|
|
||||||
|
|
||||||
-- Spawn egg
|
-- Spawn egg
|
||||||
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
||||||
|
|
|
@ -25,19 +25,6 @@ local colors = {
|
||||||
unicolor_black = { mobs_mc.items.wool_black, "#000000D0" },
|
unicolor_black = { mobs_mc.items.wool_black, "#000000D0" },
|
||||||
}
|
}
|
||||||
|
|
||||||
local rainbow_colors = {
|
|
||||||
"unicolor_light_red",
|
|
||||||
"unicolor_red",
|
|
||||||
"unicolor_orange",
|
|
||||||
"unicolor_yellow",
|
|
||||||
"unicolor_green",
|
|
||||||
"unicolor_dark_green",
|
|
||||||
"unicolor_light_blue",
|
|
||||||
"unicolor_blue",
|
|
||||||
"unicolor_violet",
|
|
||||||
"unicolor_red_violet"
|
|
||||||
}
|
|
||||||
|
|
||||||
if minetest.get_modpath("mcl_wool") ~= nil then
|
if minetest.get_modpath("mcl_wool") ~= nil then
|
||||||
colors["unicolor_light_blue"] = { mobs_mc.items.wool_light_blue, "#5050FFD0" }
|
colors["unicolor_light_blue"] = { mobs_mc.items.wool_light_blue, "#5050FFD0" }
|
||||||
end
|
end
|
||||||
|
@ -125,7 +112,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Set random color on spawn
|
-- Set random color on spawn
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self)
|
||||||
if not self.initial_color_set then
|
if not self.initial_color_set then
|
||||||
local r = math.random(0,100000)
|
local r = math.random(0,100000)
|
||||||
local textures
|
local textures
|
||||||
|
@ -162,33 +149,6 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
}
|
}
|
||||||
self.initial_color_set = true
|
self.initial_color_set = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_kay27 = self.nametag == "kay27"
|
|
||||||
|
|
||||||
if self.color_change_timer then
|
|
||||||
local old_color = self.color
|
|
||||||
if is_kay27 then
|
|
||||||
self.color_change_timer = self.color_change_timer - dtime
|
|
||||||
if self.color_change_timer < 0 then
|
|
||||||
self.color_change_timer = 0.5
|
|
||||||
self.color_index = (self.color_index + 1) % #rainbow_colors
|
|
||||||
self.color = rainbow_colors[self.color_index + 1]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.color_change_timer = nil
|
|
||||||
self.color_index = nil
|
|
||||||
self.color = self.initial_color
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_color ~= self.color then
|
|
||||||
self.base_texture = sheep_texture(self.color)
|
|
||||||
self.object:set_properties({textures = self.base_texture})
|
|
||||||
end
|
|
||||||
elseif is_kay27 then
|
|
||||||
self.initial_color = self.color
|
|
||||||
self.color_change_timer = 0
|
|
||||||
self.color_index = -1
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
|
@ -303,35 +263,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:sheep", mobs_mc.spawn.grassland, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:sheep",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"ColdTaiga",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"BirchForestM",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
3,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:sheep", S("Sheep"), "mobs_mc_spawn_icon_sheep.png", 0)
|
mobs:register_egg("mobs_mc:sheep", S("Sheep"), "mobs_mc_spawn_icon_sheep.png", 0)
|
||||||
|
|
|
@ -81,17 +81,4 @@ mobs:register_arrow("mobs_mc:shulkerbullet", {
|
||||||
|
|
||||||
mobs:register_egg("mobs_mc:shulker", S("Shulker"), "mobs_mc_spawn_icon_shulker.png", 0)
|
mobs:register_egg("mobs_mc:shulker", S("Shulker"), "mobs_mc_spawn_icon_shulker.png", 0)
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:shulker", mobs_mc.spawn.end_city, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 2, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
|
||||||
"mobs_mc:shulker",
|
|
||||||
"end",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"End"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
5000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.end_min,
|
|
||||||
mobs_mc.spawn_height.end_max)
|
|
||||||
|
|
|
@ -30,14 +30,13 @@ local skeleton = {
|
||||||
"mcl_bows_bow_0.png", -- bow
|
"mcl_bows_bow_0.png", -- bow
|
||||||
"mobs_mc_skeleton.png", -- skeleton
|
"mobs_mc_skeleton.png", -- skeleton
|
||||||
} },
|
} },
|
||||||
visual_size = {x=1, y=1},
|
visual_size = {x=3, y=3},
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
textures = {
|
sounds = {
|
||||||
{
|
random = "mobs_mc_skeleton_random",
|
||||||
"mobs_mc_empty.png", -- armor
|
death = "mobs_mc_skeleton_death",
|
||||||
"mobs_mc_skeleton.png", -- texture
|
damage = "mobs_mc_skeleton_hurt",
|
||||||
"mcl_bows_bow_0.png", -- wielded_item
|
distance = 16,
|
||||||
}
|
|
||||||
},
|
},
|
||||||
walk_velocity = 1.2,
|
walk_velocity = 1.2,
|
||||||
run_velocity = 2.4,
|
run_velocity = 2.4,
|
||||||
|
@ -112,9 +111,9 @@ local stray = table.copy(skeleton)
|
||||||
stray.mesh = "mobs_mc_stray.b3d"
|
stray.mesh = "mobs_mc_stray.b3d"
|
||||||
stray.textures = {
|
stray.textures = {
|
||||||
{
|
{
|
||||||
"mobs_mc_stray_overlay.png",
|
|
||||||
"mobs_mc_stray.png",
|
|
||||||
"mcl_bows_bow_0.png",
|
"mcl_bows_bow_0.png",
|
||||||
|
"mobs_mc_stray.png",
|
||||||
|
"mobs_mc_stray_overlay.png",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
-- TODO: different sound (w/ echo)
|
-- TODO: different sound (w/ echo)
|
||||||
|
@ -140,195 +139,13 @@ table.insert(stray.drops, {
|
||||||
mobs:register_mob("mobs_mc:stray", stray)
|
mobs:register_mob("mobs_mc:stray", stray)
|
||||||
|
|
||||||
-- Overworld spawn
|
-- Overworld spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 17000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:skeleton",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
7,
|
|
||||||
20,
|
|
||||||
17000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.overworld_min,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
|
|
||||||
-- Nether spawn
|
-- Nether spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 10000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||||
"mobs_mc:skeleton",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
7,
|
|
||||||
30,
|
|
||||||
10000,
|
|
||||||
3,
|
|
||||||
mobs_mc.spawn_height.nether_min,
|
|
||||||
mobs_mc.spawn_height.nether_max)
|
|
||||||
|
|
||||||
-- Stray spawn
|
-- Stray spawn
|
||||||
-- TODO: Spawn directly under the sky
|
-- TODO: Spawn directly under the sky
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:stray", mobs_mc.spawn.snow, {"air"}, 0, 7, 20, 19000, 2, mobs_mc.spawn_height.water, mobs_mc.spawn_height.overworld_max)
|
||||||
"mobs_mc:stray",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"ColdTaiga",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"IcePlains",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
7,
|
|
||||||
20,
|
|
||||||
19000,
|
|
||||||
2,
|
|
||||||
mobs_mc.spawn_height.water,
|
|
||||||
mobs_mc.spawn_height.overworld_max)
|
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
|
|
|
@ -25,12 +25,11 @@ mobs:register_mob("mobs_mc:witherskeleton", {
|
||||||
mesh = "mobs_mc_witherskeleton.b3d",
|
mesh = "mobs_mc_witherskeleton.b3d",
|
||||||
textures = {
|
textures = {
|
||||||
{
|
{
|
||||||
"mobs_mc_empty.png", -- armor
|
|
||||||
"mobs_mc_wither_skeleton.png", -- wither skeleton
|
|
||||||
"default_tool_stonesword.png", -- sword
|
"default_tool_stonesword.png", -- sword
|
||||||
|
"mobs_mc_wither_skeleton.png", -- wither skeleton
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visual_size = {x=1.2, y=1.2},
|
visual_size = {x=3.6, y=3.6},
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
sounds = {
|
sounds = {
|
||||||
random = "mobs_mc_skeleton_random",
|
random = "mobs_mc_skeleton_random",
|
||||||
|
@ -95,20 +94,7 @@ mobs:register_mob("mobs_mc:witherskeleton", {
|
||||||
})
|
})
|
||||||
|
|
||||||
--spawn
|
--spawn
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:witherskeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 5000, 5, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||||
"mobs_mc:witherskeleton",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
7,
|
|
||||||
30,
|
|
||||||
5000,
|
|
||||||
5,
|
|
||||||
mobs_mc.spawn_height.nether_min,
|
|
||||||
mobs_mc.spawn_height.nether_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "mobs_mc_spawn_icon_witherskeleton.png", 0)
|
mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "mobs_mc_spawn_icon_witherskeleton.png", 0)
|
||||||
|
|
|
@ -51,6 +51,7 @@ local spawn_children_on_die = function(child_mob, children_count, spawn_distance
|
||||||
end
|
end
|
||||||
end, children, self.attack)
|
end, children, self.attack)
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ local slime_big = {
|
||||||
fear_height = 0,
|
fear_height = 0,
|
||||||
spawn_small_alternative = "mobs_mc:slime_small",
|
spawn_small_alternative = "mobs_mc:slime_small",
|
||||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
|
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
|
||||||
|
fire_resistant = true,
|
||||||
use_texture_alpha = true,
|
use_texture_alpha = true,
|
||||||
}
|
}
|
||||||
mobs:register_mob("mobs_mc:slime_big", slime_big)
|
mobs:register_mob("mobs_mc:slime_big", slime_big)
|
||||||
|
@ -157,137 +159,9 @@ mobs:register_mob("mobs_mc:slime_tiny", slime_tiny)
|
||||||
local smin = mobs_mc.spawn_height.overworld_min
|
local smin = mobs_mc.spawn_height.overworld_min
|
||||||
local smax = mobs_mc.spawn_height.water - 23
|
local smax = mobs_mc.spawn_height.water - 23
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:slime_tiny", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 12000, 4, smin, smax)
|
||||||
"mobs_mc:slime_tiny",
|
mobs:spawn_specific("mobs_mc:slime_small", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8500, 4, smin, smax)
|
||||||
"overworld",
|
mobs:spawn_specific("mobs_mc:slime_big", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 10000, 4, smin, smax)
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
12000,
|
|
||||||
4,
|
|
||||||
smin,
|
|
||||||
smax)
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:slime_small",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
8500,
|
|
||||||
4,
|
|
||||||
smin,
|
|
||||||
smax)
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:slime_big",
|
|
||||||
"overworld",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"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,
|
|
||||||
10000,
|
|
||||||
4,
|
|
||||||
smin,
|
|
||||||
smax)
|
|
||||||
|
|
||||||
-- Magma cube
|
-- Magma cube
|
||||||
local magma_cube_big = {
|
local magma_cube_big = {
|
||||||
|
@ -400,55 +274,13 @@ mobs:register_mob("mobs_mc:magma_cube_tiny", magma_cube_tiny)
|
||||||
local mmin = mobs_mc.spawn_height.nether_min
|
local mmin = mobs_mc.spawn_height.nether_min
|
||||||
local mmax = mobs_mc.spawn_height.nether_max
|
local mmax = mobs_mc.spawn_height.nether_max
|
||||||
|
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mmin, mmax)
|
||||||
"mobs_mc:magma_cube_tiny",
|
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15500, 4, mmin, mmax)
|
||||||
"nether",
|
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 16000, 4, mmin, mmax)
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15000,
|
|
||||||
4,
|
|
||||||
mmin,
|
|
||||||
mmax)
|
|
||||||
|
|
||||||
|
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
|
||||||
"mobs_mc:magma_cube_small",
|
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
15500,
|
|
||||||
4,
|
|
||||||
mmin,
|
|
||||||
mmax)
|
|
||||||
|
|
||||||
mobs:spawn_specific(
|
|
||||||
"mobs_mc:magma_cube_big",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
16000,
|
|
||||||
4,
|
|
||||||
mmin,
|
|
||||||
mmax)
|
|
||||||
|
|
||||||
--mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
|
|
||||||
--mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
|
|
||||||
--mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
|
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue