Compare commits
1 Commits
master
...
Minecraft_
Author | SHA1 | Date |
---|---|---|
|
2be2669dc7 |
|
@ -1,12 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
|
|
||||||
[*.lua]
|
|
||||||
charset = utf8
|
|
||||||
indent_style = tab
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
keep_one_space_between_table_and_bracket = false
|
|
||||||
spaces_around_operators = true
|
|
|
@ -3,4 +3,3 @@
|
||||||
*.blend1
|
*.blend1
|
||||||
*.blend2
|
*.blend2
|
||||||
*.blend3
|
*.blend3
|
||||||
/.idea/
|
|
||||||
|
|
123
CONTRIBUTING.md
|
@ -2,7 +2,7 @@
|
||||||
So you want to contribute to MineClone2?
|
So you want to contribute to MineClone2?
|
||||||
Wow, thank you! :-)
|
Wow, thank you! :-)
|
||||||
|
|
||||||
MineClone2 is maintained by Nicu and Cora. If you have any
|
MineClone2 is maintained by Nicu and Fleckenstein. If you have any
|
||||||
problems or questions, contact us (See Links section below).
|
problems or questions, contact us (See Links section below).
|
||||||
|
|
||||||
You can help with MineClone2's development in many different ways,
|
You can help with MineClone2's development in many different ways,
|
||||||
|
@ -11,9 +11,18 @@ whether you're a programmer or not.
|
||||||
## MineClone2's development target is to...
|
## MineClone2's development target is to...
|
||||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||||
based on the Minetest engine with polished features, usable in both
|
based on the Minetest engine with polished features, usable in both
|
||||||
singleplayer and multiplayer. Currently, a lot of Minecraft features
|
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||||
are already implemented.
|
Edition 1.12.2** features are already implemented and polishing existing
|
||||||
Polishing existing features is always welcome.
|
features are prioritized over new feature requests.
|
||||||
|
- With lessened priority yet strictly, implement features targetting
|
||||||
|
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||||
|
by the Minetest Engine). This means features in parity with the listed
|
||||||
|
Minecraft experiences are prioritized over those that don't fulfill this
|
||||||
|
scope.
|
||||||
|
- Optionally, create a performant experience that will run relatively
|
||||||
|
well on really low spec computers. Unfortunately, due to Minecraft's
|
||||||
|
mechanisms and Minetest engine's limitations along with a very small
|
||||||
|
playerbase on low spec computers, optimizations are hard to investigate.
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
||||||
|
@ -36,10 +45,8 @@ referenced frequently because of its usefulness. As such, it is valuable
|
||||||
in learning how git works and its terminology. It can also help you
|
in learning how git works and its terminology. It can also help you
|
||||||
keeping your game updated, and easily test pull requests.
|
keeping your game updated, and easily test pull requests.
|
||||||
|
|
||||||
Look at our wiki for some concrete guides:
|
|
||||||
https://git.minetest.land/MineClone2/MineClone2/wiki/
|
|
||||||
|
|
||||||
## How you can help as a non-programmer
|
## How you can help as a non-programmer
|
||||||
|
|
||||||
As someone who does not know how to write programs in Lua or does not
|
As someone who does not know how to write programs in Lua or does not
|
||||||
know how to use the Minetest API, you can still help us out a lot. For
|
know how to use the Minetest API, you can still help us out a lot. For
|
||||||
example, by opening an issue in the
|
example, by opening an issue in the
|
||||||
|
@ -51,10 +58,12 @@ you can report a bug or request a feature.
|
||||||
discussion.
|
discussion.
|
||||||
* Choose a descriptive title (e.g. not just "crash", "bug" or "question"
|
* Choose a descriptive title (e.g. not just "crash", "bug" or "question"
|
||||||
).
|
).
|
||||||
|
* Please write in plain, understandable English. It will be easier to
|
||||||
|
communicate.
|
||||||
|
* Please start the issue title with a capital letter.
|
||||||
* Always check the currently opened issues before creating a new one.
|
* Always check the currently opened issues before creating a new one.
|
||||||
Try not to report bugs that have already been reported or request features
|
Don't report bugs that have already been reported or request features
|
||||||
that already have been requested. This can often be ambiguous though.
|
that already have been requested.
|
||||||
If in doubt open an issue!
|
|
||||||
* If you know about Minetest's inner workings, please think about
|
* If you know about Minetest's inner workings, please think about
|
||||||
whether the bug / the feature that you are reporting / requesting is
|
whether the bug / the feature that you are reporting / requesting is
|
||||||
actually an issue with Minetest itself, and if it is, head to the
|
actually an issue with Minetest itself, and if it is, head to the
|
||||||
|
@ -64,9 +73,6 @@ instead.
|
||||||
an issue, feel free to ask on the Discord / Matrix server or the IRC
|
an issue, feel free to ask on the Discord / Matrix server or the IRC
|
||||||
channel.
|
channel.
|
||||||
|
|
||||||
The link to the mesehub registration page is: https://git.minetest.land/user/sign_up
|
|
||||||
(It appears to sometimes get lost on the page itsself)
|
|
||||||
|
|
||||||
### Reporting bugs
|
### Reporting bugs
|
||||||
* A bug is an unintended behavior or, in the worst case, a crash.
|
* A bug is an unintended behavior or, in the worst case, a crash.
|
||||||
However, it is not a bug if you believe something is missing in the
|
However, it is not a bug if you believe something is missing in the
|
||||||
|
@ -105,28 +111,23 @@ would report issues will pull requests similar to when you were
|
||||||
reporting bugs that are the mainline (See Reporting bugs section). You
|
reporting bugs that are the mainline (See Reporting bugs section). You
|
||||||
can find currently open pull requests here:
|
can find currently open pull requests here:
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/pulls>. Note that pull
|
<https://git.minetest.land/MineClone2/MineClone2/pulls>. Note that pull
|
||||||
requests that start with a `WIP:` are not done yet and therefore could
|
requests that start with a `WIP:` are not done yet, and therefore might
|
||||||
still undergo substantial change. Testing these is still helpful however
|
not work, so it's not very useful to try them out yet.
|
||||||
because that is the reason developers put them up as WIP so other people
|
|
||||||
can have a look at the PR.
|
|
||||||
|
|
||||||
### Contributing assets
|
### Contributing assets
|
||||||
Due to license problems, MineClone2 cannot use Minecraft's assets,
|
Due to license problems, MineClone2 unfortunately cannot use
|
||||||
therefore we are always looking for asset contributions.
|
Minecraft's assets, therefore we are always looking for asset
|
||||||
|
contributions. To contribute assets, it can be useful to learn git
|
||||||
To contribute assets, it can be useful to learn git basics and read
|
basics and read the section for Programmers of this document, however
|
||||||
the section for Programmers of this document, however this is not required.
|
this is not required. It's also a good idea to join the Discord server
|
||||||
It's also a good idea to join the Discord server
|
|
||||||
(or alternatively IRC or Matrix).
|
(or alternatively IRC or Matrix).
|
||||||
|
|
||||||
#### Textures
|
#### Textures
|
||||||
For textures we use the Pixel Perfection texture pack. For older Minecraft
|
For textures we use the Pixel Perfection texture pack. This is mostly
|
||||||
features that is mostly enough but a lot of the newer textures in it are
|
enough; however in some cases - e.g. for newer Minecraft features, it's
|
||||||
copies or slight modifications of the original MC textures so great caution
|
useful to have texture artists around. If you want to make such
|
||||||
needs to be taken when using any textures coming from Minecraft texture
|
contributions, join our Discord server. Demands for textures will be
|
||||||
packs.
|
communicated there.
|
||||||
If you want to make such contributions, join our Discord server. Demands
|
|
||||||
for textures will be communicated there.
|
|
||||||
|
|
||||||
#### Sounds
|
#### Sounds
|
||||||
MineClone2 currently does not have a consistent way to handle sounds.
|
MineClone2 currently does not have a consistent way to handle sounds.
|
||||||
|
@ -250,25 +251,16 @@ of the results)
|
||||||
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
||||||
(Include a link to the specific page you used)
|
(Include a link to the specific page you used)
|
||||||
|
|
||||||
### Guidelines
|
### Stick to our guidelines
|
||||||
|
|
||||||
#### Git Guidelines
|
#### Git Guidelines
|
||||||
* Pushing to master is disabled - don't even try it.
|
* We use merge rather than rebase or squash merge
|
||||||
* Every change is tracked as a PR.
|
* We don't use git submodules.
|
||||||
* All but the tiniest changes require at least one approval from a Developer
|
* Your commit names should be relatively descriptive, e.g. when saying
|
||||||
* To update branches we use rebase not merge (so we don't end up with
|
"Fix #issueid", the commit message should also contain the title of the
|
||||||
excessive git bureaucracy commits in master)
|
issue.
|
||||||
* We use merge to add the commits from a PR/branch to master
|
* Try to keep your commits as atomic as possible (advise, but completely
|
||||||
* Submodules should only be used if a) upstream is highly reliable and
|
optional)
|
||||||
b) it is 100% certain that no mcl2 specific changes to the code will be
|
|
||||||
needed (this has never been the case before, hence mcl2 is submodule free so far)
|
|
||||||
* Commit messages should be descriptive and never contain mcl2 specific
|
|
||||||
issueids - there are other projects who might use commits from mcl2 and
|
|
||||||
it will confuse their issue trackers.
|
|
||||||
* Try to group your submissions best as you can:
|
|
||||||
* Try to keep your PRs small: In some cases things reasonably be can't
|
|
||||||
split up but in general multiple small PRs are better than a big one.
|
|
||||||
* Similarly multiple small commits are better than a giant one. (use git commit -p)
|
|
||||||
|
|
||||||
#### Code Guidelines
|
#### Code Guidelines
|
||||||
* Each mod must provide `mod.conf`.
|
* Each mod must provide `mod.conf`.
|
||||||
|
@ -351,23 +343,36 @@ Active and trusted contributors are often granted write access to the
|
||||||
MineClone2 repository.
|
MineClone2 repository.
|
||||||
|
|
||||||
#### Developer responsibilities
|
#### Developer responsibilities
|
||||||
- If you have developer privileges you can just open a new branch in the
|
- You should not push things directly to
|
||||||
mcl2 repository (which is preferred). From that you create a pull request.
|
MineClone2 master - rather, do your work on a branch on your private
|
||||||
This way other people can review your changes and make sure they work
|
repository, then create a pull request. This way other people can review
|
||||||
before they get merged.
|
your changes and make sure they work before they get merged.
|
||||||
- If you do not (yet) have developer privs you do your work on a branch
|
- Merge PRs only when they have recieved the necessary feedback and have
|
||||||
on your private repository e.g. using the "fork" function on mesehub.
|
been tested by at least two different people (including the author of
|
||||||
- Any developer is welcome to review, test and merge PRs. A PR needs
|
the pull request), to avoid crashes or the introduction of new bugs.
|
||||||
at least one approval (by someone else than the author) but the maintainers
|
- You may also be assigned to issues or pull
|
||||||
are usually relatively quick to react to new submissions.
|
requests as a developer. In this case it is your responsibility to fix
|
||||||
|
the issue / review and merge the pull request when it is ready. You can
|
||||||
|
also unassign yourself from the issue / PR if you have no time or don't
|
||||||
|
want to take care of it for some other reason. After all, everyone is a
|
||||||
|
volunteer and we can't expect you to do work that you are not interested
|
||||||
|
in. **The important thing is that you make sure to inform us if you
|
||||||
|
won't take care of something that has been assigned to you.**
|
||||||
|
- Please assign yourself to something that you want to work on to avoid
|
||||||
|
duplicate work.
|
||||||
|
- As a developer, it should be easy to reach you about your work. You
|
||||||
|
should be in at least one of the public MineClone2 discussion rooms -
|
||||||
|
preferrably Discord, but if you really don't like Discord, Matrix
|
||||||
|
or IRC are fine too.
|
||||||
|
|
||||||
### Maintainer status
|
### Maintainer status
|
||||||
Maintainers carry the main responsibility for the project.
|
Maintainers carry the main responsibility for the project.
|
||||||
|
|
||||||
#### Maintainer responsibilities
|
#### Maintainer responsibilities
|
||||||
- Making sure issues are addressed and pull requests are reviewed and
|
- Making sure issues are addressed and pull requests are reviewed and
|
||||||
merged.
|
merged, by assigning either themselves or Developers to issues / PRs
|
||||||
- Making releases
|
- Making releases
|
||||||
|
- Making sure guidelines are kept
|
||||||
- Making project decisions based on community feedback
|
- Making project decisions based on community feedback
|
||||||
- Granting/revoking developer access
|
- Granting/revoking developer access
|
||||||
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
|
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
|
||||||
|
@ -375,8 +380,8 @@ merged.
|
||||||
- Resolving conflicts and problems within the community
|
- Resolving conflicts and problems within the community
|
||||||
|
|
||||||
#### Current maintainers
|
#### Current maintainers
|
||||||
* Cora - responsible for gameplay review, publishing releases,
|
* Fleckenstein - responsible for gameplay review, publishing releases,
|
||||||
technical guidelines
|
technical guidelines and issue/PR delegation
|
||||||
* Nicu - responsible for community related issues
|
* Nicu - responsible for community related issues
|
||||||
|
|
||||||
#### Release process
|
#### Release process
|
||||||
|
|
44
CREDITS.md
|
@ -8,13 +8,12 @@
|
||||||
|
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
* AncientMariner
|
|
||||||
* Nicu
|
* Nicu
|
||||||
|
* cora
|
||||||
|
|
||||||
## Previous Maintainers
|
## Previous Maintainers
|
||||||
* Fleckenstein
|
* Fleckenstein
|
||||||
* jordan4ibanez
|
* jordan4ibanez
|
||||||
* cora
|
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
* bzoss
|
* bzoss
|
||||||
|
@ -28,14 +27,6 @@
|
||||||
* Code-Sploit
|
* Code-Sploit
|
||||||
* NO11
|
* NO11
|
||||||
* kabou
|
* kabou
|
||||||
* rudzik8
|
|
||||||
* chmodsayshello
|
|
||||||
* PrairieWind
|
|
||||||
* RandomLegoBrick
|
|
||||||
* SumianVoice
|
|
||||||
* MrRar
|
|
||||||
* talamh
|
|
||||||
* Faerraven
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
* Laurent Rocher
|
* Laurent Rocher
|
||||||
|
@ -69,6 +60,7 @@
|
||||||
* Benjamin Schötz
|
* Benjamin Schötz
|
||||||
* Doloment
|
* Doloment
|
||||||
* Sydney Gems
|
* Sydney Gems
|
||||||
|
* talamh
|
||||||
* Emily2255
|
* Emily2255
|
||||||
* Emojigit
|
* Emojigit
|
||||||
* FinishedFragment
|
* FinishedFragment
|
||||||
|
@ -79,22 +71,6 @@
|
||||||
* Sven792
|
* Sven792
|
||||||
* aldum
|
* aldum
|
||||||
* Dieter44
|
* Dieter44
|
||||||
* Pepebotella
|
|
||||||
* MrRar
|
|
||||||
* Lazerbeak12345
|
|
||||||
* mrminer
|
|
||||||
* Thunder1035
|
|
||||||
* opfromthestart
|
|
||||||
* snowyu
|
|
||||||
* FaceDeer
|
|
||||||
* Faerraven / Michieal
|
|
||||||
* FossFanatic
|
|
||||||
* Herbert West
|
|
||||||
* GuyLiner
|
|
||||||
* 3raven
|
|
||||||
* anarquimico
|
|
||||||
* TheOnlyJoeEnderman
|
|
||||||
* Ranko Saotome
|
|
||||||
|
|
||||||
## MineClone5
|
## MineClone5
|
||||||
* kay27
|
* kay27
|
||||||
|
@ -102,12 +78,10 @@
|
||||||
* epCode
|
* epCode
|
||||||
* NO11
|
* NO11
|
||||||
* j45
|
* j45
|
||||||
* chmodsayshello
|
|
||||||
* 3raven
|
* 3raven
|
||||||
* PrairieWind
|
* PrarieWind
|
||||||
* Gustavo6046 / wallabra
|
* Gustavo1
|
||||||
* CableGuy67
|
* CableGuy67
|
||||||
* MrRar
|
|
||||||
|
|
||||||
## Mineclonia
|
## Mineclonia
|
||||||
* erlehmann
|
* erlehmann
|
||||||
|
@ -145,7 +119,6 @@
|
||||||
* 4Evergreen4
|
* 4Evergreen4
|
||||||
* jordan4ibanez
|
* jordan4ibanez
|
||||||
* paramat
|
* paramat
|
||||||
* cora
|
|
||||||
|
|
||||||
## 3D Models
|
## 3D Models
|
||||||
* 22i
|
* 22i
|
||||||
|
@ -161,10 +134,6 @@
|
||||||
* yutyo
|
* yutyo
|
||||||
* NO11
|
* NO11
|
||||||
* kay27
|
* kay27
|
||||||
* MysticTempest
|
|
||||||
* RandomLegoBrick
|
|
||||||
* cora
|
|
||||||
* Faerraven / Michieal
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
* Wuzzy
|
* Wuzzy
|
||||||
|
@ -174,10 +143,6 @@
|
||||||
* pitchum
|
* pitchum
|
||||||
* todoporlalibertad
|
* todoporlalibertad
|
||||||
* Marcin Serwin
|
* Marcin Serwin
|
||||||
* Pepebotella
|
|
||||||
* Emojigit
|
|
||||||
* snowyu
|
|
||||||
* 3raven
|
|
||||||
|
|
||||||
## Funders
|
## Funders
|
||||||
* 40W
|
* 40W
|
||||||
|
@ -185,6 +150,5 @@
|
||||||
## Special thanks
|
## Special thanks
|
||||||
* celeron55 for creating Minetest
|
* celeron55 for creating Minetest
|
||||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||||
* wsor for working tirelessly in the shadows for the good of all of us, particularly helping with solving contentDB and copyright issues.
|
|
||||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
* 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
|
* Notch and Jeb for being the major forces behind Minecraft
|
||||||
|
|
|
@ -74,8 +74,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
||||||
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
||||||
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
||||||
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
|
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
|
||||||
* `leaves=X`: Node will spotaneously decay if no tree trunk nodes remain within 6 blocks distance.
|
|
||||||
* `leaves_orphan`: See above, these nodes are in the process of decayed.
|
|
||||||
|
|
||||||
#### Footnotes
|
#### Footnotes
|
||||||
|
|
||||||
|
|
18
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.82 (in development)
|
Version: 0.75 (in development)
|
||||||
|
|
||||||
### 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
|
||||||
|
@ -91,11 +91,11 @@ The MineClone2 repository is hosted at Mesehub. To contribute or report issues,
|
||||||
## Target
|
## Target
|
||||||
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||||
based on the Minetest engine with polished features, usable in both
|
based on the Minetest engine with polished features, usable in both
|
||||||
singleplayer and multiplayer. Currently, a lot of **Minecraft Java
|
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||||
Edition** features are already implemented and polishing existing
|
Edition 1.12.2** features are already implemented and polishing existing
|
||||||
features are prioritized over new feature requests.
|
features are prioritized over new feature requests.
|
||||||
- With lessened priority yet strictly, implement features targetting
|
- With lessened priority yet strictly, implement features targetting
|
||||||
**Current Minecraft versions + OptiFine** (OptiFine only as far as supported
|
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||||
by the Minetest Engine). This means features in parity with the listed
|
by the Minetest Engine). This means features in parity with the listed
|
||||||
Minecraft experiences are prioritized over those that don't fulfill this
|
Minecraft experiences are prioritized over those that don't fulfill this
|
||||||
scope.
|
scope.
|
||||||
|
@ -108,7 +108,8 @@ playerbase on low spec computers, optimizations are hard to investigate.
|
||||||
This game is currently in **beta** stage.
|
This game is currently in **beta** stage.
|
||||||
It is playable, but not yet feature-complete.
|
It is playable, but not yet feature-complete.
|
||||||
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
||||||
If you want to use the development version of MineClone2 in production, the master branch is usually relatively stable. The testing branch often features some experimental PRs and should be considered less stable.
|
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
||||||
|
It is updated weekly and contains relatively stable code for servers.
|
||||||
|
|
||||||
The following main features are available:
|
The following main features are available:
|
||||||
|
|
||||||
|
@ -123,7 +124,7 @@ The following main features are available:
|
||||||
* Most blocks in the overworld
|
* Most blocks in the overworld
|
||||||
* Water and lava
|
* Water and lava
|
||||||
* Weather
|
* Weather
|
||||||
* 28 biomes + 5 Nether Biomes
|
* 28 biomes
|
||||||
* The Nether, a fiery underworld in another dimension
|
* The Nether, a fiery underworld in another dimension
|
||||||
* Redstone circuits (partially)
|
* Redstone circuits (partially)
|
||||||
* Minecarts (partial)
|
* Minecarts (partial)
|
||||||
|
@ -161,7 +162,7 @@ The following features are incomplete:
|
||||||
* 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):
|
Bonus features (not found in Minecraft 1.12):
|
||||||
|
|
||||||
* 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
|
||||||
|
@ -174,9 +175,6 @@ Bonus features (not found in Minecraft):
|
||||||
* Nether Brick Fence Gate
|
* Nether Brick Fence Gate
|
||||||
* Red Nether Brick Fence
|
* Red Nether Brick Fence
|
||||||
* Red Nether Brick Fence Gate
|
* Red Nether Brick Fence Gate
|
||||||
* Structure replacements - these small variants of Minecraft structures serve as replacements until we can get large structures working:
|
|
||||||
* Woodland Cabin (Mansions)
|
|
||||||
* Nether Outpost (Fortress)
|
|
||||||
|
|
||||||
Technical differences from Minecraft:
|
Technical differences from Minecraft:
|
||||||
|
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
# MineClone2
|
|
||||||
Un jeu non-officiel similaire à Minecraft pour Minetest. Forké depuis Mineclone par davedevils. Développé par de nombreuses personnes. Ni développé ou supporté par Mojang AB.
|
|
||||||
|
|
||||||
Version: 0.79 (en dévelopment)
|
|
||||||
|
|
||||||
### Gameplay
|
|
||||||
|
|
||||||
Vous atterissez dans un monde fait entièrement de cubes et généré aléatoirement. Vous pouvez explorer le monde, miner et construire presque n'importe quel bloc pour créer de nouvelles structures. Vous pouvez choisir de jouer en "mode survie" dans lequel vous devez combattre des monstres et la faim et progresser lentement dans différents aspects du jeu, comme l'extraction de minerai, l'agriculture, la construction de machines et ainsi de suite. Ou alors vous pouvez jouer en "mode créatif" où vous pouvez construire à peu près n'importe quoi instantanément.
|
|
||||||
|
|
||||||
### Résumé du Gameplay
|
|
||||||
|
|
||||||
* Jeu de type bac-à-sable, sans objetifs
|
|
||||||
* Survie : combattre des monstres hostiles et la faim
|
|
||||||
* Creuser pour du minerai et d'autres trésors
|
|
||||||
* Magie : gagner de l'expérience et enchanter les outils
|
|
||||||
* Utiliser les blocs ramassés pour construire de magnifiques bâtiments, votre imagination est la limite
|
|
||||||
* Ramasser des fleurs (et d'autres sources de teinture) et colorez votre monde
|
|
||||||
* Trouvez des graines et commencez à cultiver
|
|
||||||
* Trouvez ou fabriquez des centaines d'objets
|
|
||||||
* Construisez un réseau ferroviaire complexe et amusez vous avec les wagonnets
|
|
||||||
* En mode créatif vous pouvez construire presque n'importe quoi gratuitement et sans limite
|
|
||||||
|
|
||||||
## Comment jouer (démarrer rapidement)
|
|
||||||
### Commencer
|
|
||||||
* **Frappez un arbre** jusqu'à ce qu'il casse et donne du bois
|
|
||||||
* Placez le **bois dans la grille 2x2** (la "grille de fabrication" de votre menu d'inventaire) et fabriquez 4 planches de bois
|
|
||||||
* Placer les 4 planches de bois dans la grille 2x2 et **fabriquez une table d'artisanat**
|
|
||||||
* **Cliquez droit la table d'artisanat** (icone livre) pour apprendre toutes les recettes possibles
|
|
||||||
* **Fabriquez une pioche de bois** pour miner la pierre
|
|
||||||
* Différents outils minent différentes sortes de blocs. Essayez les !
|
|
||||||
* Continuez à jouer comme vous voulez. Amusez vous !
|
|
||||||
|
|
||||||
### Agriculture
|
|
||||||
* Trouvez des graines
|
|
||||||
* Fabriquez une houe
|
|
||||||
* Cliquez droit la terre ou des blocs similaires avec la houe pour créer des terres agricoles
|
|
||||||
* Placer des graines sur des terres agricoles et regardez les pousser
|
|
||||||
* Récoltez les plantes une fois matûres
|
|
||||||
* Les terres agricoles proche de l'eau deviennent humides et accélèrent la croissance
|
|
||||||
|
|
||||||
### Four
|
|
||||||
* Fabriquer un Four
|
|
||||||
* Le four permet d'obtenir plus d'objets
|
|
||||||
* L'emplacement du haut doit contienir un objet fondable (par ex : minerai de fer)
|
|
||||||
* L'emplacement du bas doit contienir un objet combustible (par ex : charbon)
|
|
||||||
* Voir le guide d'artisanat pour en apprendre plus sur les objets fondables et combustibles
|
|
||||||
|
|
||||||
### Aide supplémentaire
|
|
||||||
Plus d'aide à propos du jeu, des blocs, objets et plus encore peuvent être trouvés dans le jeu. Vous pouvez accéder à l'aide depuis le menu inventaire.
|
|
||||||
|
|
||||||
### Objets spéciaux
|
|
||||||
Les objets suivants sont intéressants pour le mode Créatif et pour les constructeurs de cartes d'aventure. Ils ne peuvent être obtenus dans le jeu ou dans l'inventaire créatif.
|
|
||||||
|
|
||||||
* Barrière : `mcl_core:barrier`
|
|
||||||
|
|
||||||
Utilisez la commande de chat `/giveme` pour les obtenir. Voir l'aide interne au jeu pour une explication.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
Ce jeu nécessite [Minetest](http://minetest.net) pour fonctionner (version 5.4.1 ou plus). Vous devez donc installer Minetest d'abord. Seules les versions stables de Minetest sont officielement supportées.
|
|
||||||
Il n'y a pas de support de MineClone2 dans les versions développement de Minetest.
|
|
||||||
|
|
||||||
Pour installer MineClone2 (si ce n'est pas déjà fait), déplacez ce dossier dans le dossier “games” de Minetest. Consultez l'aide de Minetest pour en apprendre plus.
|
|
||||||
|
|
||||||
## Liens utiles
|
|
||||||
Le dépôt de MineClone2 est hébergé sur Mesehub. Pour contribuer ou rapporter des problèmes, aller là-bas.
|
|
||||||
|
|
||||||
* Mesehub: <https://git.minetest.land/MineClone2/MineClone2>
|
|
||||||
* Discord: <https://discord.gg/xE4z8EEpDC>
|
|
||||||
* YouTube <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
|
|
||||||
* IRC: <https://web.libera.chat/#mineclone2>
|
|
||||||
* Matrix: <https://app.element.io/#/room/#mc2:matrix.org>
|
|
||||||
* Reddit: <https://www.reddit.com/r/MineClone2/>
|
|
||||||
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
|
||||||
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
|
||||||
* OpenCollective: <https://opencollective.com/mineclone2>
|
|
||||||
|
|
||||||
## Objectif
|
|
||||||
* Créer un clone stable, moddable, libre et gratuit basé sur le moteur de jeu Minetest avec des fonctionalités abouties, utilisable à la fois en mode solo et multijoueur. Actuellement, beaucoup des fonctionalités de **Minecraft Java Edition** sont déjà implémentées et leur amélioration est prioritaire sur les nouvelles demandes.
|
|
||||||
* Avec une priorité moindre, implémenter les fonctionalités des versions **Minecraft + OptiFine** (OtiFine autant que supporté par le moteur Minetest). Cela signifie que les fonctionalités présentes dans les versions listées sont priorisées.
|
|
||||||
* Dans l'idéal, créer une expérience performante qui tourne bien sur des ordinateurs à basse performance. Malheureusement, en raison des mécanismes de Minecraft et des limitations du moteur Minetest ainsi que de la petite taille de la communauté de joueurs sur des ordinateurs à basse performances, les optimisations sont difficiles à explorer.
|
|
||||||
|
|
||||||
## Statut de complétion
|
|
||||||
Ce jeu est actuellement au stade **beta**.
|
|
||||||
Il est jouable mais incomplet en fonctionalités.
|
|
||||||
La rétro-compatibilité n'est pas entièrement garantie, mettre votre monde à jour peut causer de petits bugs.
|
|
||||||
Si vous voulez utiliser la version de développement de MineClone2 en production, la branche master est habituellement relativement stable. Les branches de test fusionnent souvent des pull requests expérimentales et doivent être considérées comme moins stable.
|
|
||||||
|
|
||||||
Les principales fonctionalités suivantes sont disponibles :
|
|
||||||
|
|
||||||
* Outils, armes
|
|
||||||
* Armure
|
|
||||||
* Système de fabrication : grille 2x2, table d'artisanat (grille 3x3), four, incluant un guide de fabrication
|
|
||||||
* Coffres, grands coffres, coffre ender, boite de shulker
|
|
||||||
* Fours, entonnoirs
|
|
||||||
* Faim
|
|
||||||
* La plupart des monstres et animaux
|
|
||||||
* Tout les minerais de Minecraft
|
|
||||||
* La plupart des blocs de l'overworld
|
|
||||||
* Eau et lave
|
|
||||||
* Météo
|
|
||||||
* 28 biomes + 5 biomes du nether
|
|
||||||
* Le Nether, monde souterrain brûlant dans une autre dimension
|
|
||||||
* Circuits Redstone (partiel)
|
|
||||||
* Effets de Statut (partiel)
|
|
||||||
* Expérience
|
|
||||||
* Enchantement
|
|
||||||
* Brassage, potions, flèches trempées (partiel)
|
|
||||||
* Bâteaux
|
|
||||||
* Feu
|
|
||||||
* Blocs de construction : escaliers, dalles, portes, trappes, barrière, portillon, muret
|
|
||||||
* Horloge
|
|
||||||
* Boussole
|
|
||||||
* Eponge
|
|
||||||
* Bloc de slime
|
|
||||||
* Petites plantes et pousses
|
|
||||||
* Teintures
|
|
||||||
* Bannières
|
|
||||||
* Blocs de décoration : verre, verre teinté, vitres, barres de fer, terre cuites (et couleurs), têtes et plus
|
|
||||||
* Cadres d'objets
|
|
||||||
* Juke-boxes
|
|
||||||
* Livres pour écrire
|
|
||||||
* Commandes
|
|
||||||
* Villages
|
|
||||||
* L'End
|
|
||||||
* et plus !
|
|
||||||
|
|
||||||
Les fonctionalités suivantes sont incomplètes :
|
|
||||||
|
|
||||||
* certains monstres et animaux
|
|
||||||
* certains composants de Redstone
|
|
||||||
* Wagonnets spéciaux
|
|
||||||
* quelques blocs et objets non-triviaux
|
|
||||||
|
|
||||||
Fonctionalités bonus (absentes de Minecraft) :
|
|
||||||
|
|
||||||
* Guide d'artisanat intégré au jeu qui montre les recettes d'artisanat et de cuisson
|
|
||||||
* Système d'aide intégré au jeu contenant des informations à propos des techniques de base, blocs, objets et plus
|
|
||||||
* Recettes d'artisanat temporaires. Elles existent uniquement pour rendre des objets accessibles qui ne le seraient pas autrement sauf en mode créatif. Elles seront retirées au cours de l'avancement du développement et de l'ajout de nouvelles fonctionalités.
|
|
||||||
* Pousses dans les coffres en mapgen v6
|
|
||||||
* Entièrement moddable (grâce la puissante API lua de Minetest)
|
|
||||||
* Nouveaux blocs et objets :
|
|
||||||
* Outil de recherche, montre l'aide de ce qu'il touche
|
|
||||||
* Plus de dalles et d'escaliers
|
|
||||||
* Portillon en briques du Nether
|
|
||||||
* Barrière en briques du Nether rouges
|
|
||||||
* Portillon en briques du Nether rouges
|
|
||||||
* Structures de remplacement - ces petites variantes de structures de Minecraft servent de remplacement en attendant qu'on arrive à en faire fonctionner de plus grandes :
|
|
||||||
* Cabine dans les bois (Manoir des bois)
|
|
||||||
* Avant-poste du Nether (Forteresse)
|
|
||||||
|
|
||||||
Différences techniques avec Minecraft :
|
|
||||||
* Limite en hauteur de 31000 blocs (bien plus grand que Minecraft)
|
|
||||||
* Taille horizontale du monde 62000×62000 blocs (bien plus petit que Minecraft mais toujours très grand)
|
|
||||||
* Toujours assez incomplet et buggé
|
|
||||||
* Des blocs, objets, ennemis et fonctionalités manquent
|
|
||||||
* Quelques objets ont des noms légèrement différents pour être plus faciles à distinguer
|
|
||||||
* Des musiques différentes pour le juke-boxe
|
|
||||||
* Des textures différentes (Pixel Perfection)
|
|
||||||
* Des sons différents (sources diverses)
|
|
||||||
* Un moteur de jeu différent (Minetest)
|
|
||||||
* Des bonus cachés différents
|
|
||||||
...et enfin MineClone2 est un logiciel libre !
|
|
||||||
|
|
||||||
## Autres fichiers readme
|
|
||||||
|
|
||||||
* `LICENSE.txt`: Le texte de la license GPLv3
|
|
||||||
* `CONTRIBUTING.md`: Information pour ceux qui veulent contribuer
|
|
||||||
* `API.md`: Pour les modders Minetest qui veulent modder ce jeu
|
|
||||||
* `LEGAL.md`: Information légale
|
|
||||||
* `CREDITS.md`: Liste des contributeurs
|
|
19
RELEASE.md
|
@ -1,19 +0,0 @@
|
||||||
#File to document release steps with a view to evolving into a script
|
|
||||||
|
|
||||||
#Update CREDITS.md
|
|
||||||
#Update version in README.md (soon to be game.conf from of 0.82.0)
|
|
||||||
|
|
||||||
lua tools/generate_ingame_credits.lua
|
|
||||||
|
|
||||||
git add CREDITS.md
|
|
||||||
git add mods/HUD/mcl_credits/people.lua
|
|
||||||
|
|
||||||
git add README.md
|
|
||||||
# To uncomment when applicable
|
|
||||||
#git add game.conf
|
|
||||||
|
|
||||||
git commit -m "Pre-release update credits and set version 0.81.1"
|
|
||||||
|
|
||||||
git tag 0.81.1
|
|
||||||
|
|
||||||
git push origin 0.81.1
|
|
|
@ -1,4 +1,2 @@
|
||||||
title = MineClone 2
|
title = MineClone 2
|
||||||
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
||||||
disallowed_mapgens = v6
|
|
||||||
version=MCL2-0.82-indev
|
|
|
@ -5,8 +5,6 @@
|
||||||
-- Nodes in group "supported_node" can be placed on any node that does not
|
-- Nodes in group "supported_node" can be placed on any node that does not
|
||||||
-- have the "airlike" drawtype. Carpets are an example of this type.
|
-- have the "airlike" drawtype. Carpets are an example of this type.
|
||||||
|
|
||||||
local pairs = pairs
|
|
||||||
local math = math
|
|
||||||
local vector = vector
|
local vector = vector
|
||||||
|
|
||||||
local facedir_to_dir = minetest.facedir_to_dir
|
local facedir_to_dir = minetest.facedir_to_dir
|
||||||
|
@ -24,17 +22,15 @@ local add_item = minetest.add_item
|
||||||
-- We need this to do the exact same dropping node handling in our override
|
-- We need this to do the exact same dropping node handling in our override
|
||||||
-- minetest.check_single_for_falling() function as in the builtin function.
|
-- minetest.check_single_for_falling() function as in the builtin function.
|
||||||
--
|
--
|
||||||
---@param p Vector
|
|
||||||
local function drop_attached_node(p)
|
local function drop_attached_node(p)
|
||||||
local n = get_node(p)
|
local n = get_node(p)
|
||||||
local drops = get_node_drops(n, "")
|
local drops = get_node_drops(n, "")
|
||||||
local def = registered_nodes[n.name]
|
local def = registered_nodes[n.name]
|
||||||
|
|
||||||
if def and def.preserve_metadata then
|
if def and def.preserve_metadata then
|
||||||
local oldmeta = get_meta(p):to_table().fields
|
local oldmeta = get_meta(p):to_table().fields
|
||||||
-- Copy pos and node because the callback can modify them.
|
-- Copy pos and node because the callback can modify them.
|
||||||
local pos_copy = vector.copy(p)
|
local pos_copy = vector.new(p)
|
||||||
local node_copy = { name = n.name, param1 = n.param1, param2 = n.param2 }
|
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
|
||||||
local drop_stacks = {}
|
local drop_stacks = {}
|
||||||
for k, v in pairs(drops) do
|
for k, v in pairs(drops) do
|
||||||
drop_stacks[k] = ItemStack(v)
|
drop_stacks[k] = ItemStack(v)
|
||||||
|
@ -42,18 +38,16 @@ local function drop_attached_node(p)
|
||||||
drops = drop_stacks
|
drops = drop_stacks
|
||||||
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
|
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
|
||||||
end
|
end
|
||||||
|
|
||||||
if def and def.sounds and def.sounds.fall then
|
if def and def.sounds and def.sounds.fall then
|
||||||
minetest.sound_play(def.sounds.fall, { pos = p }, true)
|
core.sound_play(def.sounds.fall, {pos = p}, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
remove_node(p)
|
remove_node(p)
|
||||||
for _, item in pairs(drops) do
|
for _, item in pairs(drops) do
|
||||||
local pos = vector.offset(p,
|
local pos = {
|
||||||
math.random() / 2 - 0.25,
|
x = p.x + math.random()/2 - 0.25,
|
||||||
math.random() / 2 - 0.25,
|
y = p.y + math.random()/2 - 0.25,
|
||||||
math.random() / 2 - 0.25
|
z = p.z + math.random()/2 - 0.25,
|
||||||
)
|
}
|
||||||
add_item(pos, item)
|
add_item(pos, item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -96,3 +90,4 @@ function minetest.check_single_for_falling(pos)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ mcl_damage = {
|
||||||
drown = {bypasses_armor = true},
|
drown = {bypasses_armor = true},
|
||||||
starve = {bypasses_armor = true, bypasses_magic = true},
|
starve = {bypasses_armor = true, bypasses_magic = true},
|
||||||
cactus = {},
|
cactus = {},
|
||||||
sweet_berry = {},
|
|
||||||
fall = {bypasses_armor = true},
|
fall = {bypasses_armor = true},
|
||||||
fly_into_wall = {bypasses_armor = true}, -- unused
|
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
||||||
|
@ -34,8 +33,6 @@ mcl_damage = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local damage_enabled = minetest.settings:get_bool("enabled_damage",true)
|
|
||||||
|
|
||||||
function mcl_damage.register_modifier(func, priority)
|
function mcl_damage.register_modifier(func, priority)
|
||||||
table.insert(mcl_damage.modifiers, {func = func, priority = priority or 0})
|
table.insert(mcl_damage.modifiers, {func = func, priority = priority or 0})
|
||||||
end
|
end
|
||||||
|
@ -142,7 +139,6 @@ function mcl_damage.register_type(name, def)
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
if not damage_enabled then return 0 end
|
|
||||||
if hp_change < 0 then
|
if hp_change < 0 then
|
||||||
if player:get_hp() <= 0 then
|
if player:get_hp() <= 0 then
|
||||||
return 0
|
return 0
|
||||||
|
@ -153,7 +149,6 @@ minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
end, true)
|
end, true)
|
||||||
|
|
||||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
if not damage_enabled then return 0 end
|
|
||||||
if player:get_hp() > 0 then
|
if player:get_hp() > 0 then
|
||||||
mt_reason.approved = true
|
mt_reason.approved = true
|
||||||
if hp_change < 0 then
|
if hp_change < 0 then
|
||||||
|
@ -166,9 +161,9 @@ minetest.register_on_dieplayer(function(player, mt_reason)
|
||||||
if mt_reason.approved then
|
if mt_reason.approved then
|
||||||
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
||||||
end
|
end
|
||||||
minetest.log("action","Player "..player:get_player_name().." died at "..minetest.pos_to_string(vector.round(player:get_pos())))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_mods_loaded(function()
|
minetest.register_on_mods_loaded(function()
|
||||||
table.sort(mcl_damage.modifiers, function(a, b) return a.priority < b.priority end)
|
table.sort(mcl_damage.modifiers, function(a, b) return a.priority < b.priority end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
## mcl_events
|
|
||||||
### Registering Events
|
|
||||||
`mlc_events.register_event("name",def)`
|
|
||||||
|
|
||||||
#### Event Definition
|
|
||||||
{
|
|
||||||
stage = 0,
|
|
||||||
max_stage = 1,
|
|
||||||
percent = 100,
|
|
||||||
bars = {},
|
|
||||||
completed = false,
|
|
||||||
cond_start = function() end,
|
|
||||||
--return table of paramtables e.g. { { player = playername, pos = position, ... } }, custom parameters will be passed to the event object/table
|
|
||||||
on_step = function(event) end,
|
|
||||||
--this function is run every game step when the event is active
|
|
||||||
on_start = function(event) end,
|
|
||||||
-- this function is run when the event starts
|
|
||||||
on_stage_begin = function(event) end,
|
|
||||||
-- this function runs when a new stage of the event starts
|
|
||||||
cond_progress = function(event) end, --return false or next stage id
|
|
||||||
--this function checks if the event should progress to the next (or any other) stage
|
|
||||||
cond_complete = function(event) end,
|
|
||||||
--return true if event finished successfully
|
|
||||||
}
|
|
||||||
|
|
||||||
### Debugging
|
|
||||||
* /event_start <event> -- starts the given event at the current player coordinates
|
|
|
@ -1,155 +0,0 @@
|
||||||
mcl_events = {}
|
|
||||||
mcl_events.registered_events = {}
|
|
||||||
local disabled_events = minetest.settings:get("mcl_disabled_events")
|
|
||||||
if disabled_events then disabled_events = disabled_events:split(",")
|
|
||||||
else disabled_events = {} end
|
|
||||||
local DBG = minetest.settings:get_bool("mcl_logging_event_api",false)
|
|
||||||
local active_events = {}
|
|
||||||
|
|
||||||
local event_tpl = {
|
|
||||||
stage = 0,
|
|
||||||
max_stage = 1,
|
|
||||||
percent = 100,
|
|
||||||
bars = {},
|
|
||||||
completed = false,
|
|
||||||
cond_start = function(event) end, --return table of positions
|
|
||||||
on_step = function(event) end,
|
|
||||||
on_start = function(event) end,
|
|
||||||
on_stage_begin = function(event) end,
|
|
||||||
cond_progress = function(event) end, --return next stage
|
|
||||||
cond_complete = function(event) end, --return success
|
|
||||||
}
|
|
||||||
|
|
||||||
local function mcl_log(m,l)
|
|
||||||
if DBG then
|
|
||||||
if not l then l = "action" end
|
|
||||||
minetest.log(l,"[mcl_events] "..m)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_events.register_event(name,def)
|
|
||||||
if table.indexof(disabled_events,name) ~= -1 then return end
|
|
||||||
mcl_events.registered_events[name] = def
|
|
||||||
mcl_events.registered_events[name].name = name
|
|
||||||
end
|
|
||||||
|
|
||||||
local function addbars(self)
|
|
||||||
if not self.enable_bossbar then return end
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
if vector.distance(self.pos,player:get_pos()) < 64 then
|
|
||||||
local bar = mcl_bossbars.add_bar(player, {color = "red", text = self.readable_name .. ": Wave "..self.stage.." / "..self.max_stage, percentage = self.percent }, true,1)
|
|
||||||
table.insert(self.bars,bar)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function start_event(p,e)
|
|
||||||
mcl_log("[mcl_events] Event started: "..e.readable_name.." at "..minetest.pos_to_string(vector.round(p.pos)))
|
|
||||||
local idx = #active_events + 1
|
|
||||||
active_events[idx] = table.copy(e)
|
|
||||||
setmetatable(active_events[idx],{__index = event_tpl})
|
|
||||||
for k,v in pairs(p) do active_events[idx][k] = v end
|
|
||||||
active_events[idx].stage = 0
|
|
||||||
active_events[idx].percent = 100
|
|
||||||
active_events[idx].bars = {}
|
|
||||||
active_events[idx].time_start = os.time()
|
|
||||||
if active_events[idx].on_start then
|
|
||||||
active_events[idx]:on_start(p.pos)
|
|
||||||
end
|
|
||||||
addbars(active_events[idx])
|
|
||||||
end
|
|
||||||
|
|
||||||
local function finish_event(self,idx)
|
|
||||||
mcl_log("[mcl_events] Finished: "..self.readable_name.." at "..minetest.pos_to_string(vector.round(self.pos)))
|
|
||||||
if self.on_complete then self:on_complete() end
|
|
||||||
for _,b in pairs(self.bars) do
|
|
||||||
mcl_bossbars.remove_bar(b)
|
|
||||||
end
|
|
||||||
table.remove(active_events,idx)
|
|
||||||
end
|
|
||||||
|
|
||||||
local etime = 0
|
|
||||||
function check_events(dtime)
|
|
||||||
--process active events
|
|
||||||
for idx,ae in pairs(active_events) do
|
|
||||||
if ae.cond_complete and ae:cond_complete() then
|
|
||||||
ae.finished = true
|
|
||||||
finish_event(ae,idx)
|
|
||||||
elseif not ae.cond_complete and ae.max_stage and ae.max_stage <= ae.stage then
|
|
||||||
ae.finished = true
|
|
||||||
finish_event(ae,idx)
|
|
||||||
elseif not ae.finished and ae.cond_progress then
|
|
||||||
local p = ae:cond_progress()
|
|
||||||
if p == true then
|
|
||||||
ae.stage = ae.stage + 1
|
|
||||||
if ae:on_stage_begin() == true then
|
|
||||||
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." failed at stage_begin of stage "..ae.stage )
|
|
||||||
active_events[idx] = nil
|
|
||||||
end
|
|
||||||
elseif tonumber(p) then
|
|
||||||
ae.stage = tonumber(p) or ae.stage + 1
|
|
||||||
ae:on_stage_begin()
|
|
||||||
end
|
|
||||||
elseif not ae.finished and ae.on_step then
|
|
||||||
ae:on_step(dtime)
|
|
||||||
end
|
|
||||||
addbars(ae)
|
|
||||||
end
|
|
||||||
-- check if a new event should be started
|
|
||||||
etime = etime - dtime
|
|
||||||
if etime > 0 then return end
|
|
||||||
etime = 10
|
|
||||||
for _,e in pairs(mcl_events.registered_events) do
|
|
||||||
local pp = e.cond_start()
|
|
||||||
if pp then
|
|
||||||
--minetest.log("It's gonna start the raid maybe")
|
|
||||||
for _,p in pairs(pp) do
|
|
||||||
local start = true
|
|
||||||
if e.exclusive_to_area then
|
|
||||||
for _,ae in pairs(active_events) do
|
|
||||||
if e.name == ae.name and vector.distance(p.pos,ae.pos) < e.exclusive_to_area then start = false end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if start then
|
|
||||||
--minetest.log("It's gonna start the raid definitely")
|
|
||||||
start_event(p,e)
|
|
||||||
elseif DBG then
|
|
||||||
mcl_log("[mcl_events] Event "..e.readable_name.." already active at "..minetest.pos_to_string(vector.round(p.pos)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--minetest.log("Do not start this raid")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for idx,ae in pairs(active_events) do
|
|
||||||
local player_near = false
|
|
||||||
for _,pl in pairs(minetest.get_connected_players()) do
|
|
||||||
if ae.pos and vector.distance(pl:get_pos(),ae.pos) < 64 then player_near = true end
|
|
||||||
end
|
|
||||||
if ae.pos and not player_near then
|
|
||||||
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." aborted - no players near." )
|
|
||||||
active_events[idx] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_globalstep(check_events)
|
|
||||||
|
|
||||||
mcl_info.register_debug_field("Active Events",{
|
|
||||||
level = 4,
|
|
||||||
func = function(pl,pos)
|
|
||||||
return tostring(#active_events)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_chatcommand("event_start",{
|
|
||||||
privs = {debug = true},
|
|
||||||
description = "Debug command to start events",
|
|
||||||
func = function(pname,param)
|
|
||||||
local p = minetest.get_player_by_name(pname)
|
|
||||||
local evdef = mcl_events.registered_events[param]
|
|
||||||
if not evdef then return false,"Event "..param.." doesn't exist.'" end
|
|
||||||
start_event({pos=p:get_pos(),player=pname,factor=1},evdef)
|
|
||||||
return true,"Started event "..param
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = mcl_events
|
|
||||||
author = cora
|
|
||||||
depends = mcl_mobs,mcl_bossbars, mcl_info
|
|
|
@ -130,10 +130,10 @@ local function add_particles(pos, radius)
|
||||||
time = 0.125,
|
time = 0.125,
|
||||||
minpos = pos,
|
minpos = pos,
|
||||||
maxpos = pos,
|
maxpos = pos,
|
||||||
minvel = vector.new(-radius, -radius, -radius),
|
minvel = {x = -radius, y = -radius, z = -radius},
|
||||||
maxvel = vector.new(radius, radius, radius),
|
maxvel = {x = radius, y = radius, z = radius},
|
||||||
minacc = vector.zero(),
|
minacc = vector.new(),
|
||||||
maxacc = vector.zero(),
|
maxacc = vector.new(),
|
||||||
minexptime = 0.5,
|
minexptime = 0.5,
|
||||||
maxexptime = 1.0,
|
maxexptime = 1.0,
|
||||||
minsize = radius * 0.5,
|
minsize = radius * 0.5,
|
||||||
|
@ -207,7 +207,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
local npos_z = math.floor(rpos_z + 0.5)
|
local npos_z = math.floor(rpos_z + 0.5)
|
||||||
local npos = { x = npos_x, y = npos_y, z = npos_z }
|
local npos = { x = npos_x, y = npos_y, z = npos_z }
|
||||||
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
|
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
|
||||||
npos_x - emin_x + 1
|
npos_x - emin_x + 1
|
||||||
|
|
||||||
local cid = data[idx]
|
local cid = data[idx]
|
||||||
local br = node_blastres[cid] or INDESTRUCT_BLASTRES
|
local br = node_blastres[cid] or INDESTRUCT_BLASTRES
|
||||||
|
@ -288,7 +288,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
rdir_y = rdir_y / rdir_len
|
rdir_y = rdir_y / rdir_len
|
||||||
rdir_z = rdir_z / rdir_len
|
rdir_z = rdir_z / rdir_len
|
||||||
|
|
||||||
for i = 0, rdir_len / STEP_LENGTH do
|
for i=0, rdir_len / STEP_LENGTH do
|
||||||
rpos_x = rpos_x + rdir_x * STEP_LENGTH
|
rpos_x = rpos_x + rdir_x * STEP_LENGTH
|
||||||
rpos_y = rpos_y + rdir_y * STEP_LENGTH
|
rpos_y = rpos_y + rdir_y * STEP_LENGTH
|
||||||
rpos_z = rpos_z + rdir_z * STEP_LENGTH
|
rpos_z = rpos_z + rdir_z * STEP_LENGTH
|
||||||
|
@ -296,7 +296,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
local npos_y = math.floor(rpos_y + 0.5)
|
local npos_y = math.floor(rpos_y + 0.5)
|
||||||
local npos_z = math.floor(rpos_z + 0.5)
|
local npos_z = math.floor(rpos_z + 0.5)
|
||||||
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
|
local idx = (npos_z - emin_z) * zstride + (npos_y - emin_y) * ystride +
|
||||||
npos_x - emin_x + 1
|
npos_x - emin_x + 1
|
||||||
|
|
||||||
|
|
||||||
local cid = data[idx]
|
local cid = data[idx]
|
||||||
|
@ -333,17 +333,17 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
end
|
end
|
||||||
|
|
||||||
if sleep_formspec_doesnt_close_mt53 then
|
if sleep_formspec_doesnt_close_mt53 then
|
||||||
minetest.after(0.3,
|
minetest.after(0.3, function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
||||||
function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
if not obj:is_player() then
|
||||||
if not obj:is_player() then
|
return
|
||||||
return
|
end
|
||||||
end
|
|
||||||
mcl_util.deal_damage(obj, damage, { type = "explosion", direct = direct, source = source })
|
|
||||||
|
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||||
end)
|
|
||||||
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
mcl_util.deal_damage(obj, damage, { type = "explosion", direct = direct, source = source })
|
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||||
|
|
||||||
if obj:is_player() or ent.tnt_knockback then
|
if obj:is_player() or ent.tnt_knockback then
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
|
@ -389,24 +389,23 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
-- We use bulk_set_node instead of LVM because we want to have on_destruct and
|
-- We use bulk_set_node instead of LVM because we want to have on_destruct and
|
||||||
-- on_construct being called
|
-- on_construct being called
|
||||||
if #airs > 0 then
|
if #airs > 0 then
|
||||||
bulk_set_node(airs, { name = "air" })
|
bulk_set_node(airs, {name="air"})
|
||||||
end
|
end
|
||||||
if #fires > 0 then
|
if #fires > 0 then
|
||||||
bulk_set_node(fires, { name = "mcl_fire:fire" })
|
bulk_set_node(fires, {name="mcl_fire:fire"})
|
||||||
end
|
end
|
||||||
-- Update falling nodes
|
-- Update falling nodes
|
||||||
for a = 1, #airs do
|
for a=1, #airs do
|
||||||
local p = airs[a]
|
local p = airs[a]
|
||||||
check_for_falling(vector.offset(p, 0, 1, 0))
|
check_for_falling({x=p.x, y=p.y+1, z=p.z})
|
||||||
end
|
end
|
||||||
for f = 1, #fires do
|
for f=1, #fires do
|
||||||
local p = fires[f]
|
local p = fires[f]
|
||||||
check_for_falling(vector.offset(p, 0, 1, 0))
|
check_for_falling({x=p.x, y=p.y+1, z=p.z})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Log explosion
|
-- Log explosion
|
||||||
minetest.log("action", "Explosion at " .. pos_to_string(pos) .. " with strength " .. strength .. " and radius " ..
|
minetest.log("action", "Explosion at "..pos_to_string(pos).." with strength "..strength.." and radius "..radius)
|
||||||
radius)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create an explosion with strength at pos.
|
-- Create an explosion with strength at pos.
|
||||||
|
@ -430,11 +429,6 @@ end
|
||||||
-- griefing - If true, the explosion will destroy nodes (default: true)
|
-- griefing - If true, the explosion will destroy nodes (default: true)
|
||||||
-- grief_protected - If true, the explosion will also destroy nodes which have
|
-- grief_protected - If true, the explosion will also destroy nodes which have
|
||||||
-- been protected (default: false)
|
-- been protected (default: false)
|
||||||
---@param pos Vector
|
|
||||||
---@param strength number
|
|
||||||
---@param info {drop_chance: number, max_blast_resistance: number, sound: boolean, particles: boolean, fire: boolean, griefing: boolean, grief_protected: boolean}
|
|
||||||
---@param direct? ObjectRef
|
|
||||||
---@param source? ObjectRef
|
|
||||||
function mcl_explosions.explode(pos, strength, info, direct, source)
|
function mcl_explosions.explode(pos, strength, info, direct, source)
|
||||||
if info == nil then
|
if info == nil then
|
||||||
info = {}
|
info = {}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1は爆発に巻き込まれた。
|
|
|
@ -138,7 +138,6 @@ mcl_vars.mg_nether_min = -29067 -- Carefully chosen to be at a mapchunk border
|
||||||
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
|
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
|
||||||
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
|
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
|
||||||
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
|
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
|
||||||
mcl_vars.mg_nether_deco_max = mcl_vars.mg_nether_max -11 -- this is so ceiling decorations don't spill into other biomes as bedrock generation calls minetest.generate_decorations to put netherrack under the bedrock
|
|
||||||
if not superflat then
|
if not superflat then
|
||||||
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
|
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
|
||||||
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
|
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
|
||||||
|
@ -163,8 +162,7 @@ end
|
||||||
mcl_vars.mg_end_min = -27073 -- Carefully chosen to be at a mapchunk border
|
mcl_vars.mg_end_min = -27073 -- Carefully chosen to be at a mapchunk border
|
||||||
mcl_vars.mg_end_max_official = mcl_vars.mg_end_min + minecraft_height_limit
|
mcl_vars.mg_end_max_official = mcl_vars.mg_end_min + minecraft_height_limit
|
||||||
mcl_vars.mg_end_max = mcl_vars.mg_overworld_min - 2000
|
mcl_vars.mg_end_max = mcl_vars.mg_overworld_min - 2000
|
||||||
mcl_vars.mg_end_platform_pos = { x = 100, y = mcl_vars.mg_end_min + 64, z = 0 }
|
mcl_vars.mg_end_platform_pos = { x = 100, y = mcl_vars.mg_end_min + 74, z = 0 }
|
||||||
mcl_vars.mg_end_exit_portal_pos = vector.new(0, mcl_vars.mg_end_min + 71, 0)
|
|
||||||
|
|
||||||
-- Realm barrier used to safely separate the End from the void below the Overworld
|
-- Realm barrier used to safely separate the End from the void below the Overworld
|
||||||
mcl_vars.mg_realm_barrier_overworld_end_max = mcl_vars.mg_end_max
|
mcl_vars.mg_realm_barrier_overworld_end_max = mcl_vars.mg_end_max
|
||||||
|
|
Before Width: | Height: | Size: 144 B |
|
@ -22,29 +22,6 @@ function table.update_nil(t, ...)
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default",false)
|
|
||||||
local LOG_MODULE = "[MCL2]"
|
|
||||||
function mcl_util.mcl_log (message, module, bypass_default_logger)
|
|
||||||
local selected_module = LOG_MODULE
|
|
||||||
if module then
|
|
||||||
selected_module = module
|
|
||||||
end
|
|
||||||
if (bypass_default_logger or LOGGING_ON) and message then
|
|
||||||
minetest.log(selected_module .. " " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function mcl_util.file_exists(name)
|
|
||||||
if type(name) ~= "string" then return end
|
|
||||||
local f = io.open(name)
|
|
||||||
if not f then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Based on minetest.rotate_and_place
|
-- Based on minetest.rotate_and_place
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
@ -613,12 +590,10 @@ function mcl_util.get_object_name(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_util.replace_mob(obj, mob)
|
function mcl_util.replace_mob(obj, mob)
|
||||||
if not obj then return end
|
|
||||||
local rot = obj:get_yaw()
|
local rot = obj:get_yaw()
|
||||||
local pos = obj:get_pos()
|
local pos = obj:get_pos()
|
||||||
obj:remove()
|
obj:remove()
|
||||||
obj = minetest.add_entity(pos, mob)
|
obj = minetest.add_entity(pos, mob)
|
||||||
if not obj then return end
|
|
||||||
obj:set_yaw(rot)
|
obj:set_yaw(rot)
|
||||||
return obj
|
return obj
|
||||||
end
|
end
|
||||||
|
@ -635,96 +610,3 @@ function mcl_util.get_pointed_thing(player, liquid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This following part is 2 wrapper functions + helpers for
|
|
||||||
-- object:set_bones
|
|
||||||
-- and player:set_properties preventing them from being resent on
|
|
||||||
-- every globalstep when they have not changed.
|
|
||||||
|
|
||||||
local function roundN(n, d)
|
|
||||||
if type(n) ~= "number" then return n end
|
|
||||||
local m = 10^d
|
|
||||||
return math.floor(n * m + 0.5) / m
|
|
||||||
end
|
|
||||||
|
|
||||||
local function close_enough(a,b)
|
|
||||||
local rt=true
|
|
||||||
if type(a) == "table" and type(b) == "table" then
|
|
||||||
for k,v in pairs(a) do
|
|
||||||
if roundN(v,2) ~= roundN(b[k],2) then
|
|
||||||
rt=false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
rt = roundN(a,2) == roundN(b,2)
|
|
||||||
end
|
|
||||||
return rt
|
|
||||||
end
|
|
||||||
|
|
||||||
local function props_changed(props,oldprops)
|
|
||||||
local changed=false
|
|
||||||
local p={}
|
|
||||||
for k,v in pairs(props) do
|
|
||||||
if not close_enough(v,oldprops[k]) then
|
|
||||||
p[k]=v
|
|
||||||
changed=true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return changed,p
|
|
||||||
end
|
|
||||||
|
|
||||||
--tests for roundN
|
|
||||||
local test_round1=15
|
|
||||||
local test_round2=15.00199999999
|
|
||||||
local test_round3=15.00111111
|
|
||||||
local test_round4=15.00999999
|
|
||||||
|
|
||||||
assert(roundN(test_round1,2)==roundN(test_round1,2))
|
|
||||||
assert(roundN(test_round1,2)==roundN(test_round2,2))
|
|
||||||
assert(roundN(test_round1,2)==roundN(test_round3,2))
|
|
||||||
assert(roundN(test_round1,2)~=roundN(test_round4,2))
|
|
||||||
|
|
||||||
-- tests for close_enough
|
|
||||||
local test_cb = {-0.35,0,-0.35,0.35,0.8,0.35} --collisionboxes
|
|
||||||
local test_cb_close = {-0.351213,0,-0.35,0.35,0.8,0.351212}
|
|
||||||
local test_cb_diff = {-0.35,0,-1.35,0.35,0.8,0.35}
|
|
||||||
|
|
||||||
local test_eh = 1.65 --eye height
|
|
||||||
local test_eh_close = 1.65123123
|
|
||||||
local test_eh_diff = 1.35
|
|
||||||
|
|
||||||
local test_nt = { r = 225, b = 225, a = 225, g = 225 } --nametag
|
|
||||||
local test_nt_diff = { r = 225, b = 225, a = 0, g = 225 }
|
|
||||||
|
|
||||||
assert(close_enough(test_cb,test_cb_close))
|
|
||||||
assert(not close_enough(test_cb,test_cb_diff))
|
|
||||||
assert(close_enough(test_eh,test_eh_close))
|
|
||||||
assert(not close_enough(test_eh,test_eh_diff))
|
|
||||||
assert(not close_enough(test_nt,test_nt_diff)) --no floats involved here
|
|
||||||
|
|
||||||
--tests for properties_changed
|
|
||||||
local test_properties_set1={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
|
||||||
local test_properties_set2={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
|
||||||
|
|
||||||
local test_p1,_=props_changed(test_properties_set1,test_properties_set1)
|
|
||||||
local test_p2,_=props_changed(test_properties_set1,test_properties_set2)
|
|
||||||
|
|
||||||
assert(not test_p1)
|
|
||||||
assert(test_p2)
|
|
||||||
|
|
||||||
function mcl_util.set_properties(obj,props)
|
|
||||||
local changed,p=props_changed(props,obj:get_properties())
|
|
||||||
if changed then
|
|
||||||
obj:set_properties(p)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_util.set_bone_position(obj, bone, pos, rot)
|
|
||||||
local current_pos, current_rot = obj:get_bone_position(bone)
|
|
||||||
local pos_equal = not pos or vector.equals(vector.round(current_pos), vector.round(pos))
|
|
||||||
local rot_equal = not rot or vector.equals(vector.round(current_rot), vector.round(rot))
|
|
||||||
if not pos_equal or not rot_equal then
|
|
||||||
obj:set_bone_position(bone, pos or current_pos, rot or current_rot)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -28,10 +28,11 @@ end)
|
||||||
local timer = 0
|
local timer = 0
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
timer = timer + dtime
|
timer = timer + dtime
|
||||||
if timer >= 0.6 then
|
if timer >= 0.3 then
|
||||||
for _, player in pairs(get_connected_players()) do
|
for _, player in pairs(get_connected_players()) do
|
||||||
local ppos = player:get_pos()
|
local ppos = player:get_pos()
|
||||||
local npos = vector.add(ppos, vector.new(0, -0.1, 0))
|
ppos.y = ceil(ppos.y)
|
||||||
|
local npos = vector.add(ppos, vector.new(0, -1, 0))
|
||||||
if npos then
|
if npos then
|
||||||
local node = get_node(npos)
|
local node = get_node(npos)
|
||||||
if node then
|
if node then
|
||||||
|
|
|
@ -13,21 +13,11 @@ local function is_group(pos, group)
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_water = flowlib.is_water
|
local is_water = flowlib.is_water
|
||||||
local function is_river_water(p)
|
|
||||||
local n = minetest.get_node(p).name
|
|
||||||
if n == "mclx_core:river_water_source" or n == "mclx_core:river_water_flowing" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_ice(pos)
|
local function is_ice(pos)
|
||||||
return is_group(pos, "ice")
|
return is_group(pos, "ice")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_fire(pos)
|
|
||||||
return is_group(pos, "set_on_fire")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_sign(i)
|
local function get_sign(i)
|
||||||
if i == 0 then
|
if i == 0 then
|
||||||
return 0
|
return 0
|
||||||
|
@ -56,7 +46,7 @@ end
|
||||||
|
|
||||||
local function set_attach(boat)
|
local function set_attach(boat)
|
||||||
boat._driver:set_attach(boat.object, "",
|
boat._driver:set_attach(boat.object, "",
|
||||||
{x = 0, y = 1.5, z = 1}, {x = 0, y = 0, z = 0})
|
{x = 0, y = 0.42, z = -1}, {x = 0, y = 0, z = 0})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_double_attach(boat)
|
local function set_double_attach(boat)
|
||||||
|
@ -65,13 +55,9 @@ local function set_double_attach(boat)
|
||||||
boat._passenger:set_attach(boat.object, "",
|
boat._passenger:set_attach(boat.object, "",
|
||||||
{x = 0, y = 0.42, z = -2.2}, {x = 0, y = 0, z = 0})
|
{x = 0, y = 0.42, z = -2.2}, {x = 0, y = 0, z = 0})
|
||||||
end
|
end
|
||||||
local function set_choat_attach(boat)
|
|
||||||
boat._driver:set_attach(boat.object, "",
|
|
||||||
{x = 0, y = 1.5, z = 1}, {x = 0, y = 0, z = 0})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function attach_object(self, obj)
|
local function attach_object(self, obj)
|
||||||
if self._driver and not self._inv_id then
|
if self._driver then
|
||||||
if self._driver:is_player() then
|
if self._driver:is_player() then
|
||||||
self._passenger = obj
|
self._passenger = obj
|
||||||
else
|
else
|
||||||
|
@ -81,11 +67,7 @@ local function attach_object(self, obj)
|
||||||
set_double_attach(self)
|
set_double_attach(self)
|
||||||
else
|
else
|
||||||
self._driver = obj
|
self._driver = obj
|
||||||
if self._inv_id then
|
set_attach(self)
|
||||||
set_choat_attach(self)
|
|
||||||
else
|
|
||||||
set_attach(self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local visual_size = get_visual_size(obj)
|
local visual_size = get_visual_size(obj)
|
||||||
|
@ -109,7 +91,6 @@ local function attach_object(self, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function detach_object(obj, change_pos)
|
local function detach_object(obj, change_pos)
|
||||||
if not obj or not obj:get_pos() then return end
|
|
||||||
obj:set_detach()
|
obj:set_detach()
|
||||||
obj:set_properties({visual_size = get_visual_size(obj)})
|
obj:set_properties({visual_size = get_visual_size(obj)})
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
|
@ -132,11 +113,11 @@ local boat = {
|
||||||
pointable = true,
|
pointable = true,
|
||||||
-- Warning: Do not change the position of the collisionbox top surface,
|
-- Warning: Do not change the position of the collisionbox top surface,
|
||||||
-- lowering it causes the boat to fall through the world if underwater
|
-- lowering it causes the boat to fall through the world if underwater
|
||||||
collisionbox = {-0.5, -0.15, -0.5, 0.5, 0.55, 0.5},
|
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
||||||
selectionbox = {-0.7, -0.15, -0.7, 0.7, 0.55, 0.7},
|
selectionbox = {-0.7, -0.35, -0.7, 0.7, 0.3, 0.7},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mcl_boats_boat.b3d",
|
mesh = "mcl_boats_boat.b3d",
|
||||||
textures = { "mcl_boats_texture_oak_boat.png", "blank.png" },
|
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
|
||||||
visual_size = boat_visual_size,
|
visual_size = boat_visual_size,
|
||||||
hp_max = boat_max_hp,
|
hp_max = boat_max_hp,
|
||||||
damage_texture_modifier = "^[colorize:white:0",
|
damage_texture_modifier = "^[colorize:white:0",
|
||||||
|
@ -147,7 +128,7 @@ local boat = {
|
||||||
_last_v = 0, -- Temporary speed variable
|
_last_v = 0, -- Temporary speed variable
|
||||||
_removed = false, -- If true, boat entity is considered removed (e.g. after punch) and should be ignored
|
_removed = false, -- If true, boat entity is considered removed (e.g. after punch) and should be ignored
|
||||||
_itemstring = "mcl_boats:boat", -- Itemstring of the boat item (implies boat type)
|
_itemstring = "mcl_boats:boat", -- Itemstring of the boat item (implies boat type)
|
||||||
_animation = 0, -- 0: not animated; 1: paddling forwards; -1: paddling backwards
|
_animation = 0, -- 0: not animated; 1: paddling forwards; -1: paddling forwards
|
||||||
_regen_timer = 0,
|
_regen_timer = 0,
|
||||||
_damage_anim = 0,
|
_damage_anim = 0,
|
||||||
}
|
}
|
||||||
|
@ -170,14 +151,8 @@ function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self._last_v = self._v
|
self._last_v = self._v
|
||||||
self._itemstring = data.itemstring
|
self._itemstring = data.itemstring
|
||||||
|
|
||||||
-- Update the texutes for existing old boat entity instances.
|
while #data.textures < 5 do
|
||||||
-- Maybe remove this in the future.
|
table.insert(data.textures, data.textures[1])
|
||||||
if #data.textures ~= 2 then
|
|
||||||
local has_chest = self._itemstring:find("chest")
|
|
||||||
data.textures = {
|
|
||||||
data.textures[1]:gsub("_chest", ""),
|
|
||||||
has_chest and "mcl_chests_normal.png" or "blank.png"
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.object:set_properties({textures = data.textures})
|
self.object:set_properties({textures = data.textures})
|
||||||
|
@ -221,8 +196,6 @@ end
|
||||||
|
|
||||||
function boat.on_step(self, dtime, moveresult)
|
function boat.on_step(self, dtime, moveresult)
|
||||||
mcl_burning.tick(self.object, dtime, self)
|
mcl_burning.tick(self.object, dtime, self)
|
||||||
-- mcl_burning.tick may remove object immediately
|
|
||||||
if not self.object:get_pos() then return end
|
|
||||||
|
|
||||||
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
||||||
local v_factor = 1
|
local v_factor = 1
|
||||||
|
@ -231,21 +204,16 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
local on_water = true
|
local on_water = true
|
||||||
local on_ice = false
|
local on_ice = false
|
||||||
local in_water = is_water({x=p.x, y=p.y-boat_y_offset+1, z=p.z})
|
local in_water = is_water({x=p.x, y=p.y-boat_y_offset+1, z=p.z})
|
||||||
local in_river_water = is_river_water({x=p.x, y=p.y-boat_y_offset+1, z=p.z})
|
|
||||||
local waterp = {x=p.x, y=p.y-boat_y_offset - 0.1, z=p.z}
|
local waterp = {x=p.x, y=p.y-boat_y_offset - 0.1, z=p.z}
|
||||||
if not is_water(waterp) then
|
if not is_water(waterp) then
|
||||||
on_water = false
|
on_water = false
|
||||||
if not in_water and is_ice(waterp) then
|
if not in_water and is_ice(waterp) then
|
||||||
on_ice = true
|
on_ice = true
|
||||||
elseif is_fire({x=p.x, y=p.y-boat_y_offset, z=p.z}) then
|
|
||||||
boat.on_death(self, nil)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
else
|
else
|
||||||
v_slowdown = 0.04
|
v_slowdown = 0.04
|
||||||
v_factor = 0.5
|
v_factor = 0.5
|
||||||
end
|
end
|
||||||
elseif in_water and not in_river_water then
|
elseif in_water then
|
||||||
on_water = false
|
on_water = false
|
||||||
in_water = true
|
in_water = true
|
||||||
v_factor = 0.75
|
v_factor = 0.75
|
||||||
|
@ -377,18 +345,7 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
else
|
else
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
||||||
if is_river_water(p) then
|
if is_water(p) or is_obsidian_boat then
|
||||||
local y = self.object:get_velocity().y
|
|
||||||
if y >= 5 then
|
|
||||||
y = 5
|
|
||||||
elseif y < 0 then
|
|
||||||
new_acce = {x = 0, y = 10, z = 0}
|
|
||||||
else
|
|
||||||
new_acce = {x = 0, y = 2, z = 0}
|
|
||||||
end
|
|
||||||
new_velo = get_velocity(self._v, self.object:get_yaw(), y)
|
|
||||||
self.object:set_pos(self.object:get_pos())
|
|
||||||
elseif is_water(p) and not is_river_water(p) or is_obsidian_boat then
|
|
||||||
-- Inside water: Slowly sink
|
-- Inside water: Slowly sink
|
||||||
local y = self.object:get_velocity().y
|
local y = self.object:get_velocity().y
|
||||||
y = y - 0.01
|
y = y - 0.01
|
||||||
|
@ -428,18 +385,13 @@ end
|
||||||
-- Register one entity for all boat types
|
-- Register one entity for all boat types
|
||||||
minetest.register_entity("mcl_boats:boat", boat)
|
minetest.register_entity("mcl_boats:boat", boat)
|
||||||
|
|
||||||
local cboat = table.copy(boat)
|
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
|
||||||
cboat.textures = { "mcl_boats_texture_oak_chest_boat.png", "mcl_chests_normal.png" }
|
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
||||||
cboat._itemstring = "mcl_boats:chest_boat"
|
local craftstuffs = {}
|
||||||
cboat.collisionbox = {-0.5, -0.15, -0.5, 0.5, 0.75, 0.5}
|
if minetest.get_modpath("mcl_core") then
|
||||||
cboat.selectionbox = {-0.7, -0.15, -0.7, 0.7, 0.75, 0.7}
|
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
|
||||||
|
end
|
||||||
minetest.register_entity("mcl_boats:chest_boat", cboat)
|
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
|
||||||
mcl_entity_invs.register_inv("mcl_boats:chest_boat","Boat",27)
|
|
||||||
|
|
||||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian", "boat_mangrove", "chest_boat", "chest_boat_spruce", "chest_boat_birch", "chest_boat_jungle", "chest_boat_acacia", "chest_boat_dark_oak", "chest_boat_mangrove" }
|
|
||||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat"), S("Mangrove Boat"), S("Oak Chest Boat"), S("Spruce Chest Boat"), S("Birch Chest Boat"), S("Jungle Chest Boat"), S("Acacia Chest Boat"), S("Dark Oak Chest Boat"), S("Mangrove Chest Boat") }
|
|
||||||
local craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian", "mcl_mangrove:mangrove_wood" }
|
|
||||||
|
|
||||||
for b=1, #boat_ids do
|
for b=1, #boat_ids do
|
||||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||||
|
@ -455,21 +407,6 @@ for b=1, #boat_ids do
|
||||||
end
|
end
|
||||||
tt_help = S("Water vehicle")
|
tt_help = S("Water vehicle")
|
||||||
|
|
||||||
local inventory_image
|
|
||||||
local texture
|
|
||||||
local id = boat_ids[b]
|
|
||||||
if id:find("chest") then
|
|
||||||
if id == "chest_boat" then id = "oak" end
|
|
||||||
local id = id:gsub("chest_boat_", "")
|
|
||||||
inventory_image = "mcl_boats_" .. id .. "_chest_boat.png"
|
|
||||||
texture = "mcl_boats_texture_" .. id .. "_boat.png"
|
|
||||||
else
|
|
||||||
if id == "boat" then id = "oak" end
|
|
||||||
local id = id:gsub("boat_", "")
|
|
||||||
inventory_image = "mcl_boats_" .. id .. "_boat.png"
|
|
||||||
texture = "mcl_boats_texture_" .. id .. "_boat.png"
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_craftitem(itemstring, {
|
minetest.register_craftitem(itemstring, {
|
||||||
description = names[b],
|
description = names[b],
|
||||||
_tt_help = tt_help,
|
_tt_help = tt_help,
|
||||||
|
@ -477,7 +414,7 @@ for b=1, #boat_ids do
|
||||||
_doc_items_entry_name = helpname,
|
_doc_items_entry_name = helpname,
|
||||||
_doc_items_longdesc = longdesc,
|
_doc_items_longdesc = longdesc,
|
||||||
_doc_items_usagehelp = usagehelp,
|
_doc_items_usagehelp = usagehelp,
|
||||||
inventory_image = inventory_image,
|
inventory_image = "mcl_boats_"..images[b].."_boat.png",
|
||||||
liquids_pointable = true,
|
liquids_pointable = true,
|
||||||
groups = { boat = 1, transport = 1},
|
groups = { boat = 1, transport = 1},
|
||||||
stack_max = 1,
|
stack_max = 1,
|
||||||
|
@ -504,15 +441,10 @@ for b=1, #boat_ids do
|
||||||
else
|
else
|
||||||
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
||||||
end
|
end
|
||||||
local boat_ent = "mcl_boats:boat"
|
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
||||||
local chest_tex = "blank.png"
|
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
||||||
if itemstring:find("chest") then
|
|
||||||
boat_ent = "mcl_boats:chest_boat"
|
|
||||||
chest_tex = "mcl_chests_normal.png"
|
|
||||||
end
|
|
||||||
local boat = minetest.add_entity(pos, boat_ent)
|
|
||||||
boat:get_luaentity()._itemstring = itemstring
|
boat:get_luaentity()._itemstring = itemstring
|
||||||
boat:set_properties({ textures = { texture, chest_tex } })
|
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
||||||
boat:set_yaw(placer:get_look_horizontal())
|
boat:set_yaw(placer:get_look_horizontal())
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
|
@ -532,22 +464,13 @@ for b=1, #boat_ids do
|
||||||
})
|
})
|
||||||
|
|
||||||
local c = craftstuffs[b]
|
local c = craftstuffs[b]
|
||||||
if not itemstring:find("chest") then
|
minetest.register_craft({
|
||||||
minetest.register_craft({
|
output = itemstring,
|
||||||
output = itemstring:gsub(":boat",":chest_boat"),
|
recipe = {
|
||||||
recipe = {
|
{c, "", c},
|
||||||
{"mcl_chests:chest"},
|
{c, c, c},
|
||||||
{itemstring},
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
output = itemstring,
|
|
||||||
recipe = {
|
|
||||||
{c, "", c},
|
|
||||||
{c, c, c},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
|
|
|
@ -9,5 +9,4 @@ Oak Boat=Bateau en Chêne
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||||
Spruce Boat=Bateau en Sapin
|
Spruce Boat=Bateau en Sapin
|
||||||
Water vehicle=Véhicule aquatique
|
Water vehicle=Véhicule aquatique
|
||||||
Sneak to dismount=Se baisser pour descendre
|
Sneak to dismount=
|
||||||
Obsidian Boat=Bateau en Obsidienne
|
|
|
@ -1,13 +0,0 @@
|
||||||
# textdomain: mcl_boats
|
|
||||||
Acacia Boat=アカシアのボート
|
|
||||||
Birch Boat=シラカバのボート
|
|
||||||
Boat=ボート
|
|
||||||
Boats are used to travel on the surface of water.=ボートは、水面を移動するために使われます。
|
|
||||||
Dark Oak Boat=ダークオークのボート
|
|
||||||
Jungle Boat=ジャングルのボート
|
|
||||||
Oak Boat=オークのボート
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=水源を右クリックすると、ボートが配置されます。ボートを右クリックすると、乗り込みます。[左][右]で舵取り、[前]で加速、[後]で減速または後退します。[スニーク]でボートから離れ、ボートをパンチするとアイテムとしてドロップします。
|
|
||||||
Spruce Boat=トウヒのボート
|
|
||||||
Water vehicle=水上用の乗物
|
|
||||||
Sneak to dismount=スニークで降りる
|
|
||||||
Obsidian Boat=黒曜石のボート
|
|
|
@ -10,4 +10,3 @@ Rightclick on a water source to place the boat. Rightclick the boat to enter it.
|
||||||
Spruce Boat=
|
Spruce Boat=
|
||||||
Water vehicle=
|
Water vehicle=
|
||||||
Sneak to dismount=
|
Sneak to dismount=
|
||||||
Obsidian Boat=
|
|
|
@ -1,5 +1,7 @@
|
||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib, mcl_title, mcl_entity_invs
|
depends = mcl_player, flowlib, mcl_title
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 12 KiB |
|
@ -1,5 +1,3 @@
|
||||||
local enable_damage = minetest.settings:get_bool("enable_damage")
|
|
||||||
|
|
||||||
function mcl_burning.get_storage(obj)
|
function mcl_burning.get_storage(obj)
|
||||||
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||||
end
|
end
|
||||||
|
@ -79,7 +77,7 @@ end
|
||||||
-- The effective burn duration is modified by obj's armor protection.
|
-- The effective burn duration is modified by obj's armor protection.
|
||||||
-- If obj was already burning, its burn duration is updated if the current
|
-- If obj was already burning, its burn duration is updated if the current
|
||||||
-- duration is less than burn_time.
|
-- duration is less than burn_time.
|
||||||
-- If obj is dead, fireproof or enable_damage is disabled, this function does nothing.
|
-- If obj is dead, fireproof or a creative player, this function does nothing.
|
||||||
--
|
--
|
||||||
function mcl_burning.set_on_fire(obj, burn_time)
|
function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
if obj:get_hp() < 0 then
|
if obj:get_hp() < 0 then
|
||||||
|
@ -91,9 +89,8 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if obj:is_player() and not enable_damage then
|
if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
burn_time = 0
|
burn_time = 0
|
||||||
return
|
|
||||||
else
|
else
|
||||||
local max_fire_prot_lvl = 0
|
local max_fire_prot_lvl = 0
|
||||||
local inv = mcl_util.get_inventory(obj)
|
local inv = mcl_util.get_inventory(obj)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
name = mcl_burning
|
name = mcl_burning
|
||||||
description = Burning Objects for MineClone2
|
description = Burning Objects for MineClone2
|
||||||
author = Fleckenstein
|
author = Fleckenstein
|
||||||
depends = mcl_weather
|
|
|
@ -1,36 +0,0 @@
|
||||||
# mcl_dripping
|
|
||||||
|
|
||||||
Dripping Mod by kddekadenz, modified for MineClone 2 by Wuzzy, NO11 and AFCM
|
|
||||||
|
|
||||||
## Manual
|
|
||||||
|
|
||||||
- drops are generated rarely under solid nodes
|
|
||||||
- they will stay some time at the generated block and than they fall down
|
|
||||||
- when they collide with the ground, a sound is played and they are destroyed
|
|
||||||
|
|
||||||
Water and Lava have builtin drops registered.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
code & sounds: CC0
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```lua
|
|
||||||
mcl_dripping.register_drop({
|
|
||||||
-- The group the liquid's nodes belong to
|
|
||||||
liquid = "water",
|
|
||||||
-- The texture used (particles will take a random 2x2 area of it)
|
|
||||||
texture = "default_water_source_animated.png",
|
|
||||||
-- Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
|
||||||
light = 1,
|
|
||||||
-- The nodes (or node group) the particles will spawn under
|
|
||||||
nodes = { "group:opaque", "group:leaves" },
|
|
||||||
-- The sound that will be played then the particle detaches from the roof, see SimpleSoundSpec in lua_api.txt
|
|
||||||
sound = "drippingwater_drip",
|
|
||||||
-- The interval for the ABM to run
|
|
||||||
interval = 60,
|
|
||||||
-- The chance of the ABM
|
|
||||||
chance = 10,
|
|
||||||
})
|
|
||||||
```
|
|
|
@ -1,101 +1,66 @@
|
||||||
-- Dripping Water Mod
|
-- Dripping Water Mod
|
||||||
-- by kddekadenz
|
-- by kddekadenz
|
||||||
-- License of code, textures & sounds: CC0
|
|
||||||
|
|
||||||
local math = math
|
local math = math
|
||||||
|
|
||||||
mcl_dripping = {}
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
|
local function register_drop(liquid, glow, sound, nodes)
|
||||||
---@param pos Vector
|
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
|
||||||
---@param liquid string
|
hp_max = 1,
|
||||||
---@param sound SimpleSoundSpec
|
physical = true,
|
||||||
---@param interval integer
|
collide_with_objects = false,
|
||||||
---@param texture string
|
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
|
||||||
local function make_drop(pos, liquid, sound, interval, texture)
|
glow = glow,
|
||||||
local pt = {
|
pointable = false,
|
||||||
velocity = vector.zero(),
|
visual = "sprite",
|
||||||
collision_removal = false,
|
visual_size = {x = 0.1, y = 0.1},
|
||||||
}
|
textures = {""},
|
||||||
|
spritediv = {x = 1, y = 1},
|
||||||
local t = math.random() + math.random(1, interval)
|
initial_sprite_basepos = {x = 0, y = 0},
|
||||||
|
static_save = false,
|
||||||
minetest.after(t, function()
|
_dropped = false,
|
||||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
on_activate = function(self)
|
||||||
|
self.object:set_properties({
|
||||||
pt.pos = vector.offset(pos, x, -0.52, z)
|
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
|
||||||
pt.acceleration = vector.zero()
|
})
|
||||||
pt.collisiondetection = false
|
end,
|
||||||
pt.expirationtime = t
|
on_step = function(self, dtime)
|
||||||
|
local k = math.random(1, 222)
|
||||||
pt.texture = "[combine:2x2:" ..
|
local ownpos = self.object:get_pos()
|
||||||
-math.random(1, 16) .. "," .. -math.random(1, 16) .. "=" .. texture
|
if k == 1 then
|
||||||
|
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||||
minetest.add_particle(pt)
|
end
|
||||||
|
if minetest.get_node(vector.offset(ownpos, 0, 0.5, 0)).name == "air" then
|
||||||
minetest.after(t, function()
|
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||||
pt.acceleration = vector.new(0, -5, 0)
|
end
|
||||||
pt.collisiondetection = true
|
if minetest.get_node(vector.offset(ownpos, 0, -0.1, 0)).name ~= "air" then
|
||||||
pt.expirationtime = math.random() + math.random(1, interval / 2)
|
local ent = self.object:get_luaentity()
|
||||||
|
if not ent._dropped then
|
||||||
minetest.add_particle(pt)
|
ent._dropped = true
|
||||||
|
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
minetest.sound_play(sound, { pos = pos, gain = 0.5, max_hear_distance = 8 },
|
end
|
||||||
true)
|
if k < 3 then
|
||||||
end)
|
self.object:remove()
|
||||||
end)
|
end
|
||||||
end
|
end
|
||||||
|
end,
|
||||||
---@class mcl_dripping_drop_definition
|
})
|
||||||
---@field liquid string The group the liquid's nodes belong to
|
|
||||||
---@field texture string The texture used (particles will take a random 2x2 area of it)
|
|
||||||
---@field light integer Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
|
||||||
---@field nodes string[] The nodes (or node group) the particles will spawn under
|
|
||||||
---@field interval integer The interval for the ABM to run
|
|
||||||
---@field chance integer The chance of the ABM
|
|
||||||
---@field sound SimpleSoundSpec The sound that will be played then the particle detaches from the roof
|
|
||||||
|
|
||||||
---@param def mcl_dripping_drop_definition
|
|
||||||
function mcl_dripping.register_drop(def)
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Create drops",
|
label = "Create drops",
|
||||||
nodenames = def.nodes,
|
nodenames = nodes,
|
||||||
neighbors = { "group:" .. def.liquid },
|
neighbors = {"group:" .. liquid},
|
||||||
interval = def.interval,
|
interval = 2,
|
||||||
chance = def.chance,
|
chance = 22,
|
||||||
action = function(pos)
|
action = function(pos)
|
||||||
local below = minetest.get_node(vector.offset(pos,0,-1,0)).name
|
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
||||||
if below ~= "air" then return end
|
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
||||||
local r = math.ceil(def.interval / 20)
|
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||||
local nn = minetest.find_nodes_in_area(vector.offset(pos, -r, 0, -r), vector.offset(pos, r, 0, r), def.nodes)
|
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
||||||
--start a bunch of particle cycles to be able to get away
|
|
||||||
--with longer abm cycles
|
|
||||||
table.shuffle(nn)
|
|
||||||
for i = 1, math.random(#nn) do
|
|
||||||
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, def.liquid) ~= 0 then
|
|
||||||
make_drop(nn[i], def.liquid, def.sound, def.interval, def.texture)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_dripping.register_drop({
|
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
||||||
liquid = "water",
|
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
||||||
texture = "default_water_source_animated.png",
|
|
||||||
light = 1,
|
|
||||||
nodes = { "group:opaque", "group:leaves" },
|
|
||||||
sound = "drippingwater_drip",
|
|
||||||
interval = 60.3,
|
|
||||||
chance = 10,
|
|
||||||
})
|
|
||||||
|
|
||||||
mcl_dripping.register_drop({
|
|
||||||
liquid = "lava",
|
|
||||||
texture = "default_lava_source_animated.png",
|
|
||||||
light = math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3),
|
|
||||||
nodes = { "group:opaque" },
|
|
||||||
sound = "drippingwater_lavadrip",
|
|
||||||
interval = 110.1,
|
|
||||||
chance = 10,
|
|
||||||
})
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
Dripping Mod
|
||||||
|
by kddekadenz
|
||||||
|
|
||||||
|
modified for MineClone 2 by Wuzzy and NO11
|
||||||
|
|
||||||
|
|
||||||
|
Installing instructions:
|
||||||
|
|
||||||
|
1. Copy the mcl_dripping mod folder into games/gamemode/mods
|
||||||
|
|
||||||
|
2. Start game and enjoy :)
|
||||||
|
|
||||||
|
|
||||||
|
Manual:
|
||||||
|
|
||||||
|
-> drops are generated rarely under solid nodes
|
||||||
|
-> they will stay some time at the generated block and than they fall down
|
||||||
|
-> when they collide with the ground, a sound is played and they are destroyed
|
||||||
|
|
||||||
|
|
||||||
|
License:
|
||||||
|
|
||||||
|
code & sounds: CC0
|
||||||
|
|
||||||
|
|
||||||
|
Changelog:
|
||||||
|
|
||||||
|
16.04.2012 - first release
|
||||||
|
28.04.2012 - drops are now 3D; added lava drops; fixed generating of drops (not at edges now)
|
|
@ -1,13 +0,0 @@
|
||||||
mcl_entity_invs
|
|
||||||
===============
|
|
||||||
|
|
||||||
Inventories for your entities. It's simple. Depend on mcl_entity_invs and register your entity like so:
|
|
||||||
|
|
||||||
* mcl_entity_invs.register_inv("entity:name","Title shown in formspec",inventory_size,disable_on_righclick)
|
|
||||||
*If disable_on_righclick is true other mods can handle when to show the inventory themselves
|
|
||||||
* The inventory size can be set dynamically by initializing it with an explicit nil
|
|
||||||
|
|
||||||
* mcl_entity_invs.show_inv_form(entity,clicker,[formspec text])
|
|
||||||
* formspec_text is an additional text that is put after the title
|
|
||||||
|
|
||||||
It works by setting up a detached inventory per entity which is accessed by an id/hash generated from the entities position at creation, the progressed gametime at creation and a random salt.
|
|
|
@ -1,210 +0,0 @@
|
||||||
mcl_entity_invs = {}
|
|
||||||
|
|
||||||
local open_invs = {}
|
|
||||||
|
|
||||||
local function mcl_log (message)
|
|
||||||
mcl_util.mcl_log (message, "[Entity Invs]")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_distance(inv,player,count)
|
|
||||||
for _,o in pairs(minetest.get_objects_inside_radius(player:get_pos(),5)) do
|
|
||||||
local l = o:get_luaentity()
|
|
||||||
if l and l._inv_id and inv:get_location().name == l._inv_id then return count end
|
|
||||||
end
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local inv_callbacks = {
|
|
||||||
allow_take = function(inv, listname, index, stack, player)
|
|
||||||
return check_distance(inv,player,stack:get_count())
|
|
||||||
end,
|
|
||||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
|
||||||
return check_distance(inv,player,count)
|
|
||||||
end,
|
|
||||||
allow_put = function(inv, listname, index, stack, player)
|
|
||||||
return check_distance(inv,player,stack:get_count())
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
function mcl_entity_invs.load_inv(ent,size)
|
|
||||||
mcl_log("load_inv")
|
|
||||||
if not ent._inv_id then return end
|
|
||||||
mcl_log("load_inv 2")
|
|
||||||
local inv = minetest.get_inventory({type="detached", name=ent._inv_id})
|
|
||||||
if not inv then
|
|
||||||
mcl_log("load_inv 3")
|
|
||||||
inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks)
|
|
||||||
inv:set_size("main", size)
|
|
||||||
if ent._items then
|
|
||||||
inv:set_list("main",ent._items)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("load_inv 4")
|
|
||||||
end
|
|
||||||
return inv
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_entity_invs.save_inv(ent)
|
|
||||||
if ent._inv then
|
|
||||||
ent._items = {}
|
|
||||||
for i,it in ipairs(ent._inv:get_list("main")) do
|
|
||||||
ent._items[i] = it:to_string()
|
|
||||||
end
|
|
||||||
minetest.remove_detached_inventory(ent._inv_id)
|
|
||||||
ent._inv = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function load_default_formspec (ent, text)
|
|
||||||
text = text or ""
|
|
||||||
|
|
||||||
local invent_size = ent._inv_size
|
|
||||||
local div_by_two = invent_size % 2 == 0
|
|
||||||
local div_by_three = invent_size % 3 == 0
|
|
||||||
|
|
||||||
--mcl_log("Div by 3: ".. tostring(div_by_three))
|
|
||||||
--mcl_log("Div by 2: ".. tostring(div_by_two))
|
|
||||||
--mcl_log("invent_size: ".. tostring(invent_size))
|
|
||||||
local rows = 3
|
|
||||||
if invent_size > 18 or (div_by_three == true and invent_size > 8) then
|
|
||||||
--mcl_log("Div by 3")
|
|
||||||
rows = 3
|
|
||||||
elseif (div_by_two == true and invent_size > 3) or invent_size > 9 then
|
|
||||||
--mcl_log("Div by 2")
|
|
||||||
rows = 2
|
|
||||||
else
|
|
||||||
--mcl_log("Not div by 2 or 3")
|
|
||||||
rows = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
--local rows = 3
|
|
||||||
local cols = (math.ceil(ent._inv_size/rows))
|
|
||||||
local spacing = (9 - cols) / 2
|
|
||||||
|
|
||||||
local formspec = "size[9,8.75]"
|
|
||||||
.. "label[0,0;" .. minetest.formspec_escape(
|
|
||||||
minetest.colorize("#313131", ent._inv_title .. " ".. text)) .. "]"
|
|
||||||
.. "list[detached:"..ent._inv_id..";main;"..spacing..",0.5;"..cols..","..rows..";]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(spacing,0.5,cols,rows)
|
|
||||||
.. "label[0,4.0;" .. minetest.formspec_escape(
|
|
||||||
minetest.colorize("#313131", "Inventory")) .. "]"
|
|
||||||
.. "list[current_player;main;0,4.5;9,3;9]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(0,4.5,9,3)
|
|
||||||
.. "list[current_player;main;0,7.74;9,1;]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(0,7.74,9,1)
|
|
||||||
.. "listring[detached:"..ent._inv_id..";main]"
|
|
||||||
.. "listring[current_player;main]"
|
|
||||||
return formspec
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function mcl_entity_invs.show_inv_form(ent,player,text)
|
|
||||||
if not ent._inv_id then return end
|
|
||||||
if not open_invs[ent] then
|
|
||||||
open_invs[ent] = 0
|
|
||||||
end
|
|
||||||
ent._inv = mcl_entity_invs.load_inv(ent,ent._inv_size)
|
|
||||||
open_invs[ent] = open_invs[ent] + 1
|
|
||||||
|
|
||||||
local playername = player:get_player_name()
|
|
||||||
|
|
||||||
minetest.show_formspec(playername, ent._inv_id, load_default_formspec (ent, text))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drop_inv(ent)
|
|
||||||
if not ent._items then return end
|
|
||||||
local pos = ent.object:get_pos()
|
|
||||||
for i,it in pairs(ent._items) do
|
|
||||||
local p = vector.add(pos,vector.new(math.random() - 0.5, math.random()-0.5, math.random()-0.5))
|
|
||||||
minetest.add_item(p,it)
|
|
||||||
end
|
|
||||||
ent._items = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function on_remove(self,killer,oldf)
|
|
||||||
mcl_entity_invs.save_inv(self)
|
|
||||||
drop_inv(self)
|
|
||||||
if oldf then return oldf(self,killer) end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
||||||
for k,v in pairs(open_invs) do
|
|
||||||
if formname == k._inv_id then
|
|
||||||
open_invs[k] = open_invs[k] - 1
|
|
||||||
if open_invs[k] < 1 then
|
|
||||||
mcl_entity_invs.save_inv(k)
|
|
||||||
open_invs[k] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
function mcl_entity_invs.register_inv(entity_name,show_name,size,no_on_righclick,no_sneak)
|
|
||||||
assert(minetest.registered_entities[entity_name],"mcl_entity_invs.register_inv called with invalid entity: "..tostring(entity_name))
|
|
||||||
minetest.registered_entities[entity_name]._inv_size = size
|
|
||||||
minetest.registered_entities[entity_name]._inv_title = show_name
|
|
||||||
|
|
||||||
local old_oa = minetest.registered_entities[entity_name].on_activate
|
|
||||||
minetest.registered_entities[entity_name].on_activate = function(self,staticdata,dtime_s)
|
|
||||||
local r
|
|
||||||
if old_oa then r=old_oa(self,staticdata,dtime_s) end
|
|
||||||
local d = minetest.deserialize(staticdata)
|
|
||||||
if type(d) == "table" and d._inv_id then
|
|
||||||
self._inv_id = d._inv_id
|
|
||||||
self._items = d._items
|
|
||||||
self._inv_size = d._inv_size
|
|
||||||
else
|
|
||||||
self._inv_id="entity_inv_"..minetest.sha1(minetest.get_gametime()..minetest.pos_to_string(self.object:get_pos())..tostring(math.random()))
|
|
||||||
--gametime and position for collision safety and math.random salt to protect against position brute-force
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
if not no_on_righclick then
|
|
||||||
local old_rc = minetest.registered_entities[entity_name].on_rightclick
|
|
||||||
minetest.registered_entities[entity_name].on_rightclick = function(self,clicker)
|
|
||||||
if no_sneak or clicker:get_player_control().sneak then
|
|
||||||
mcl_entity_invs.show_inv_form(self,clicker,"")
|
|
||||||
if not no_sneak then return end
|
|
||||||
end
|
|
||||||
if old_rc then return old_rc(self,clicker) end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_gsd = minetest.registered_entities[entity_name].get_staticdata
|
|
||||||
minetest.registered_entities[entity_name].get_staticdata = function(self)
|
|
||||||
local old_sd = old_gsd(self)
|
|
||||||
local d = minetest.deserialize(old_sd)
|
|
||||||
assert(type(d) == "table","mcl_entity_invs currently only works with entities that return a (serialized) table in get_staticdata. "..tostring(self.name).." returned: "..tostring(old_sd))
|
|
||||||
d._inv_id = self._inv_id
|
|
||||||
d._inv_size = self._inv_size
|
|
||||||
d._items = {}
|
|
||||||
if self._items then
|
|
||||||
for i,it in ipairs(self._items) do
|
|
||||||
d._items[i] = it
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return minetest.serialize(d)
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_ode = minetest.registered_entities[entity_name].on_deactivate
|
|
||||||
minetest.registered_entities[entity_name].on_deactivate = function(self,removal)
|
|
||||||
mcl_entity_invs.save_inv(self)
|
|
||||||
if removal then
|
|
||||||
on_remove(self)
|
|
||||||
end
|
|
||||||
if old_ode then return old_ode(self,removal) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_od = minetest.registered_entities[entity_name].on_death
|
|
||||||
minetest.registered_entities[entity_name].on_death = function(self,killer)
|
|
||||||
if not self.is_mob then
|
|
||||||
on_remove(self,killer,old_od)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local old_odi = minetest.registered_entities[entity_name].on_die
|
|
||||||
minetest.registered_entities[entity_name].on_die = function(self,killer)
|
|
||||||
if self.is_mob then
|
|
||||||
on_remove(self,killer,old_od)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = mcl_entity_invs
|
|
||||||
author = cora
|
|
||||||
depends = mcl_formspec
|
|
|
@ -25,10 +25,4 @@ http://minetest.net/
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
---------
|
|
||||||
|
|
||||||
Alterations and contributions are released under GNU GPLv3 after 11/11/2022 and for contributors:
|
|
||||||
|
|
||||||
AncientMariner/ancientmarinerdev
|
|
||||||
|
|
|
@ -6,16 +6,6 @@ local pool = {}
|
||||||
|
|
||||||
local tick = false
|
local tick = false
|
||||||
|
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities",false)
|
|
||||||
local function mcl_log (message)
|
|
||||||
if LOGGING_ON then
|
|
||||||
mcl_util.mcl_log (message, "[Item Entities]", true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
local name
|
local name
|
||||||
name = player:get_player_name()
|
name = player:get_player_name()
|
||||||
|
@ -69,10 +59,6 @@ mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:crying_obsidian", "mcl:whosCuttingOnions")
|
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hiddenInTheDepths")
|
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_end:dragon_egg", "mcl:PickUpDragonEgg")
|
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_armor:elytra", "mcl:skysTheLimit")
|
|
||||||
|
|
||||||
local function check_pickup_achievements(object, player)
|
local function check_pickup_achievements(object, player)
|
||||||
if has_awards then
|
if has_awards then
|
||||||
|
@ -330,14 +316,18 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
-- Spawn item and apply random speed
|
-- Spawn item and apply random speed
|
||||||
local obj = minetest.add_item(dpos, drop_item)
|
local obj = minetest.add_item(dpos, drop_item)
|
||||||
if obj then
|
if obj then
|
||||||
-- set the velocity multiplier to the stored amount or if the game dug this node, apply a bigger velocity
|
local x = math.random(1, 5)
|
||||||
local v = 1
|
if math.random(1,2) == 1 then
|
||||||
if digger and digger:is_player() then
|
x = -x
|
||||||
obj:get_luaentity().random_velocity = 1
|
|
||||||
else
|
|
||||||
obj:get_luaentity().random_velocity = 1.6
|
|
||||||
end
|
end
|
||||||
|
local z = math.random(1, 5)
|
||||||
|
if math.random(1,2) == 1 then
|
||||||
|
z = -z
|
||||||
|
end
|
||||||
|
obj:set_velocity({x=1/x, y=obj:get_velocity().y, z=1/z})
|
||||||
|
|
||||||
obj:get_luaentity().age = item_drop_settings.dug_buffer
|
obj:get_luaentity().age = item_drop_settings.dug_buffer
|
||||||
|
|
||||||
obj:get_luaentity()._insta_collect = false
|
obj:get_luaentity()._insta_collect = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -385,116 +375,6 @@ local function cxcz(o, cw, one, zero)
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hopper_take_item (self, pos)
|
|
||||||
--mcl_log("self.itemstring: ".. self.itemstring)
|
|
||||||
--mcl_log("self.itemstring: ".. minetest.pos_to_string(pos))
|
|
||||||
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 2)
|
|
||||||
|
|
||||||
if objs and self.itemstring then
|
|
||||||
--mcl_log("there is an itemstring. Number of objs: ".. #objs)
|
|
||||||
|
|
||||||
for k,v in pairs(objs) do
|
|
||||||
local ent = v:get_luaentity()
|
|
||||||
|
|
||||||
-- Don't forget actual hoppers
|
|
||||||
if ent and ent.name == "mcl_minecarts:hopper_minecart" then
|
|
||||||
local taken_items = false
|
|
||||||
|
|
||||||
mcl_log("ent.name: ".. tostring(ent.name))
|
|
||||||
mcl_log("ent pos: ".. tostring(ent.object:get_pos()))
|
|
||||||
|
|
||||||
local inv = mcl_entity_invs.load_inv(ent,5)
|
|
||||||
|
|
||||||
if not inv then
|
|
||||||
mcl_log("No inv")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local current_itemstack = ItemStack(self.itemstring)
|
|
||||||
|
|
||||||
mcl_log("inv. size: " .. ent._inv_size)
|
|
||||||
if inv:room_for_item("main", current_itemstack) then
|
|
||||||
mcl_log("Room")
|
|
||||||
inv:add_item("main", current_itemstack)
|
|
||||||
self.object:get_luaentity().itemstring = ""
|
|
||||||
self.object:remove()
|
|
||||||
taken_items = true
|
|
||||||
else
|
|
||||||
mcl_log("no Room")
|
|
||||||
end
|
|
||||||
|
|
||||||
if not taken_items then
|
|
||||||
local items_remaining = current_itemstack:get_count()
|
|
||||||
|
|
||||||
-- This will take part of a floating item stack if no slot can hold the full amount
|
|
||||||
for i = 1, ent._inv_size,1 do
|
|
||||||
local stack = inv:get_stack("main", i)
|
|
||||||
|
|
||||||
mcl_log("i: " .. tostring(i))
|
|
||||||
mcl_log("Items remaining: " .. items_remaining)
|
|
||||||
mcl_log("Name: " .. tostring(stack:get_name()))
|
|
||||||
|
|
||||||
if current_itemstack:get_name() == stack:get_name() then
|
|
||||||
mcl_log("We have a match. Name: " .. tostring(stack:get_name()))
|
|
||||||
|
|
||||||
local room_for = stack:get_stack_max() - stack:get_count()
|
|
||||||
mcl_log("Room for: " .. tostring(room_for))
|
|
||||||
|
|
||||||
if room_for == 0 then
|
|
||||||
-- Do nothing
|
|
||||||
mcl_log("No room")
|
|
||||||
elseif room_for < items_remaining then
|
|
||||||
mcl_log("We have more items remaining than space")
|
|
||||||
|
|
||||||
items_remaining = items_remaining - room_for
|
|
||||||
stack:set_count(stack:get_stack_max())
|
|
||||||
inv:set_stack("main", i, stack)
|
|
||||||
taken_items = true
|
|
||||||
else
|
|
||||||
local new_stack_size = stack:get_count() + items_remaining
|
|
||||||
stack:set_count(new_stack_size)
|
|
||||||
mcl_log("We have more than enough space. Now holds: " .. new_stack_size)
|
|
||||||
|
|
||||||
inv:set_stack("main", i, stack)
|
|
||||||
items_remaining = 0
|
|
||||||
|
|
||||||
self.object:get_luaentity().itemstring = ""
|
|
||||||
self.object:remove()
|
|
||||||
|
|
||||||
taken_items = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_log("Count: " .. tostring(stack:get_count()))
|
|
||||||
mcl_log("stack max: " .. tostring(stack:get_stack_max()))
|
|
||||||
--mcl_log("Is it empty: " .. stack:to_string())
|
|
||||||
end
|
|
||||||
|
|
||||||
if i == ent._inv_size and taken_items then
|
|
||||||
mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining)
|
|
||||||
current_itemstack:set_count(items_remaining)
|
|
||||||
--mcl_log("Itemstack2: " .. current_itemstack:to_string())
|
|
||||||
self.itemstring = current_itemstack:to_string()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--Add in, and delete
|
|
||||||
if taken_items then
|
|
||||||
mcl_log("Saving")
|
|
||||||
mcl_entity_invs.save_inv(ent)
|
|
||||||
return taken_items
|
|
||||||
else
|
|
||||||
mcl_log("No need to save")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity(":__builtin:item", {
|
minetest.register_entity(":__builtin:item", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
|
@ -526,33 +406,9 @@ 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,
|
||||||
|
|
||||||
-- Multiplier for initial random velocity when the item is spawned
|
|
||||||
random_velocity = 1,
|
|
||||||
|
|
||||||
-- How old it has become in the collection animation
|
-- How old it has become in the collection animation
|
||||||
collection_age = 0,
|
collection_age = 0,
|
||||||
|
|
||||||
-- Function to apply a random velocity
|
|
||||||
apply_random_vel = function(self, speed)
|
|
||||||
if not self or not self.object or not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- if you passed a value then use that for the velocity multiplier
|
|
||||||
if speed ~= nil then self.random_velocity = speed end
|
|
||||||
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
if vel and vel.x == 0 and vel.z == 0 and self.random_velocity > 0 then
|
|
||||||
local v = self.random_velocity
|
|
||||||
local x = math.random(5, 10) / 10 * v
|
|
||||||
if math.random(0,10) < 5 then x = -x end
|
|
||||||
local z = math.random(5, 10) / 10 * v
|
|
||||||
if math.random(0,10) < 5 then z = -z end
|
|
||||||
local y = math.random(2,4)
|
|
||||||
self.object:set_velocity({x=x, y=y, z=z})
|
|
||||||
end
|
|
||||||
self.random_velocity = 0
|
|
||||||
end,
|
|
||||||
|
|
||||||
set_item = function(self, itemstring)
|
set_item = function(self, itemstring)
|
||||||
self.itemstring = itemstring
|
self.itemstring = itemstring
|
||||||
if self.itemstring == "" then
|
if self.itemstring == "" then
|
||||||
|
@ -605,9 +461,27 @@ minetest.register_entity(":__builtin:item", {
|
||||||
glow = glow,
|
glow = glow,
|
||||||
}
|
}
|
||||||
self.object:set_properties(prop)
|
self.object:set_properties(prop)
|
||||||
if item_drop_settings.random_item_velocity == true and self.age < 1 then
|
if item_drop_settings.random_item_velocity == true then
|
||||||
minetest.after(0, self.apply_random_vel, self)
|
minetest.after(0, function(self)
|
||||||
|
if not self or not self.object or not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
if vel and vel.x == 0 and vel.z == 0 then
|
||||||
|
local x = math.random(1, 5)
|
||||||
|
if math.random(1,2) == 1 then
|
||||||
|
x = -x
|
||||||
|
end
|
||||||
|
local z = math.random(1, 5)
|
||||||
|
if math.random(1,2) == 1 then
|
||||||
|
z = -z
|
||||||
|
end
|
||||||
|
local y = math.random(2,4)
|
||||||
|
self.object:set_velocity({x=1/x, y=y, z=1/z})
|
||||||
|
end
|
||||||
|
end, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
|
@ -661,9 +535,9 @@ minetest.register_entity(":__builtin:item", {
|
||||||
self.itemstring = data.itemstring
|
self.itemstring = data.itemstring
|
||||||
self.always_collect = data.always_collect
|
self.always_collect = data.always_collect
|
||||||
if data.age then
|
if data.age then
|
||||||
self.age = data.age
|
self.age = data.age + dtime_s
|
||||||
else
|
else
|
||||||
self.age = self.age
|
self.age = dtime_s
|
||||||
end
|
end
|
||||||
--remember collection data
|
--remember collection data
|
||||||
-- If true, can collect item without delay
|
-- If true, can collect item without delay
|
||||||
|
@ -696,7 +570,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
self._forcetimer = 0
|
self._forcetimer = 0
|
||||||
|
|
||||||
self.object:set_armor_groups({immortal = 1})
|
self.object:set_armor_groups({immortal = 1})
|
||||||
-- self.object:set_velocity({x = 0, y = 2, z = 0})
|
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||||
self.object:set_acceleration({x = 0, y = -get_gravity(), z = 0})
|
self.object:set_acceleration({x = 0, y = -get_gravity(), z = 0})
|
||||||
self:set_item(self.itemstring)
|
self:set_item(self.itemstring)
|
||||||
end,
|
end,
|
||||||
|
@ -726,13 +600,12 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
-- Merge the remote stack into this one
|
-- Merge the remote stack into this one
|
||||||
|
|
||||||
-- local pos = object:get_pos()
|
local pos = object:get_pos()
|
||||||
-- pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
||||||
-- self.object:move_to(pos)
|
self.object:move_to(pos)
|
||||||
|
|
||||||
self.age = 0 -- Handle as new entity
|
self.age = 0 -- Handle as new entity
|
||||||
own_stack:set_count(total_count)
|
own_stack:set_count(total_count)
|
||||||
self.random_velocity = 0
|
|
||||||
self:set_item(own_stack:to_string())
|
self:set_item(own_stack:to_string())
|
||||||
|
|
||||||
entity._removed = true
|
entity._removed = true
|
||||||
|
@ -768,62 +641,36 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
|
|
||||||
-- If hopper has taken item, it has gone, and no operations should be conducted on this item
|
|
||||||
if hopper_take_item(self, p) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = minetest.get_node_or_nil(p)
|
||||||
local in_unloaded = (node == nil)
|
local in_unloaded = (node == nil)
|
||||||
|
|
||||||
if in_unloaded then
|
|
||||||
-- Don't infinetly fall into unloaded map
|
|
||||||
disable_physics(self.object, self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.is_clock then
|
if self.is_clock then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local nn = node.name
|
|
||||||
local is_in_water = (minetest.get_item_group(nn, "liquid") ~= 0)
|
|
||||||
local nn_above = minetest.get_node({x=p.x, y=p.y+0.1, z=p.z}).name
|
|
||||||
-- make sure it's more or less stationary and is at water level
|
|
||||||
local sleep_threshold = 0.3
|
|
||||||
local is_floating = false
|
|
||||||
local is_stationary = math.abs(self.object:get_velocity().x) < sleep_threshold
|
|
||||||
and math.abs(self.object:get_velocity().y) < sleep_threshold
|
|
||||||
and math.abs(self.object:get_velocity().z) < sleep_threshold
|
|
||||||
if is_in_water and is_stationary then
|
|
||||||
is_floating = (is_in_water
|
|
||||||
and (minetest.get_item_group(nn_above, "liquid") == 0))
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_floating and self.physical_state == true then
|
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
disable_physics(self.object, self)
|
|
||||||
end
|
|
||||||
-- If no collector was found for a long enough time, declare the magnet as disabled
|
-- If no collector was found for a long enough time, declare the magnet as disabled
|
||||||
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
||||||
self._magnet_active = false
|
self._magnet_active = false
|
||||||
enable_physics(self.object, self)
|
enable_physics(self.object, self)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
if in_unloaded then
|
||||||
|
-- Don't infinetly fall into unloaded map
|
||||||
|
disable_physics(self.object, self)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Destroy item in lava, fire or special nodes
|
-- Destroy item in lava, fire or special nodes
|
||||||
|
local nn = node.name
|
||||||
local def = minetest.registered_nodes[nn]
|
local def = minetest.registered_nodes[nn]
|
||||||
local lg = minetest.get_item_group(nn, "lava")
|
local lg = minetest.get_item_group(nn, "lava")
|
||||||
local fg = minetest.get_item_group(nn, "fire")
|
local fg = minetest.get_item_group(nn, "fire")
|
||||||
local dg = minetest.get_item_group(nn, "destroys_items")
|
local dg = minetest.get_item_group(nn, "destroys_items")
|
||||||
if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then
|
if (def and (lg ~= 0 or fg ~= 0 or dg == 1)) then
|
||||||
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
|
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
|
||||||
if self.age > 2 and minetest.get_item_group(self.itemstring, "fire_immune") == 0 then
|
if self.age > 2 then
|
||||||
if dg ~= 2 then
|
if dg ~= 2 then
|
||||||
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5})
|
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5})
|
||||||
end
|
end
|
||||||
|
@ -846,7 +693,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Push item out when stuck inside solid opaque node
|
-- Push item out when stuck inside solid opaque node
|
||||||
if not is_in_water and def and def.walkable and def.groups and def.groups.opaque == 1 then
|
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||||
local shootdir
|
local shootdir
|
||||||
local cx = (p.x % 1) - 0.5
|
local cx = (p.x % 1) - 0.5
|
||||||
local cz = (p.z % 1) - 0.5
|
local cz = (p.z % 1) - 0.5
|
||||||
|
@ -887,8 +734,8 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local newv = vector.multiply(shootdir, 3)
|
local newv = vector.multiply(shootdir, 3)
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
self.object:set_velocity(newv)
|
self.object:set_velocity(newv)
|
||||||
disable_physics(self.object, self, false, false)
|
|
||||||
|
|
||||||
|
disable_physics(self.object, self, false, false)
|
||||||
|
|
||||||
if shootdir.y == 0 then
|
if shootdir.y == 0 then
|
||||||
self._force = newv
|
self._force = newv
|
||||||
|
@ -927,8 +774,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Move item around on flowing liquids; add 'source' check to allow items to continue flowing a bit in the source block of flowing water.
|
-- Move item around on flowing liquids; add 'source' check to allow items to continue flowing a bit in the source block of flowing water.
|
||||||
if def and not is_floating and (def.liquidtype == "flowing" or def.liquidtype == "source") then
|
if def and def.liquidtype == "flowing" or def.liquidtype == "source" then
|
||||||
self._flowing = true
|
|
||||||
|
|
||||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
||||||
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
||||||
|
@ -950,29 +796,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if is_in_water and def.liquidtype == "source" then
|
elseif self._flowing == true then
|
||||||
local cur_vec = self.object:get_velocity()
|
|
||||||
-- apply some acceleration in the opposite direction so it doesn't slide forever
|
|
||||||
local vec = {
|
|
||||||
x = 0 -cur_vec.x*0.9,
|
|
||||||
y = 3 -cur_vec.y*0.9,
|
|
||||||
z = 0 -cur_vec.z*0.9}
|
|
||||||
self.object:set_acceleration(vec)
|
|
||||||
-- slow down the item in water
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
if vel.y < 0 then
|
|
||||||
vel.y = vel.y * 0.9
|
|
||||||
end
|
|
||||||
self.object:set_velocity(vel)
|
|
||||||
if self.physical_state ~= false or self._flowing ~= true then
|
|
||||||
self.physical_state = true
|
|
||||||
self._flowing = true
|
|
||||||
self.object:set_properties({
|
|
||||||
physical = true
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif self._flowing == true and not is_in_water and not is_floating then
|
|
||||||
-- Disable flowing physics if not on/in flowing liquid
|
-- Disable flowing physics if not on/in flowing liquid
|
||||||
self._flowing = false
|
self._flowing = false
|
||||||
enable_physics(self.object, self, true)
|
enable_physics(self.object, self, true)
|
||||||
|
@ -981,34 +805,28 @@ minetest.register_entity(":__builtin:item", {
|
||||||
|
|
||||||
-- If node is not registered or node is walkably solid and resting on nodebox
|
-- If node is not registered or node is walkably solid and resting on nodebox
|
||||||
local nn = minetest.get_node({x=p.x, y=p.y-0.5, z=p.z}).name
|
local nn = minetest.get_node({x=p.x, y=p.y-0.5, z=p.z}).name
|
||||||
local def = minetest.registered_nodes[nn]
|
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
local is_on_floor = def and (def.walkable
|
|
||||||
and not def.groups.slippery and v.y == 0)
|
|
||||||
|
|
||||||
if not minetest.registered_nodes[nn]
|
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and not minetest.registered_nodes[nn].groups.slippery and v.y == 0 then
|
||||||
or is_floating or is_on_floor then
|
if self.physical_state then
|
||||||
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
|
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
|
||||||
-- Merge with close entities of the same item
|
-- Merge with close entities of the same item
|
||||||
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
|
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
|
||||||
local obj = object:get_luaentity()
|
local obj = object:get_luaentity()
|
||||||
if obj and obj.name == "__builtin:item"
|
if obj and obj.name == "__builtin:item"
|
||||||
and obj.physical_state == false then
|
and obj.physical_state == false then
|
||||||
if self:try_merge_with(own_stack, object, obj) then
|
if self:try_merge_with(own_stack, object, obj) then
|
||||||
return
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- don't disable if underwater
|
disable_physics(self.object, self)
|
||||||
if not is_in_water then
|
|
||||||
disable_physics(self.object, self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if self._magnet_active == false and not is_floating then
|
if self._magnet_active == false then
|
||||||
enable_physics(self.object, self)
|
enable_physics(self.object, self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Note: on_punch intentionally left out. The player should *not* be able to collect items by punching
|
-- Note: on_punch intentionally left out. The player should *not* be able to collect items by punching
|
||||||
|
|
|
@ -677,8 +677,7 @@ register_minecart(
|
||||||
{ "mcl_chests_normal.png", "mcl_minecarts_minecart.png" },
|
{ "mcl_chests_normal.png", "mcl_minecarts_minecart.png" },
|
||||||
"mcl_minecarts_minecart_chest.png",
|
"mcl_minecarts_minecart_chest.png",
|
||||||
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
||||||
nil, nil, true)
|
nil, nil, false)
|
||||||
mcl_entity_invs.register_inv("mcl_minecarts:chest_minecart","Minecart",27,false,true)
|
|
||||||
|
|
||||||
-- Minecart with Furnace
|
-- Minecart with Furnace
|
||||||
register_minecart(
|
register_minecart(
|
||||||
|
@ -731,7 +730,7 @@ register_minecart(
|
||||||
"mcl_minecarts_minecart.png",
|
"mcl_minecarts_minecart.png",
|
||||||
}})
|
}})
|
||||||
end
|
end
|
||||||
end, nil, true
|
end, nil, false
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Minecart with Command Block
|
-- Minecart with Command Block
|
||||||
|
@ -770,9 +769,8 @@ register_minecart(
|
||||||
},
|
},
|
||||||
"mcl_minecarts_minecart_hopper.png",
|
"mcl_minecarts_minecart_hopper.png",
|
||||||
{"mcl_minecarts:minecart", "mcl_hoppers:hopper"},
|
{"mcl_minecarts:minecart", "mcl_hoppers:hopper"},
|
||||||
nil, nil, true
|
nil, nil, false
|
||||||
)
|
)
|
||||||
mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true)
|
|
||||||
|
|
||||||
-- Minecart with TNT
|
-- Minecart with TNT
|
||||||
register_minecart(
|
register_minecart(
|
||||||
|
@ -832,7 +830,8 @@ minetest.register_craft({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||||
|
--[[minetest.register_craft({
|
||||||
output = "mcl_minecarts:furnace_minecart",
|
output = "mcl_minecarts:furnace_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_furnaces:furnace"},
|
{"mcl_furnaces:furnace"},
|
||||||
|
@ -848,18 +847,18 @@ minetest.register_craft({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:chest_minecart",
|
output = "mcl_minecarts:chest_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_chests:chest"},
|
{"mcl_chests:chest"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})]]
|
||||||
|
|
||||||
|
|
||||||
if has_mcl_wip then
|
if has_mcl_wip then
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
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:furnace_minecart")
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||||
|
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,4 +33,4 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
||||||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||||
Sneak to dismount=Se baisser pour descendre
|
Sneak to dismount=
|
|
@ -1,36 +0,0 @@
|
||||||
# textdomain: mcl_minecarts
|
|
||||||
Minecart=トロッコ
|
|
||||||
Minecarts can be used for a quick transportion on rails.=トロッコは、レールを使った高速輸送を可能にします。
|
|
||||||
Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type.=トロッコはレール上にしか乗らない為、常に線路に沿って走ります。直進できない丁字路では、取り敢えず左折します。速度はレールの種類によって異なります。
|
|
||||||
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=レールの上にトロッコを置けます。右クリックで乗り込みます。パンチすると動き出します。
|
|
||||||
To obtain the minecart, punch it while holding down the sneak key.=トロッコを入手するには、スニークキーを押しながらパンチします。
|
|
||||||
A minecart with TNT is an explosive vehicle that travels on rail.=TNT付きトロッコは、レール上を行きかう爆薬車両です。
|
|
||||||
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=レール上に配置。パンチで移動。TNTが着火するのは、火打石と打金を使った時か、稼動中のアクティベーターレール上にトロッコが乗った時です。
|
|
||||||
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=トロッコとTNTを入手するには、スニークキーを押しながらパンチしてください。TNTに火が着いていた場合は、無理です。
|
|
||||||
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=かまど付きトロッコは、レール上を走行する車両です。燃料で自走できます。
|
|
||||||
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=レールの上に置きます。石炭を与えると、かまどが長時間燃え続け、トロッコが自走可能になります。パンチすると動き出します。
|
|
||||||
To obtain the minecart and furnace, punch them while holding down the sneak key.=トロッコとかまどを入手するには、スニークキーを押しながらパンチします。
|
|
||||||
Minecart with Chest=チェスト付きトロッコ
|
|
||||||
Minecart with Furnace=かまど付きトロッコ
|
|
||||||
Minecart with Command Block=コマンドブロック付きトロッコ
|
|
||||||
Minecart with Hopper=ホッパー付きトロッコ
|
|
||||||
Minecart with TNT=TNT付きトロッコ
|
|
||||||
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=地面に置いて線路を作ると、レール同士が自動的につながり、必要に応じてカーブや丁字路、踏切、坂道などに変化します。
|
|
||||||
Rail=レール
|
|
||||||
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=レールを利用して、トロッコの輸送路が敷けます。普通のレールは、摩擦の関係でトロッコが少しずつ減速していきます。
|
|
||||||
Powered Rail=パワードレール
|
|
||||||
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=レールを利用して、トロッコの輸送路が敷けます。パワードレールは、トロッコを加速させたり、ブレーキをかけたりできます。
|
|
||||||
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=レッドストーン動力なしだと、このレールはトロッコにブレーキをかけます。このレールでトロッコを加速させるには、レッドストーン動力を供給してください。
|
|
||||||
Activator Rail=アクティベーターレール
|
|
||||||
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=レールを利用して、トロッコの輸送路が敷けます。アクティベーターレールは、特殊なトロッコを作動させるために使われます。
|
|
||||||
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=このレールでトロッコを作動させるには、レッドストーン動力を与えたレール上にトロッコを送り込みます。
|
|
||||||
Detector Rail=
|
|
||||||
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=レールを利用して、トロッコの輸送路が敷けます。ディテクターレールは、その上にあるトロッコを検知でき、その際レッドストーン機構の動力源となります。
|
|
||||||
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=トロッコを検知してレッドストーン動力を供給するには、レッドストーン導線またはレッドストーン機構に接続し、任意のトロッコをレール上に送り込みます。
|
|
||||||
Track for minecarts=トロッコ用の線路
|
|
||||||
Speed up when powered, slow down when not powered=稼動中は加速、非稼動中は減速
|
|
||||||
Activates minecarts when powered=稼動中はトロッコを作動
|
|
||||||
Emits redstone power when a minecart is detected=トロッコを検知するとレッドストーン動力を放出
|
|
||||||
Vehicle for fast travel on rails=レール上を快速移動するための車両
|
|
||||||
Can be ignited by tools or powered activator rail=道具や稼動中のアクティベーターレールにより着火が可能
|
|
||||||
Sneak to dismount=スニークで降りる
|
|
|
@ -1,5 +1,6 @@
|
||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_entity_invs
|
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier, mcl_wip
|
||||||
|
|
||||||
|
|
|
@ -258,10 +258,7 @@ functions needed for the mob to work properly which contains the following:
|
||||||
'nofollow' Do not follow players when they wield the "follow" item. For mobs (like villagers)
|
'nofollow' Do not follow players when they wield the "follow" item. For mobs (like villagers)
|
||||||
that are bred in a different way.
|
that are bred in a different way.
|
||||||
'pick_up' table of itemstrings the mob will pick up (e.g. for breeding)
|
'pick_up' table of itemstrings the mob will pick up (e.g. for breeding)
|
||||||
'on_pick_up' function that will be called on item pickup - arguments are self and the itementity return a (modified) itemstack
|
'on_pick_up' function that will be called on item pickup - return true to not pickup the item
|
||||||
'custom_visual_size' will not reset visual_size from the base class on reload
|
|
||||||
'noyaw' If true this mob will not automatically change yaw
|
|
||||||
'particlespawners' Table of particlespawners attached to the mob. This is implemented in a coord safe manner i.e. spawners are only sent to players within the player_transfer_distance (and automatically removed). This enables infinitely lived particlespawners.
|
|
||||||
|
|
||||||
mobs:gopath(self,target,callback_arrived) pathfind a way to target and run callback on arrival
|
mobs:gopath(self,target,callback_arrived) pathfind a way to target and run callback on arrival
|
||||||
|
|
||||||
|
@ -436,7 +433,7 @@ true the mob will not spawn.
|
||||||
MineClone 2 extensions
|
MineClone 2 extensions
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
mcl_mobs:spawn_child(pos, mob_type)
|
mobs:spawn_child(pos, mob_type)
|
||||||
|
|
||||||
This function spawns a mob as a child. The parameter mob_type is the
|
This function spawns a mob as a child. The parameter mob_type is the
|
||||||
entitystring of the new mob.
|
entitystring of the new mob.
|
||||||
|
@ -446,7 +443,6 @@ mobs:death_effect(pos, collisionbox)
|
||||||
|
|
||||||
Create death particles at pos with the given collisionbox.
|
Create death particles at pos with the given collisionbox.
|
||||||
|
|
||||||
mcl_mobs.spawn(pos,name/entity name)
|
|
||||||
|
|
||||||
Making Arrows
|
Making Arrows
|
||||||
-------------
|
-------------
|
||||||
|
@ -488,13 +484,13 @@ This function registers a arrow for mobs with the attack type shoot.
|
||||||
Spawn Eggs
|
Spawn Eggs
|
||||||
----------
|
----------
|
||||||
|
|
||||||
mobs:register_egg(name, description, background_color, overlay_color, addegg, no_creative)
|
mobs:register_egg(name, description, background, addegg, no_creative)
|
||||||
|
|
||||||
This function registers a spawn egg which can be used by admin to properly spawn in a mob.
|
This function registers a spawn egg which can be used by admin to properly spawn in a mob.
|
||||||
|
|
||||||
'name' this is the name of your new mob to spawn e.g. "mob:sheep"
|
'name' this is the name of your new mob to spawn e.g. "mob:sheep"
|
||||||
'description' the name of the new egg you are creating e.g. "Spawn Sheep"
|
'description' the name of the new egg you are creating e.g. "Spawn Sheep"
|
||||||
'background_color' and 'overlay_color' define the colors for the texture displayed for the egg in inventory
|
'background'the texture displayed for the egg in inventory
|
||||||
'addegg' would you like an egg image in front of your texture (1 = yes,
|
'addegg' would you like an egg image in front of your texture (1 = yes,
|
||||||
0 = no)
|
0 = no)
|
||||||
'no_creative' when set to true this stops spawn egg appearing in creative
|
'no_creative' when set to true this stops spawn egg appearing in creative
|
||||||
|
|
|
@ -1,352 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
|
|
||||||
local MAX_MOB_NAME_LENGTH = 30
|
|
||||||
local HORNY_TIME = 30
|
|
||||||
local HORNY_AGAIN_TIME = 300
|
|
||||||
local CHILD_GROW_TIME = 60*20
|
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
|
||||||
|
|
||||||
local LOG_MODULE = "[mcl_mobs]"
|
|
||||||
local function mcl_log (message)
|
|
||||||
if LOGGING_ON and message then
|
|
||||||
minetest.log(LOG_MODULE .. " " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- No-op in MCL2 (capturing mobs is not possible).
|
|
||||||
-- Provided for compability with Mobs Redo
|
|
||||||
function mcl_mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, force_take, replacewith)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- No-op in MCL2 (protecting mobs is not possible).
|
|
||||||
function mcl_mobs:protect(self, clicker)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- feeding, taming and breeding (thanks blert2112)
|
|
||||||
function mob_class:feed_tame(clicker, feed_count, breed, tame, notake)
|
|
||||||
if not self.follow then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
-- can eat/tame with item in hand
|
|
||||||
if self.nofollow or self:follow_holding(clicker) then
|
|
||||||
local consume_food = false
|
|
||||||
|
|
||||||
-- tame if not still a baby
|
|
||||||
|
|
||||||
if tame and not self.child then
|
|
||||||
if not self.owner or self.owner == "" then
|
|
||||||
self.tamed = true
|
|
||||||
self.owner = clicker:get_player_name()
|
|
||||||
consume_food = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- increase health
|
|
||||||
|
|
||||||
if self.health < self.hp_max and not consume_food then
|
|
||||||
consume_food = true
|
|
||||||
self.health = math.min(self.health + 4, self.hp_max)
|
|
||||||
|
|
||||||
if self.htimer < 1 then
|
|
||||||
self.htimer = 5
|
|
||||||
end
|
|
||||||
self.object:set_hp(self.health)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- make children grow quicker
|
|
||||||
|
|
||||||
if not consume_food and self.child == true then
|
|
||||||
consume_food = true
|
|
||||||
-- deduct 10% of the time to adulthood
|
|
||||||
self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- breed animals
|
|
||||||
|
|
||||||
if breed and not consume_food and self.hornytimer == 0 and not self.horny then
|
|
||||||
self.food = (self.food or 0) + 1
|
|
||||||
consume_food = true
|
|
||||||
if self.food >= feed_count then
|
|
||||||
self.food = 0
|
|
||||||
self.horny = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:update_tag()
|
|
||||||
-- play a sound if the animal used the item and take the item if not in creative
|
|
||||||
if consume_food then
|
|
||||||
-- don't consume food if clicker is in creative
|
|
||||||
if not minetest.is_creative_enabled(clicker:get_player_name()) and not notake then
|
|
||||||
local item = clicker:get_wielded_item()
|
|
||||||
item:take_item()
|
|
||||||
clicker:set_wielded_item(item)
|
|
||||||
end
|
|
||||||
-- always play the eat sound if food is used, even in creative
|
|
||||||
self:mob_sound("eat", nil, true)
|
|
||||||
|
|
||||||
else
|
|
||||||
-- make sound when the mob doesn't want food
|
|
||||||
self:mob_sound("random", true)
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Spawn a child
|
|
||||||
function mcl_mobs.spawn_child(pos, mob_type)
|
|
||||||
local child = minetest.add_entity(pos, mob_type)
|
|
||||||
if not child then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ent = child:get_luaentity()
|
|
||||||
mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5)
|
|
||||||
|
|
||||||
ent.child = true
|
|
||||||
|
|
||||||
local textures
|
|
||||||
-- using specific child texture (if found)
|
|
||||||
if ent.child_texture then
|
|
||||||
textures = ent.child_texture[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- and resize to half height
|
|
||||||
child:set_properties({
|
|
||||||
textures = textures,
|
|
||||||
visual_size = {
|
|
||||||
x = ent.base_size.x * .5,
|
|
||||||
y = ent.base_size.y * .5,
|
|
||||||
},
|
|
||||||
collisionbox = {
|
|
||||||
ent.base_colbox[1] * .5,
|
|
||||||
ent.base_colbox[2] * .5,
|
|
||||||
ent.base_colbox[3] * .5,
|
|
||||||
ent.base_colbox[4] * .5,
|
|
||||||
ent.base_colbox[5] * .5,
|
|
||||||
ent.base_colbox[6] * .5,
|
|
||||||
},
|
|
||||||
selectionbox = {
|
|
||||||
ent.base_selbox[1] * .5,
|
|
||||||
ent.base_selbox[2] * .5,
|
|
||||||
ent.base_selbox[3] * .5,
|
|
||||||
ent.base_selbox[4] * .5,
|
|
||||||
ent.base_selbox[5] * .5,
|
|
||||||
ent.base_selbox[6] * .5,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
ent.animation = ent._child_animations
|
|
||||||
ent._current_animation = nil
|
|
||||||
ent:set_animation("stand")
|
|
||||||
|
|
||||||
return child
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find two animals of same type and breed if nearby and horny
|
|
||||||
function mob_class:check_breeding()
|
|
||||||
|
|
||||||
--mcl_log("In breed function")
|
|
||||||
-- child takes a long time before growing into adult
|
|
||||||
if self.child == true then
|
|
||||||
|
|
||||||
-- When a child, hornytimer is used to count age until adulthood
|
|
||||||
self.hornytimer = self.hornytimer + 1
|
|
||||||
|
|
||||||
if self.hornytimer >= CHILD_GROW_TIME then
|
|
||||||
|
|
||||||
self.child = false
|
|
||||||
self.hornytimer = 0
|
|
||||||
|
|
||||||
self.object:set_properties({
|
|
||||||
textures = self.base_texture,
|
|
||||||
mesh = self.base_mesh,
|
|
||||||
visual_size = self.base_size,
|
|
||||||
collisionbox = self.base_colbox,
|
|
||||||
selectionbox = self.base_selbox,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- custom function when child grows up
|
|
||||||
if self.on_grown then
|
|
||||||
self.on_grown(self)
|
|
||||||
else
|
|
||||||
-- jump when fully grown so as not to fall into ground
|
|
||||||
self.object:set_velocity({
|
|
||||||
x = 0,
|
|
||||||
y = self.jump_height*3,
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
self.animation = nil
|
|
||||||
local anim = self._current_animation
|
|
||||||
self._current_animation = nil -- Mobs Redo does nothing otherwise
|
|
||||||
self:set_animation(anim)
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- horny animal can mate for HORNY_TIME seconds,
|
|
||||||
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
|
||||||
if self.horny == true
|
|
||||||
and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then
|
|
||||||
|
|
||||||
self.hornytimer = self.hornytimer + 1
|
|
||||||
|
|
||||||
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
|
||||||
self.hornytimer = 0
|
|
||||||
self.horny = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find another same animal who is also horny and mate if nearby
|
|
||||||
if self.horny == true
|
|
||||||
and self.hornytimer <= HORNY_TIME then
|
|
||||||
|
|
||||||
mcl_log("In breed function. All good. Do the magic.")
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
mcl_mobs.effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1)
|
|
||||||
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 3)
|
|
||||||
local num = 0
|
|
||||||
local ent = nil
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
ent = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
-- check for same animal with different colour
|
|
||||||
local canmate = false
|
|
||||||
|
|
||||||
if ent then
|
|
||||||
|
|
||||||
if ent.name == self.name then
|
|
||||||
canmate = true
|
|
||||||
else
|
|
||||||
local entname = string.split(ent.name,":")
|
|
||||||
local selfname = string.split(self.name,":")
|
|
||||||
|
|
||||||
if entname[1] == selfname[1] then
|
|
||||||
entname = string.split(entname[2],"_")
|
|
||||||
selfname = string.split(selfname[2],"_")
|
|
||||||
|
|
||||||
if entname[1] == selfname[1] then
|
|
||||||
canmate = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if canmate then mcl_log("In breed function. Can mate.") end
|
|
||||||
|
|
||||||
if ent
|
|
||||||
and canmate == true
|
|
||||||
and ent.horny == true
|
|
||||||
and ent.hornytimer <= HORNY_TIME then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- found your mate? then have a baby
|
|
||||||
if num > 1 then
|
|
||||||
|
|
||||||
self.hornytimer = HORNY_TIME + 1
|
|
||||||
ent.hornytimer = HORNY_TIME + 1
|
|
||||||
|
|
||||||
-- spawn baby
|
|
||||||
|
|
||||||
|
|
||||||
minetest.after(5, function(parent1, parent2, pos)
|
|
||||||
if not parent1.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not parent2.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_experience.throw_xp(pos, math.random(1, 7))
|
|
||||||
|
|
||||||
-- custom breed function
|
|
||||||
if parent1.on_breed then
|
|
||||||
-- when false, skip going any further
|
|
||||||
if parent1.on_breed(parent1, parent2) == false then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
|
||||||
|
|
||||||
local ent_c = child:get_luaentity()
|
|
||||||
|
|
||||||
|
|
||||||
-- Use texture of one of the parents
|
|
||||||
local p = math.random(1, 2)
|
|
||||||
if p == 1 then
|
|
||||||
ent_c.base_texture = parent1.base_texture
|
|
||||||
else
|
|
||||||
ent_c.base_texture = parent2.base_texture
|
|
||||||
end
|
|
||||||
child:set_properties({
|
|
||||||
textures = ent_c.base_texture
|
|
||||||
})
|
|
||||||
|
|
||||||
-- tamed and owned by parents' owner
|
|
||||||
ent_c.tamed = true
|
|
||||||
ent_c.owner = parent1.owner
|
|
||||||
end, self, ent, pos)
|
|
||||||
|
|
||||||
num = 0
|
|
||||||
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:toggle_sit(clicker,p)
|
|
||||||
if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local particle
|
|
||||||
if not self.order or self.order == "" or self.order == "sit" then
|
|
||||||
particle = "mobs_mc_wolf_icon_roam.png"
|
|
||||||
self.order = "roam"
|
|
||||||
self.state = "stand"
|
|
||||||
self.walk_chance = default_walk_chance
|
|
||||||
self.jump = true
|
|
||||||
self:set_animation("stand")
|
|
||||||
-- TODO: Add sitting model
|
|
||||||
else
|
|
||||||
particle = "mobs_mc_wolf_icon_sit.png"
|
|
||||||
self.order = "sit"
|
|
||||||
self.state = "stand"
|
|
||||||
self.walk_chance = 0
|
|
||||||
self.jump = false
|
|
||||||
if self.animation.sit_start then
|
|
||||||
self:set_animation("sit")
|
|
||||||
else
|
|
||||||
self:set_animation("stand")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local pp = vector.new(0,1.4,0)
|
|
||||||
if p then pp = vector.offset(pp,0,p,0) end
|
|
||||||
-- Display icon to show current order (sit or roam)
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = vector.add(pos, pp),
|
|
||||||
velocity = {x=0,y=0.2,z=0},
|
|
||||||
expirationtime = 1,
|
|
||||||
size = 4,
|
|
||||||
texture = particle,
|
|
||||||
playername = self.owner,
|
|
||||||
glow = minetest.LIGHT_MAX,
|
|
||||||
})
|
|
||||||
end
|
|
|
@ -1,796 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
|
|
||||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
|
||||||
|
|
||||||
-- pathfinding settings
|
|
||||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
|
||||||
local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
|
||||||
|
|
||||||
-- check if daytime and also if mob is docile during daylight hours
|
|
||||||
function mob_class:day_docile()
|
|
||||||
if self.docile_by_day == false then
|
|
||||||
return false
|
|
||||||
elseif self.docile_by_day == true
|
|
||||||
and self.time_of_day > 0.2
|
|
||||||
and self.time_of_day < 0.8 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- attack player/mob
|
|
||||||
function mob_class:do_attack(player)
|
|
||||||
|
|
||||||
if self.state == "attack" or self.state == "die" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.attack = player
|
|
||||||
self.state = "attack"
|
|
||||||
|
|
||||||
-- TODO: Implement war_cry sound without being annoying
|
|
||||||
--if random(0, 100) < 90 then
|
|
||||||
--self:mob_sound("war_cry", true)
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- blast damage to entities nearby
|
|
||||||
local function entity_physics(pos,radius)
|
|
||||||
|
|
||||||
radius = radius * 2
|
|
||||||
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
|
||||||
local obj_pos, dist
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
obj_pos = objs[n]:get_pos()
|
|
||||||
|
|
||||||
dist = vector.distance(pos, obj_pos)
|
|
||||||
if dist < 1 then dist = 1 end
|
|
||||||
|
|
||||||
local damage = math.floor((4 / dist) * radius)
|
|
||||||
local ent = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
-- punches work on entities AND players
|
|
||||||
objs[n]:punch(objs[n], 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = damage},
|
|
||||||
}, pos)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:entity_physics(pos,radius) return entity_physics(pos,radius) end
|
|
||||||
|
|
||||||
local los_switcher = false
|
|
||||||
local height_switcher = false
|
|
||||||
|
|
||||||
-- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3
|
|
||||||
function mob_class:smart_mobs(s, p, dist, dtime)
|
|
||||||
|
|
||||||
local s1 = self.path.lastpos
|
|
||||||
|
|
||||||
local target_pos = self.attack:get_pos()
|
|
||||||
|
|
||||||
-- is it becoming stuck?
|
|
||||||
if math.abs(s1.x - s.x) + math.abs(s1.z - s.z) < .5 then
|
|
||||||
self.path.stuck_timer = self.path.stuck_timer + dtime
|
|
||||||
else
|
|
||||||
self.path.stuck_timer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
|
||||||
|
|
||||||
local use_pathfind = false
|
|
||||||
local has_lineofsight = minetest.line_of_sight(
|
|
||||||
{x = s.x, y = (s.y) + .5, z = s.z},
|
|
||||||
{x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2)
|
|
||||||
|
|
||||||
-- im stuck, search for path
|
|
||||||
if not has_lineofsight then
|
|
||||||
|
|
||||||
if los_switcher == true then
|
|
||||||
use_pathfind = true
|
|
||||||
los_switcher = false
|
|
||||||
end -- cannot see target!
|
|
||||||
else
|
|
||||||
if los_switcher == false then
|
|
||||||
|
|
||||||
los_switcher = true
|
|
||||||
use_pathfind = false
|
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
|
||||||
if not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
|
||||||
end, self)
|
|
||||||
end -- can see target!
|
|
||||||
end
|
|
||||||
|
|
||||||
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
|
||||||
|
|
||||||
use_pathfind = true
|
|
||||||
self.path.stuck_timer = 0
|
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
|
||||||
if not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
|
||||||
end, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
|
||||||
|
|
||||||
use_pathfind = true
|
|
||||||
self.path.stuck_timer = 0
|
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
|
||||||
if not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
|
||||||
end, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then
|
|
||||||
|
|
||||||
if height_switcher then
|
|
||||||
use_pathfind = true
|
|
||||||
height_switcher = false
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if not height_switcher then
|
|
||||||
use_pathfind = false
|
|
||||||
height_switcher = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if use_pathfind then
|
|
||||||
-- lets try find a path, first take care of positions
|
|
||||||
-- since pathfinder is very sensitive
|
|
||||||
local sheight = self.collisionbox[5] - self.collisionbox[2]
|
|
||||||
|
|
||||||
-- round position to center of node to avoid stuck in walls
|
|
||||||
-- also adjust height for player models!
|
|
||||||
s.x = math.floor(s.x + 0.5)
|
|
||||||
s.z = math.floor(s.z + 0.5)
|
|
||||||
|
|
||||||
local ssight, sground = minetest.line_of_sight(s, {
|
|
||||||
x = s.x, y = s.y - 4, z = s.z}, 1)
|
|
||||||
|
|
||||||
-- determine node above ground
|
|
||||||
if not ssight then
|
|
||||||
s.y = sground.y + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local p1 = self.attack:get_pos()
|
|
||||||
|
|
||||||
p1.x = math.floor(p1.x + 0.5)
|
|
||||||
p1.y = math.floor(p1.y + 0.5)
|
|
||||||
p1.z = math.floor(p1.z + 0.5)
|
|
||||||
|
|
||||||
local dropheight = 12
|
|
||||||
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
|
||||||
local jumpheight = 0
|
|
||||||
if self.jump and self.jump_height >= 4 then
|
|
||||||
jumpheight = math.min(math.ceil(self.jump_height / 4), 4)
|
|
||||||
elseif self.stepheight > 0.5 then
|
|
||||||
jumpheight = 1
|
|
||||||
end
|
|
||||||
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
|
||||||
|
|
||||||
self.state = ""
|
|
||||||
self:do_attack(self.attack)
|
|
||||||
|
|
||||||
-- no path found, try something else
|
|
||||||
if not self.path.way then
|
|
||||||
|
|
||||||
self.path.following = false
|
|
||||||
|
|
||||||
-- lets make way by digging/building if not accessible
|
|
||||||
if self.pathfinding == 2 and mobs_griefing then
|
|
||||||
|
|
||||||
-- is player higher than mob?
|
|
||||||
if s.y < p1.y then
|
|
||||||
|
|
||||||
-- build upwards
|
|
||||||
if not minetest.is_protected(s, "") then
|
|
||||||
|
|
||||||
local ndef1 = minetest.registered_nodes[self.standing_in]
|
|
||||||
|
|
||||||
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
|
|
||||||
|
|
||||||
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sheight = math.ceil(self.collisionbox[5]) + 1
|
|
||||||
|
|
||||||
-- assume mob is 2 blocks high so it digs above its head
|
|
||||||
s.y = s.y + sheight
|
|
||||||
|
|
||||||
-- remove one block above to make room to jump
|
|
||||||
if not minetest.is_protected(s, "") then
|
|
||||||
|
|
||||||
local node1 = node_ok(s, "air").name
|
|
||||||
local ndef1 = minetest.registered_nodes[node1]
|
|
||||||
|
|
||||||
if node1 ~= "air"
|
|
||||||
and node1 ~= "ignore"
|
|
||||||
and ndef1
|
|
||||||
and not ndef1.groups.level
|
|
||||||
and not ndef1.groups.unbreakable
|
|
||||||
and not ndef1.groups.liquid then
|
|
||||||
|
|
||||||
minetest.set_node(s, {name = "air"})
|
|
||||||
minetest.add_item(s, ItemStack(node1))
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
s.y = s.y - sheight
|
|
||||||
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z})
|
|
||||||
|
|
||||||
else -- dig 2 blocks to make door toward player direction
|
|
||||||
|
|
||||||
local yaw1 = self.object:get_yaw() + math.pi / 2
|
|
||||||
local p1 = {
|
|
||||||
x = s.x + math.cos(yaw1),
|
|
||||||
y = s.y,
|
|
||||||
z = s.z + math.sin(yaw1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if not minetest.is_protected(p1, "") then
|
|
||||||
|
|
||||||
local node1 = node_ok(p1, "air").name
|
|
||||||
local ndef1 = minetest.registered_nodes[node1]
|
|
||||||
|
|
||||||
if node1 ~= "air"
|
|
||||||
and node1 ~= "ignore"
|
|
||||||
and ndef1
|
|
||||||
and not ndef1.groups.level
|
|
||||||
and not ndef1.groups.unbreakable
|
|
||||||
and not ndef1.groups.liquid then
|
|
||||||
|
|
||||||
minetest.add_item(p1, ItemStack(node1))
|
|
||||||
minetest.set_node(p1, {name = "air"})
|
|
||||||
end
|
|
||||||
|
|
||||||
p1.y = p1.y + 1
|
|
||||||
node1 = node_ok(p1, "air").name
|
|
||||||
ndef1 = minetest.registered_nodes[node1]
|
|
||||||
|
|
||||||
if node1 ~= "air"
|
|
||||||
and node1 ~= "ignore"
|
|
||||||
and ndef1
|
|
||||||
and not ndef1.groups.level
|
|
||||||
and not ndef1.groups.unbreakable
|
|
||||||
and not ndef1.groups.liquid then
|
|
||||||
|
|
||||||
minetest.add_item(p1, ItemStack(node1))
|
|
||||||
minetest.set_node(p1, {name = "air"})
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- will try again in 2 seconds
|
|
||||||
self.path.stuck_timer = stuck_timeout - 2
|
|
||||||
elseif s.y < p1.y and (not self.fly) then
|
|
||||||
self:do_jump() --add jump to pathfinding
|
|
||||||
self.path.following = true
|
|
||||||
-- Yay, I found path!
|
|
||||||
-- TODO: Implement war_cry sound without being annoying
|
|
||||||
--self:mob_sound("war_cry", true)
|
|
||||||
else
|
|
||||||
self:set_velocity(self.walk_velocity)
|
|
||||||
|
|
||||||
-- follow path now that it has it
|
|
||||||
self.path.following = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- specific attacks
|
|
||||||
local specific_attack = function(list, what)
|
|
||||||
|
|
||||||
-- no list so attack default (player, animals etc.)
|
|
||||||
if list == nil then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- found entity on list to attack?
|
|
||||||
for no = 1, #list do
|
|
||||||
|
|
||||||
if list[no] == what then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find someone to attack
|
|
||||||
function mob_class:monster_attack()
|
|
||||||
if not damage_enabled
|
|
||||||
or self.passive ~= false
|
|
||||||
or self.state == "attack"
|
|
||||||
or self:day_docile() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local p, sp, dist
|
|
||||||
local player, obj, min_player
|
|
||||||
local type, name = "", ""
|
|
||||||
local min_dist = self.view_range + 1
|
|
||||||
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
|
||||||
local blacklist_attack = {}
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
if not objs[n]:is_player() then
|
|
||||||
obj = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
if obj then
|
|
||||||
player = obj.object
|
|
||||||
name = obj.name or ""
|
|
||||||
end
|
|
||||||
if obj and obj.type == self.type and obj.passive == false and obj.state == "attack" and obj.attack then
|
|
||||||
table.insert(blacklist_attack, obj.attack)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
|
|
||||||
if objs[n]:is_player() then
|
|
||||||
if mcl_mobs.invis[ objs[n]:get_player_name() ] or (not self:object_in_range(objs[n])) then
|
|
||||||
type = ""
|
|
||||||
elseif (self.type == "monster" or self._aggro) then
|
|
||||||
player = objs[n]
|
|
||||||
type = "player"
|
|
||||||
name = "player"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
obj = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
if obj then
|
|
||||||
player = obj.object
|
|
||||||
type = obj.type
|
|
||||||
name = obj.name or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find specific mob to attack, failing that attack player/npc/animal
|
|
||||||
if specific_attack(self.specific_attack, name)
|
|
||||||
and (type == "player" or ( type == "npc" and self.attack_npcs )
|
|
||||||
or (type == "animal" and self.attack_animals == true)) then
|
|
||||||
|
|
||||||
p = player:get_pos()
|
|
||||||
sp = s
|
|
||||||
|
|
||||||
dist = vector.distance(p, s)
|
|
||||||
|
|
||||||
-- aim higher to make looking up hills more realistic
|
|
||||||
p.y = p.y + 1
|
|
||||||
sp.y = sp.y + 1
|
|
||||||
|
|
||||||
local attacked_p = false
|
|
||||||
for c=1, #blacklist_attack do
|
|
||||||
if blacklist_attack[c] == player then
|
|
||||||
attacked_p = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- choose closest player to attack
|
|
||||||
if dist < min_dist
|
|
||||||
and not attacked_p
|
|
||||||
and self:line_of_sight( sp, p, 2) == true then
|
|
||||||
min_dist = dist
|
|
||||||
min_player = player
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not min_player and #blacklist_attack > 0 then
|
|
||||||
min_player=blacklist_attack[math.random(#blacklist_attack)]
|
|
||||||
end
|
|
||||||
-- attack player
|
|
||||||
if min_player then
|
|
||||||
self:do_attack(min_player)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- npc, find closest monster to attack
|
|
||||||
function mob_class:npc_attack()
|
|
||||||
|
|
||||||
if self.type ~= "npc"
|
|
||||||
or not self.attacks_monsters
|
|
||||||
or self.state == "attack" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local p, sp, obj, min_player
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local min_dist = self.view_range + 1
|
|
||||||
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
obj = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
if obj and obj.type == "monster" then
|
|
||||||
|
|
||||||
p = obj.object:get_pos()
|
|
||||||
sp = s
|
|
||||||
|
|
||||||
local dist = vector.distance(p, s)
|
|
||||||
|
|
||||||
-- aim higher to make looking up hills more realistic
|
|
||||||
p.y = p.y + 1
|
|
||||||
sp.y = sp.y + 1
|
|
||||||
|
|
||||||
if dist < min_dist
|
|
||||||
and self:line_of_sight( sp, p, 2) == true then
|
|
||||||
min_dist = dist
|
|
||||||
min_player = obj.object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if min_player then
|
|
||||||
self:do_attack(min_player)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- dogshoot attack switch and counter function
|
|
||||||
function mob_class:dogswitch(dtime)
|
|
||||||
|
|
||||||
-- switch mode not activated
|
|
||||||
if not self.dogshoot_switch
|
|
||||||
or not dtime then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self.dogshoot_count = self.dogshoot_count + dtime
|
|
||||||
|
|
||||||
if (self.dogshoot_switch == 1
|
|
||||||
and self.dogshoot_count > self.dogshoot_count_max)
|
|
||||||
or (self.dogshoot_switch == 2
|
|
||||||
and self.dogshoot_count > self.dogshoot_count2_max) then
|
|
||||||
|
|
||||||
self.dogshoot_count = 0
|
|
||||||
|
|
||||||
if self.dogshoot_switch == 1 then
|
|
||||||
self.dogshoot_switch = 2
|
|
||||||
else
|
|
||||||
self.dogshoot_switch = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return self.dogshoot_switch
|
|
||||||
end
|
|
||||||
|
|
||||||
-- no damage to nodes explosion
|
|
||||||
function mob_class:safe_boom(pos, strength)
|
|
||||||
minetest.sound_play(self.sounds and self.sounds.explode or "tnt_explode", {
|
|
||||||
pos = pos,
|
|
||||||
gain = 1.0,
|
|
||||||
max_hear_distance = self.sounds and self.sounds.distance or 32
|
|
||||||
}, true)
|
|
||||||
local radius = strength
|
|
||||||
entity_physics(pos, radius)
|
|
||||||
mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", radius * 3, radius * 5, radius, 1, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- make explosion with protection and tnt mod check
|
|
||||||
function mob_class:boom(pos, strength, fire)
|
|
||||||
if mobs_griefing and not minetest.is_protected(pos, "") then
|
|
||||||
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
|
|
||||||
else
|
|
||||||
mcl_mobs.mob_class.safe_boom(self, pos, strength) --need to call it this way bc self is the "arrow" object here
|
|
||||||
end
|
|
||||||
|
|
||||||
-- delete the object after it punched the player to avoid nil entities in e.g. mcl_shields!!
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- deal damage and effects when mob punched
|
|
||||||
function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|
||||||
|
|
||||||
-- custom punch function
|
|
||||||
if self.do_punch then
|
|
||||||
|
|
||||||
-- when false skip going any further
|
|
||||||
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- error checking when mod profiling is enabled
|
|
||||||
if not tool_capabilities then
|
|
||||||
minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_player = hitter:is_player()
|
|
||||||
|
|
||||||
if is_player then
|
|
||||||
-- is mob protected?
|
|
||||||
if self.protected and minetest.is_protected(self.object:get_pos(), hitter:get_player_name()) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.is_creative_enabled(hitter:get_player_name()) then
|
|
||||||
self.health = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set/update 'drop xp' timestamp if hitted by player
|
|
||||||
self.xp_timestamp = minetest.get_us_time()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- punch interval
|
|
||||||
local weapon = hitter:get_wielded_item()
|
|
||||||
local punch_interval = 1.4
|
|
||||||
|
|
||||||
-- exhaust attacker
|
|
||||||
if is_player then
|
|
||||||
mcl_hunger.exhaust(hitter:get_player_name(), mcl_hunger.EXHAUST_ATTACK)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- calculate mob damage
|
|
||||||
local damage = 0
|
|
||||||
local armor = self.object:get_armor_groups() or {}
|
|
||||||
local tmp
|
|
||||||
|
|
||||||
-- quick error check incase it ends up 0 (serialize.h check test)
|
|
||||||
if tflp == 0 then
|
|
||||||
tflp = 0.2
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
|
||||||
|
|
||||||
tmp = tflp / (tool_capabilities.full_punch_interval or 1.4)
|
|
||||||
|
|
||||||
if tmp < 0 then
|
|
||||||
tmp = 0.0
|
|
||||||
elseif tmp > 1 then
|
|
||||||
tmp = 1.0
|
|
||||||
end
|
|
||||||
|
|
||||||
damage = damage + (tool_capabilities.damage_groups[group] or 0)
|
|
||||||
* tmp * ((armor[group] or 0) / 100.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
if weapon then
|
|
||||||
local fire_aspect_level = mcl_enchanting.get_enchantment(weapon, "fire_aspect")
|
|
||||||
if fire_aspect_level > 0 then
|
|
||||||
mcl_burning.set_on_fire(self.object, fire_aspect_level * 4)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for tool immunity or special damage
|
|
||||||
for n = 1, #self.immune_to do
|
|
||||||
|
|
||||||
if self.immune_to[n][1] == weapon:get_name() then
|
|
||||||
|
|
||||||
damage = self.immune_to[n][2] or 0
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- healing
|
|
||||||
if damage <= -1 then
|
|
||||||
self.health = self.health - math.floor(damage)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if tool_capabilities then
|
|
||||||
punch_interval = tool_capabilities.full_punch_interval or 1.4
|
|
||||||
end
|
|
||||||
|
|
||||||
-- add weapon wear manually
|
|
||||||
-- Required because we have custom health handling ("health" property)
|
|
||||||
if minetest.is_creative_enabled("") ~= true
|
|
||||||
and tool_capabilities then
|
|
||||||
if tool_capabilities.punch_attack_uses then
|
|
||||||
-- Without this delay, the wear does not work. Quite hacky ...
|
|
||||||
minetest.after(0, function(name)
|
|
||||||
local player = minetest.get_player_by_name(name)
|
|
||||||
if not player then return end
|
|
||||||
local weapon = hitter:get_wielded_item(player)
|
|
||||||
local def = weapon:get_definition()
|
|
||||||
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
|
||||||
local wear = math.floor(65535/tool_capabilities.punch_attack_uses)
|
|
||||||
weapon:add_wear(wear)
|
|
||||||
hitter:set_wielded_item(weapon)
|
|
||||||
end
|
|
||||||
end, hitter:get_player_name())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local die = false
|
|
||||||
|
|
||||||
|
|
||||||
if damage >= 0 then
|
|
||||||
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
|
||||||
if damage >= 0.1 then
|
|
||||||
-- weapon sounds
|
|
||||||
if weapon:get_definition().sounds ~= nil then
|
|
||||||
|
|
||||||
local s = math.random(0, #weapon:get_definition().sounds)
|
|
||||||
|
|
||||||
minetest.sound_play(weapon:get_definition().sounds[s], {
|
|
||||||
object = self.object, --hitter,
|
|
||||||
max_hear_distance = 8
|
|
||||||
}, true)
|
|
||||||
else
|
|
||||||
minetest.sound_play("default_punch", {
|
|
||||||
object = self.object,
|
|
||||||
max_hear_distance = 5
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:damage_effect(damage)
|
|
||||||
|
|
||||||
-- do damage
|
|
||||||
self.health = self.health - damage
|
|
||||||
|
|
||||||
-- skip future functions if dead, except alerting others
|
|
||||||
if self:check_for_death( "hit", {type = "punch", puncher = hitter}) then
|
|
||||||
die = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- knock back effect (only on full punch)
|
|
||||||
if self.knock_back
|
|
||||||
and tflp >= punch_interval then
|
|
||||||
-- direction error check
|
|
||||||
dir = dir or {x = 0, y = 0, z = 0}
|
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if not v then return end
|
|
||||||
local r = 1.4 - math.min(punch_interval, 1.4)
|
|
||||||
local kb = r * (math.abs(v.x)+math.abs(v.z))
|
|
||||||
local up = 2
|
|
||||||
|
|
||||||
if die==true then
|
|
||||||
kb=kb*2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if already in air then dont go up anymore when hit
|
|
||||||
if math.abs(v.y) > 0.1
|
|
||||||
or self.fly then
|
|
||||||
up = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- check if tool already has specific knockback value
|
|
||||||
if tool_capabilities.damage_groups["knockback"] then
|
|
||||||
kb = tool_capabilities.damage_groups["knockback"]
|
|
||||||
else
|
|
||||||
kb = kb * 1.5
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local luaentity
|
|
||||||
if hitter then
|
|
||||||
luaentity = hitter:get_luaentity()
|
|
||||||
end
|
|
||||||
if hitter and is_player then
|
|
||||||
local wielditem = hitter:get_wielded_item()
|
|
||||||
kb = kb + 3 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
|
||||||
elseif luaentity and luaentity._knockback then
|
|
||||||
kb = kb + luaentity._knockback
|
|
||||||
end
|
|
||||||
self._kb_turn = true
|
|
||||||
self._turn_to=self.object:get_yaw()-1.57
|
|
||||||
self.frame_speed_multiplier=2.3
|
|
||||||
if self.animation.run_end then
|
|
||||||
self:set_animation( "run")
|
|
||||||
elseif self.animation.walk_end then
|
|
||||||
self:set_animation( "walk")
|
|
||||||
end
|
|
||||||
minetest.after(0.2, function()
|
|
||||||
if self and self.object then
|
|
||||||
self.frame_speed_multiplier=1
|
|
||||||
self._kb_turn = false
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
self.object:add_velocity({
|
|
||||||
x = dir.x * kb,
|
|
||||||
y = up*2,
|
|
||||||
z = dir.z * kb
|
|
||||||
})
|
|
||||||
|
|
||||||
self.pause_timer = 0.25
|
|
||||||
end
|
|
||||||
end -- END if damage
|
|
||||||
|
|
||||||
-- if skittish then run away
|
|
||||||
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
|
||||||
|
|
||||||
local yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
|
||||||
minetest.after(0.2,function()
|
|
||||||
if self and self.object and self.object:get_pos() and hitter and is_player and hitter:get_pos() then
|
|
||||||
yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
|
||||||
self:set_velocity( self.run_velocity)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
self.state = "runaway"
|
|
||||||
self.runaway_timer = 0
|
|
||||||
self.following = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = hitter:get_player_name() or ""
|
|
||||||
|
|
||||||
-- attack puncher and call other mobs for help
|
|
||||||
if self.passive == false
|
|
||||||
and self.state ~= "flop"
|
|
||||||
and (self.child == false or self.type == "monster")
|
|
||||||
and hitter:get_player_name() ~= self.owner
|
|
||||||
and not mcl_mobs.invis[ name ] then
|
|
||||||
if not die then
|
|
||||||
-- attack whoever punched mob
|
|
||||||
self.state = ""
|
|
||||||
self:do_attack(hitter)
|
|
||||||
self._aggro= true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- alert others to the attack
|
|
||||||
local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range)
|
|
||||||
local obj = nil
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
obj = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
if obj then
|
|
||||||
-- only alert members of same mob or friends
|
|
||||||
if obj.group_attack
|
|
||||||
and obj.state ~= "attack"
|
|
||||||
and obj.owner ~= name then
|
|
||||||
if obj.name == self.name then
|
|
||||||
obj:do_attack(hitter)
|
|
||||||
elseif type(obj.group_attack) == "table" then
|
|
||||||
for i=1, #obj.group_attack do
|
|
||||||
if obj.name == obj.group_attack[i] then
|
|
||||||
obj._aggro = true
|
|
||||||
obj:do_attack(hitter)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- have owned mobs attack player threat
|
|
||||||
if obj.owner == name and obj.owner_loyal then
|
|
||||||
obj:do_attack(self.object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_aggro(dtime)
|
|
||||||
if not self._aggro or not self.attack then return end
|
|
||||||
if not self._check_aggro_timer or self._check_aggro_timer > 5 then
|
|
||||||
self._check_aggro_timer = 0
|
|
||||||
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
|
||||||
self._aggro = nil
|
|
||||||
self.attack = nil
|
|
||||||
self.state = "stand"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self._check_aggro_timer = self._check_aggro_timer + dtime
|
|
||||||
end
|
|
|
@ -1,32 +0,0 @@
|
||||||
|
|
||||||
-- this is to make the register_mob and register egg functions commonly used by mods not break
|
|
||||||
-- when they use the weird old : notation AND self as first argument
|
|
||||||
local oldregmob = mcl_mobs.register_mob
|
|
||||||
function mcl_mobs.register_mob(self,name,def)
|
|
||||||
if type(self) == "string" then
|
|
||||||
def = name
|
|
||||||
name = self
|
|
||||||
end
|
|
||||||
return oldregmob(name,def)
|
|
||||||
end
|
|
||||||
local oldregegg = mcl_mobs.register_egg
|
|
||||||
function mcl_mobs.register_egg(self, mob, desc, background_color, overlay_color, addegg, no_creative)
|
|
||||||
if type(self) == "string" then
|
|
||||||
no_creative = addegg
|
|
||||||
addegg = overlay_color
|
|
||||||
overlay_color = background_color
|
|
||||||
background_color = desc
|
|
||||||
desc = mob
|
|
||||||
mob = self
|
|
||||||
end
|
|
||||||
return oldregegg(mob, desc, background_color, overlay_color, addegg, no_creative)
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldregarrow = mcl_mobs.register_mob
|
|
||||||
function mcl_mobs.register_mob(self,name,def)
|
|
||||||
if type(self) == "string" then
|
|
||||||
def = name
|
|
||||||
name = self
|
|
||||||
end
|
|
||||||
return oldregarrow(name,def)
|
|
||||||
end
|
|
|
@ -1,384 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
local active_particlespawners = {}
|
|
||||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
|
||||||
|
|
||||||
local player_transfer_distance = tonumber(minetest.settings:get("player_transfer_distance")) or 128
|
|
||||||
if player_transfer_distance == 0 then player_transfer_distance = math.huge end
|
|
||||||
|
|
||||||
|
|
||||||
-- custom particle effects
|
|
||||||
function mcl_mobs.effect(pos, amount, texture, min_size, max_size, radius, gravity, glow, go_down)
|
|
||||||
|
|
||||||
radius = radius or 2
|
|
||||||
min_size = min_size or 0.5
|
|
||||||
max_size = max_size or 1
|
|
||||||
gravity = gravity or DEFAULT_FALL_SPEED
|
|
||||||
glow = glow or 0
|
|
||||||
go_down = go_down or false
|
|
||||||
|
|
||||||
local ym
|
|
||||||
if go_down then
|
|
||||||
ym = 0
|
|
||||||
else
|
|
||||||
ym = -radius
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = amount,
|
|
||||||
time = 0.25,
|
|
||||||
minpos = pos,
|
|
||||||
maxpos = pos,
|
|
||||||
minvel = {x = -radius, y = ym, z = -radius},
|
|
||||||
maxvel = {x = radius, y = radius, z = radius},
|
|
||||||
minacc = {x = 0, y = gravity, z = 0},
|
|
||||||
maxacc = {x = 0, y = gravity, z = 0},
|
|
||||||
minexptime = 0.1,
|
|
||||||
maxexptime = 1,
|
|
||||||
minsize = min_size,
|
|
||||||
maxsize = max_size,
|
|
||||||
texture = texture,
|
|
||||||
glow = glow,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.death_effect(pos, yaw, collisionbox, rotate)
|
|
||||||
local min, max
|
|
||||||
if collisionbox then
|
|
||||||
min = {x=collisionbox[1], y=collisionbox[2], z=collisionbox[3]}
|
|
||||||
max = {x=collisionbox[4], y=collisionbox[5], z=collisionbox[6]}
|
|
||||||
else
|
|
||||||
min = { x = -0.5, y = 0, z = -0.5 }
|
|
||||||
max = { x = 0.5, y = 0.5, z = 0.5 }
|
|
||||||
end
|
|
||||||
if rotate then
|
|
||||||
min = vector.rotate(min, {x=0, y=yaw, z=math.pi/2})
|
|
||||||
max = vector.rotate(max, {x=0, y=yaw, z=math.pi/2})
|
|
||||||
min, max = vector.sort(min, max)
|
|
||||||
min = vector.multiply(min, 0.5)
|
|
||||||
max = vector.multiply(max, 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 50,
|
|
||||||
time = 0.001,
|
|
||||||
minpos = vector.add(pos, min),
|
|
||||||
maxpos = vector.add(pos, max),
|
|
||||||
minvel = vector.new(-5,-5,-5),
|
|
||||||
maxvel = vector.new(5,5,5),
|
|
||||||
minexptime = 1.1,
|
|
||||||
maxexptime = 1.5,
|
|
||||||
minsize = 1,
|
|
||||||
maxsize = 2,
|
|
||||||
collisiondetection = false,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_mob_death.png^[colorize:#000000:255",
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.sound_play("mcl_mobs_mob_poof", {
|
|
||||||
pos = pos,
|
|
||||||
gain = 1.0,
|
|
||||||
max_hear_distance = 8,
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- play sound
|
|
||||||
function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
|
|
||||||
|
|
||||||
local soundinfo
|
|
||||||
if self.sounds_child and self.child then
|
|
||||||
soundinfo = self.sounds_child
|
|
||||||
elseif self.sounds then
|
|
||||||
soundinfo = self.sounds
|
|
||||||
end
|
|
||||||
if not soundinfo then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local sound = soundinfo[soundname]
|
|
||||||
if sound then
|
|
||||||
if is_opinion and self.opinion_sound_cooloff > 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local pitch
|
|
||||||
if not fixed_pitch then
|
|
||||||
local base_pitch = soundinfo.base_pitch
|
|
||||||
if not base_pitch then
|
|
||||||
base_pitch = 1
|
|
||||||
end
|
|
||||||
if self.child and (not self.sounds_child) then
|
|
||||||
-- Children have higher pitch
|
|
||||||
pitch = base_pitch * 1.5
|
|
||||||
else
|
|
||||||
pitch = base_pitch
|
|
||||||
end
|
|
||||||
-- randomize the pitch a bit
|
|
||||||
pitch = pitch + math.random(-10, 10) * 0.005
|
|
||||||
end
|
|
||||||
minetest.sound_play(sound, {
|
|
||||||
object = self.object,
|
|
||||||
gain = 1.0,
|
|
||||||
max_hear_distance = self.sounds.distance,
|
|
||||||
pitch = pitch,
|
|
||||||
}, true)
|
|
||||||
self.opinion_sound_cooloff = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:add_texture_mod(mod)
|
|
||||||
local full_mod = ""
|
|
||||||
local already_added = false
|
|
||||||
for i=1, #self.texture_mods do
|
|
||||||
if mod == self.texture_mods[i] then
|
|
||||||
already_added = true
|
|
||||||
end
|
|
||||||
full_mod = full_mod .. self.texture_mods[i]
|
|
||||||
end
|
|
||||||
if not already_added then
|
|
||||||
full_mod = full_mod .. mod
|
|
||||||
table.insert(self.texture_mods, mod)
|
|
||||||
end
|
|
||||||
self.object:set_texture_mod(full_mod)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:remove_texture_mod(mod)
|
|
||||||
local full_mod = ""
|
|
||||||
local remove = {}
|
|
||||||
for i=1, #self.texture_mods do
|
|
||||||
if self.texture_mods[i] ~= mod then
|
|
||||||
full_mod = full_mod .. self.texture_mods[i]
|
|
||||||
else
|
|
||||||
table.insert(remove, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i=#remove, 1 do
|
|
||||||
table.remove(self.texture_mods, remove[i])
|
|
||||||
end
|
|
||||||
self.object:set_texture_mod(full_mod)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:damage_effect(damage)
|
|
||||||
-- damage particles
|
|
||||||
if (not disable_blood) and damage > 0 then
|
|
||||||
|
|
||||||
local amount_large = math.floor(damage / 2)
|
|
||||||
local amount_small = damage % 2
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
pos.y = pos.y + (self.collisionbox[5] - self.collisionbox[2]) * .5
|
|
||||||
|
|
||||||
local texture = "mobs_blood.png"
|
|
||||||
-- full heart damage (one particle for each 2 HP damage)
|
|
||||||
if amount_large > 0 then
|
|
||||||
mcl_mobs.effect(pos, amount_large, texture, 2, 2, 1.75, 0, nil, true)
|
|
||||||
end
|
|
||||||
-- half heart damage (one additional particle if damage is an odd number)
|
|
||||||
if amount_small > 0 then
|
|
||||||
-- TODO: Use "half heart"
|
|
||||||
mcl_mobs.effect(pos, amount_small, texture, 1, 1, 1.75, 0, nil, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:remove_particlespawners(pn)
|
|
||||||
if not active_particlespawners[pn] then return end
|
|
||||||
if not active_particlespawners[pn][self.object] then return end
|
|
||||||
for k,v in pairs(active_particlespawners[pn][self.object]) do
|
|
||||||
minetest.delete_particlespawner(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:add_particlespawners(pn)
|
|
||||||
if not active_particlespawners[pn] then active_particlespawners[pn] = {} end
|
|
||||||
if not active_particlespawners[pn][self.object] then active_particlespawners[pn][self.object] = {} end
|
|
||||||
for _,ps in pairs(self.particlespawners) do
|
|
||||||
ps.attached = self.object
|
|
||||||
ps.playername = pn
|
|
||||||
table.insert(active_particlespawners[pn][self.object],minetest.add_particlespawner(ps))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_particlespawners(dtime)
|
|
||||||
if not self.particlespawners then return end
|
|
||||||
--minetest.log(dump(active_particlespawners))
|
|
||||||
if self._particle_timer and self._particle_timer >= 1 then
|
|
||||||
self._particle_timer = 0
|
|
||||||
local players = {}
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
local pn = player:get_player_name()
|
|
||||||
table.insert(players,pn)
|
|
||||||
if not active_particlespawners[pn] then
|
|
||||||
active_particlespawners[pn] = {} end
|
|
||||||
|
|
||||||
local dst = vector.distance(player:get_pos(),self.object:get_pos())
|
|
||||||
if dst < player_transfer_distance and not active_particlespawners[pn][self.object] then
|
|
||||||
self:add_particlespawners(pn)
|
|
||||||
elseif dst >= player_transfer_distance and active_particlespawners[pn][self.object] then
|
|
||||||
self:remove_particlespawners(pn)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif not self._particle_timer then
|
|
||||||
self._particle_timer = 0
|
|
||||||
end
|
|
||||||
self._particle_timer = self._particle_timer + dtime
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- set defined animation
|
|
||||||
function mob_class:set_animation(anim, fixed_frame)
|
|
||||||
if not self.animation or not anim then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.jockey then
|
|
||||||
anim = "jockey"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if self:flight_check() and self.fly and anim == "walk" then anim = "fly" end
|
|
||||||
|
|
||||||
self._current_animation = self._current_animation or ""
|
|
||||||
|
|
||||||
if (anim == self._current_animation
|
|
||||||
or not self.animation[anim .. "_start"]
|
|
||||||
or not self.animation[anim .. "_end"]) and self.state ~= "die" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self._current_animation = anim
|
|
||||||
|
|
||||||
local a_start = self.animation[anim .. "_start"]
|
|
||||||
local a_end
|
|
||||||
if fixed_frame then
|
|
||||||
a_end = a_start
|
|
||||||
else
|
|
||||||
a_end = self.animation[anim .. "_end"]
|
|
||||||
end
|
|
||||||
if a_start and a_end then
|
|
||||||
self.object:set_animation({
|
|
||||||
x = a_start,
|
|
||||||
y = a_end},
|
|
||||||
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
|
||||||
0, self.animation[anim .. "_loop"] ~= false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- above function exported for mount.lua
|
|
||||||
function mcl_mobs:set_animation(self, anim)
|
|
||||||
self:set_animation(anim)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dir_to_pitch(dir)
|
|
||||||
--local dir2 = vector.normalize(dir)
|
|
||||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
|
||||||
return -math.atan2(-dir.y, xz)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_head_swivel(dtime)
|
|
||||||
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
|
|
||||||
local final_rotation = vector.new(0,0,0)
|
|
||||||
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 10)) do
|
|
||||||
if obj:is_player() and not self.attack or obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity() then
|
|
||||||
if not self._locked_object then
|
|
||||||
if math.random(5000/self.curiosity) == 1 or vector.distance(pos,obj:get_pos())<4 and obj:is_player() then
|
|
||||||
self._locked_object = obj
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if math.random(10000/self.curiosity) == 1 then
|
|
||||||
self._locked_object = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.attack or self.following then
|
|
||||||
self._locked_object = self.attack or self.following
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
|
|
||||||
local _locked_object_eye_height = 1.5
|
|
||||||
if self._locked_object:get_luaentity() then
|
|
||||||
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height
|
|
||||||
end
|
|
||||||
if self._locked_object:is_player() then
|
|
||||||
_locked_object_eye_height = self._locked_object:get_properties().eye_height
|
|
||||||
end
|
|
||||||
if _locked_object_eye_height then
|
|
||||||
local self_rot = self.object:get_rotation()
|
|
||||||
if self.object:get_attach() then
|
|
||||||
self_rot = self.object:get_attach():get_rotation()
|
|
||||||
end
|
|
||||||
if self.rot then
|
|
||||||
local player_pos = self._locked_object:get_pos()
|
|
||||||
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
|
|
||||||
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
|
|
||||||
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
|
|
||||||
|
|
||||||
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
|
|
||||||
final_rotation = vector.multiply(oldr, 0.9)
|
|
||||||
elseif self.attack and self.state == "attack" and not self.runaway then
|
|
||||||
if self.head_yaw == "y" then
|
|
||||||
final_rotation = vector.new(mob_pitch, mob_yaw, 0)
|
|
||||||
elseif self.head_yaw == "z" then
|
|
||||||
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
if self.head_yaw == "y" then
|
|
||||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0)
|
|
||||||
elseif self.head_yaw == "z" then
|
|
||||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
|
|
||||||
final_rotation = vector.multiply(oldr, 0.9)
|
|
||||||
else
|
|
||||||
final_rotation = vector.new(0,0,0)
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), final_rotation)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:set_animation_speed()
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if v then
|
|
||||||
if self.frame_speed_multiplier then
|
|
||||||
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
|
||||||
if not self.animation.walk_speed then
|
|
||||||
self.animation.walk_speed = 25
|
|
||||||
end
|
|
||||||
if math.abs(v.x)+math.abs(v.z) > 0.5 then
|
|
||||||
self.object:set_animation_frame_speed((v2/math.max(1,self.run_velocity))*self.animation.walk_speed*self.frame_speed_multiplier)
|
|
||||||
else
|
|
||||||
self.object:set_animation_frame_speed(25)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--set_speed
|
|
||||||
if self.acc then
|
|
||||||
self.object:add_velocity(self.acc)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
local pn = player:get_player_name()
|
|
||||||
if not active_particlespawners[pn] then return end
|
|
||||||
for _,m in pairs(active_particlespawners[pn]) do
|
|
||||||
for k,v in pairs(m) do
|
|
||||||
minetest.delete_particlespawner(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
active_particlespawners[pn] = nil
|
|
||||||
end)
|
|
|
@ -1,566 +1,14 @@
|
||||||
mcl_mobs = {}
|
|
||||||
mcl_mobs.mob_class = {}
|
local path = minetest.get_modpath(minetest.get_current_modname())
|
||||||
mcl_mobs.mob_class_meta = {__index = mcl_mobs.mob_class}
|
|
||||||
mcl_mobs.registered_mobs = {}
|
-- Mob API
|
||||||
local modname = minetest.get_current_modname()
|
|
||||||
local path = minetest.get_modpath(modname)
|
|
||||||
local S = minetest.get_translator(modname)
|
|
||||||
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
|
||||||
--api and helpers
|
|
||||||
-- effects: sounds and particles mostly
|
|
||||||
dofile(path .. "/effects.lua")
|
|
||||||
-- physics: involuntary mob movement - particularly falling and death
|
|
||||||
dofile(path .. "/physics.lua")
|
|
||||||
-- movement: general voluntary mob movement, walking avoiding cliffs etc.
|
|
||||||
dofile(path .. "/movement.lua")
|
|
||||||
-- items: item management for mobs
|
|
||||||
dofile(path .. "/items.lua")
|
|
||||||
-- pathfinding: pathfinding to target positions
|
|
||||||
dofile(path .. "/pathfinding.lua")
|
|
||||||
-- combat: attack logic
|
|
||||||
dofile(path .. "/combat.lua")
|
|
||||||
-- the enity functions themselves
|
|
||||||
dofile(path .. "/api.lua")
|
dofile(path .. "/api.lua")
|
||||||
|
|
||||||
|
-- Spawning Algorithm
|
||||||
--utility functions
|
|
||||||
dofile(path .. "/breeding.lua")
|
|
||||||
dofile(path .. "/spawning.lua")
|
dofile(path .. "/spawning.lua")
|
||||||
|
|
||||||
|
-- Rideable Mobs
|
||||||
dofile(path .. "/mount.lua")
|
dofile(path .. "/mount.lua")
|
||||||
dofile(path .. "/crafts.lua")
|
|
||||||
dofile(path .. "/compat.lua")
|
|
||||||
|
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
-- Mob Items
|
||||||
local MAX_MOB_NAME_LENGTH = 30
|
dofile(path .. "/crafts.lua")
|
||||||
|
|
||||||
local old_spawn_icons = minetest.settings:get_bool("mcl_old_spawn_icons",false)
|
|
||||||
local extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true)
|
|
||||||
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
|
||||||
|
|
||||||
-- get node but use fallback for nil or unknown
|
|
||||||
local node_ok = function(pos, fallback)
|
|
||||||
fallback = fallback or mcl_mobs.fallback_node
|
|
||||||
local node = minetest.get_node_or_nil(pos)
|
|
||||||
if node and minetest.registered_nodes[node.name] then
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
return minetest.registered_nodes[fallback]
|
|
||||||
end
|
|
||||||
|
|
||||||
--#### REGISTER FUNCS
|
|
||||||
|
|
||||||
-- Code to execute before custom on_rightclick handling
|
|
||||||
local on_rightclick_prefix = function(self, clicker)
|
|
||||||
if not clicker:is_player() then return end
|
|
||||||
local item = clicker:get_wielded_item()
|
|
||||||
if extended_pet_control and self.tamed and self.owner == clicker:get_player_name() then
|
|
||||||
self:toggle_sit(clicker)
|
|
||||||
end
|
|
||||||
-- Name mob with nametag
|
|
||||||
if not self.ignores_nametag and item:get_name() == "mcl_mobs:nametag" then
|
|
||||||
|
|
||||||
local tag = item:get_meta():get_string("name")
|
|
||||||
if tag ~= "" then
|
|
||||||
if string.len(tag) > MAX_MOB_NAME_LENGTH then
|
|
||||||
tag = string.sub(tag, 1, MAX_MOB_NAME_LENGTH)
|
|
||||||
end
|
|
||||||
self.nametag = tag
|
|
||||||
|
|
||||||
self:update_tag()
|
|
||||||
|
|
||||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
|
||||||
item:take_item()
|
|
||||||
clicker:set_wielded_item(item)
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local create_mob_on_rightclick = function(on_rightclick)
|
|
||||||
return function(self, clicker)
|
|
||||||
local stop = on_rightclick_prefix(self, clicker)
|
|
||||||
if (not stop) and (on_rightclick) then
|
|
||||||
on_rightclick(self, clicker)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if within physical map limits (-30911 to 30927)
|
|
||||||
local function within_limits(pos, radius)
|
|
||||||
local wmin, wmax = -30912, 30928
|
|
||||||
if mcl_vars then
|
|
||||||
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
|
||||||
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if radius then
|
|
||||||
wmin = wmin - radius
|
|
||||||
wmax = wmax + radius
|
|
||||||
end
|
|
||||||
for _,v in pairs(pos) do
|
|
||||||
if v < wmin or v > wmax then return false end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mobs.spawning_mobs = {}
|
|
||||||
-- register mob entity
|
|
||||||
function mcl_mobs.register_mob(name, def)
|
|
||||||
|
|
||||||
mcl_mobs.spawning_mobs[name] = true
|
|
||||||
mcl_mobs.registered_mobs[name] = def
|
|
||||||
|
|
||||||
local can_despawn
|
|
||||||
if def.can_despawn ~= nil then
|
|
||||||
can_despawn = def.can_despawn
|
|
||||||
elseif def.spawn_class == "passive" then
|
|
||||||
can_despawn = false
|
|
||||||
else
|
|
||||||
can_despawn = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scale_difficulty(value, default, min, special)
|
|
||||||
if (not value) or (value == default) or (value == special) then
|
|
||||||
return default
|
|
||||||
else
|
|
||||||
return math.max(min, value * difficulty)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
|
|
||||||
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
|
||||||
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
|
||||||
-- FIXME: Remove workaround if it's no longer needed.
|
|
||||||
if collisionbox[5] < 0.79 then
|
|
||||||
collisionbox[5] = 0.79
|
|
||||||
end
|
|
||||||
local final_def = {
|
|
||||||
use_texture_alpha = def.use_texture_alpha,
|
|
||||||
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
|
||||||
head_yaw_offset = def.head_yaw_offset or 0, -- for wonkey model bones
|
|
||||||
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
|
||||||
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
|
|
||||||
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
|
|
||||||
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
|
||||||
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
|
||||||
horrizonatal_head_height = def.horrizonatal_head_height or 0,
|
|
||||||
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
|
||||||
stepheight = def.stepheight or 0.6,
|
|
||||||
name = name,
|
|
||||||
description = def.description,
|
|
||||||
type = def.type,
|
|
||||||
attack_type = def.attack_type,
|
|
||||||
fly = def.fly or false,
|
|
||||||
fly_in = def.fly_in or {"air", "__airlike"},
|
|
||||||
owner = def.owner or "",
|
|
||||||
order = def.order or "",
|
|
||||||
on_die = def.on_die,
|
|
||||||
spawn_small_alternative = def.spawn_small_alternative,
|
|
||||||
do_custom = def.do_custom,
|
|
||||||
detach_child = def.detach_child,
|
|
||||||
jump_height = def.jump_height or 4, -- was 6
|
|
||||||
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
|
||||||
lifetimer = def.lifetimer or 57.73,
|
|
||||||
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
|
||||||
hp_max = scale_difficulty(def.hp_max, 10, 1),
|
|
||||||
xp_min = def.xp_min or 0,
|
|
||||||
xp_max = def.xp_max or 0,
|
|
||||||
xp_timestamp = 0,
|
|
||||||
breath_max = def.breath_max or 15,
|
|
||||||
breathes_in_water = def.breathes_in_water or false,
|
|
||||||
physical = true,
|
|
||||||
collisionbox = collisionbox,
|
|
||||||
selectionbox = def.selectionbox or def.collisionbox,
|
|
||||||
visual = def.visual,
|
|
||||||
visual_size = def.visual_size or {x = 1, y = 1},
|
|
||||||
mesh = def.mesh,
|
|
||||||
makes_footstep_sound = def.makes_footstep_sound or false,
|
|
||||||
view_range = def.view_range or 16,
|
|
||||||
walk_velocity = def.walk_velocity or 1,
|
|
||||||
run_velocity = def.run_velocity or 2,
|
|
||||||
damage = scale_difficulty(def.damage, 0, 0),
|
|
||||||
light_damage = def.light_damage or 0,
|
|
||||||
sunlight_damage = def.sunlight_damage or 0,
|
|
||||||
water_damage = def.water_damage or 0,
|
|
||||||
lava_damage = def.lava_damage or 8,
|
|
||||||
fire_damage = def.fire_damage or 1,
|
|
||||||
suffocation = def.suffocation or true,
|
|
||||||
fall_damage = def.fall_damage or 1,
|
|
||||||
fall_speed = def.fall_speed or DEFAULT_FALL_SPEED, -- must be lower than -2
|
|
||||||
drops = def.drops or {},
|
|
||||||
armor = def.armor or 100,
|
|
||||||
on_rightclick = create_mob_on_rightclick(def.on_rightclick),
|
|
||||||
arrow = def.arrow,
|
|
||||||
shoot_interval = def.shoot_interval,
|
|
||||||
sounds = def.sounds or {},
|
|
||||||
animation = def.animation or {},
|
|
||||||
follow = def.follow,
|
|
||||||
nofollow = def.nofollow,
|
|
||||||
can_open_doors = def.can_open_doors,
|
|
||||||
jump = def.jump ~= false,
|
|
||||||
automatic_face_movement_max_rotation_per_sec = 300,
|
|
||||||
walk_chance = def.walk_chance or 50,
|
|
||||||
attacks_monsters = def.attacks_monsters or false,
|
|
||||||
group_attack = def.group_attack or false,
|
|
||||||
passive = def.passive or false,
|
|
||||||
knock_back = def.knock_back ~= false,
|
|
||||||
shoot_offset = def.shoot_offset or 0,
|
|
||||||
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_what = def.replace_what,
|
|
||||||
replace_with = def.replace_with,
|
|
||||||
replace_offset = def.replace_offset or 0,
|
|
||||||
on_replace = def.on_replace,
|
|
||||||
timer = 0,
|
|
||||||
env_damage_timer = 0,
|
|
||||||
tamed = false,
|
|
||||||
pause_timer = 0,
|
|
||||||
horny = false,
|
|
||||||
hornytimer = 0,
|
|
||||||
gotten = false,
|
|
||||||
health = 0,
|
|
||||||
frame_speed_multiplier = 1,
|
|
||||||
reach = def.reach or 3,
|
|
||||||
htimer = 0,
|
|
||||||
texture_list = def.textures,
|
|
||||||
child_texture = def.child_texture,
|
|
||||||
docile_by_day = def.docile_by_day or false,
|
|
||||||
time_of_day = 0.5,
|
|
||||||
fear_height = def.fear_height or 0,
|
|
||||||
runaway = def.runaway,
|
|
||||||
runaway_timer = 0,
|
|
||||||
pathfinding = def.pathfinding,
|
|
||||||
immune_to = def.immune_to or {},
|
|
||||||
explosion_radius = def.explosion_radius, -- LEGACY
|
|
||||||
explosion_damage_radius = def.explosion_damage_radius, -- LEGACY
|
|
||||||
explosiontimer_reset_radius = def.explosiontimer_reset_radius,
|
|
||||||
explosion_timer = def.explosion_timer or 3,
|
|
||||||
allow_fuse_reset = def.allow_fuse_reset ~= false,
|
|
||||||
stop_to_explode = def.stop_to_explode ~= false,
|
|
||||||
custom_attack = def.custom_attack,
|
|
||||||
double_melee_attack = def.double_melee_attack,
|
|
||||||
dogshoot_switch = def.dogshoot_switch,
|
|
||||||
dogshoot_count = 0,
|
|
||||||
dogshoot_count_max = def.dogshoot_count_max or 5,
|
|
||||||
dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5),
|
|
||||||
attack_animals = def.attack_animals or false,
|
|
||||||
attack_npcs = def.attack_npcs or false,
|
|
||||||
specific_attack = def.specific_attack,
|
|
||||||
runaway_from = def.runaway_from,
|
|
||||||
owner_loyal = def.owner_loyal,
|
|
||||||
facing_fence = false,
|
|
||||||
is_mob = true,
|
|
||||||
pushable = def.pushable or true,
|
|
||||||
|
|
||||||
-- MCL2 extensions
|
|
||||||
shooter_avoid_enemy = def.shooter_avoid_enemy,
|
|
||||||
strafes = def.strafes,
|
|
||||||
avoid_distance = def.avoid_distance or 9,
|
|
||||||
do_teleport = def.do_teleport,
|
|
||||||
spawn_class = def.spawn_class,
|
|
||||||
can_spawn = def.can_spawn,
|
|
||||||
ignores_nametag = def.ignores_nametag or false,
|
|
||||||
rain_damage = def.rain_damage or 0,
|
|
||||||
glow = def.glow,
|
|
||||||
can_despawn = can_despawn,
|
|
||||||
child = def.child or false,
|
|
||||||
texture_mods = {},
|
|
||||||
shoot_arrow = def.shoot_arrow,
|
|
||||||
sounds_child = def.sounds_child,
|
|
||||||
_child_animations = def.child_animations,
|
|
||||||
pick_up = def.pick_up,
|
|
||||||
explosion_strength = def.explosion_strength,
|
|
||||||
suffocation_timer = 0,
|
|
||||||
follow_velocity = def.follow_velocity or 2.4,
|
|
||||||
instant_death = def.instant_death or false,
|
|
||||||
fire_resistant = def.fire_resistant or false,
|
|
||||||
fire_damage_resistant = def.fire_damage_resistant or false,
|
|
||||||
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
|
||||||
spawn_in_group = def.spawn_in_group,
|
|
||||||
spawn_in_group_min = def.spawn_in_group_min,
|
|
||||||
noyaw = def.noyaw or false,
|
|
||||||
particlespawners = def.particlespawners,
|
|
||||||
-- End of MCL2 extensions
|
|
||||||
on_spawn = def.on_spawn,
|
|
||||||
on_blast = def.on_blast or function(self,damage)
|
|
||||||
self.object:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = damage},
|
|
||||||
}, nil)
|
|
||||||
return false, true, {}
|
|
||||||
end,
|
|
||||||
do_punch = def.do_punch,
|
|
||||||
on_breed = def.on_breed,
|
|
||||||
on_grown = def.on_grown,
|
|
||||||
on_pick_up = def.on_pick_up,
|
|
||||||
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.is_mob = true
|
|
||||||
self.object:set_properties({
|
|
||||||
collide_with_objects = false,
|
|
||||||
})
|
|
||||||
|
|
||||||
return self:mob_activate(staticdata, def, dtime)
|
|
||||||
end,
|
|
||||||
harmed_by_heal = def.harmed_by_heal,
|
|
||||||
on_lightning_strike = def.on_lightning_strike
|
|
||||||
}
|
|
||||||
minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta))
|
|
||||||
|
|
||||||
if minetest.get_modpath("doc_identifier") ~= nil then
|
|
||||||
doc.sub.identifier.register_object(name, "basics", "mobs")
|
|
||||||
end
|
|
||||||
|
|
||||||
end -- END mcl_mobs.register_mob function
|
|
||||||
|
|
||||||
|
|
||||||
-- register arrow for shoot attack
|
|
||||||
function mcl_mobs.register_arrow(name, def)
|
|
||||||
|
|
||||||
if not name or not def then return end -- errorcheck
|
|
||||||
|
|
||||||
minetest.register_entity(name, {
|
|
||||||
|
|
||||||
physical = false,
|
|
||||||
visual = def.visual,
|
|
||||||
visual_size = def.visual_size,
|
|
||||||
textures = def.textures,
|
|
||||||
velocity = def.velocity,
|
|
||||||
hit_player = def.hit_player,
|
|
||||||
hit_node = def.hit_node,
|
|
||||||
hit_mob = def.hit_mob,
|
|
||||||
hit_object = def.hit_object,
|
|
||||||
drop = def.drop or false, -- drops arrow as registered item when true
|
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows
|
|
||||||
timer = 0,
|
|
||||||
switch = 0,
|
|
||||||
owner_id = def.owner_id,
|
|
||||||
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
|
|
||||||
and (def.rotate - (math.pi / 180)) or false,
|
|
||||||
|
|
||||||
on_activate = def.on_activate,
|
|
||||||
|
|
||||||
on_step = def.on_step or function(self, dtime)
|
|
||||||
|
|
||||||
self.timer = self.timer + 1
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
if self.switch == 0
|
|
||||||
or self.timer > 150
|
|
||||||
or not within_limits(pos, 0) then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove();
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- does arrow have a tail (fireball)
|
|
||||||
if def.tail
|
|
||||||
and def.tail == 1
|
|
||||||
and def.tail_texture then
|
|
||||||
|
|
||||||
minetest.add_particle({
|
|
||||||
pos = pos,
|
|
||||||
velocity = {x = 0, y = 0, z = 0},
|
|
||||||
acceleration = {x = 0, y = 0, z = 0},
|
|
||||||
expirationtime = def.expire or 0.25,
|
|
||||||
collisiondetection = false,
|
|
||||||
texture = def.tail_texture,
|
|
||||||
size = def.tail_size or 5,
|
|
||||||
glow = def.glow or 0,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.hit_node then
|
|
||||||
|
|
||||||
local node = node_ok(pos).name
|
|
||||||
|
|
||||||
if minetest.registered_nodes[node].walkable then
|
|
||||||
|
|
||||||
self.hit_node(self, pos, node)
|
|
||||||
|
|
||||||
if self.drop == true then
|
|
||||||
|
|
||||||
pos.y = pos.y + 1
|
|
||||||
|
|
||||||
self.lastpos = (self.lastpos or pos)
|
|
||||||
|
|
||||||
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.object:remove();
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if self.hit_player
|
|
||||||
and player:is_player() then
|
|
||||||
|
|
||||||
self.hit_player(self, player)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local entity = player:get_luaentity()
|
|
||||||
|
|
||||||
if entity
|
|
||||||
and self.hit_mob
|
|
||||||
and entity.is_mob == true
|
|
||||||
and tostring(player) ~= self.owner_id
|
|
||||||
and entity.name ~= self.object:get_luaentity().name then
|
|
||||||
self.hit_mob(self, player)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if entity
|
|
||||||
and self.hit_object
|
|
||||||
and (not entity.is_mob)
|
|
||||||
and tostring(player) ~= self.owner_id
|
|
||||||
and entity.name ~= self.object:get_luaentity().name then
|
|
||||||
self.hit_object(self, player)
|
|
||||||
self.object:remove();
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.lastpos = pos
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register spawn eggs
|
|
||||||
|
|
||||||
-- Note: This also introduces the “spawn_egg” group:
|
|
||||||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
|
||||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
|
||||||
function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addegg, no_creative)
|
|
||||||
|
|
||||||
local grp = {spawn_egg = 1}
|
|
||||||
|
|
||||||
-- do NOT add this egg to creative inventory (e.g. dungeon master)
|
|
||||||
if no_creative == true then
|
|
||||||
grp.not_in_creative_inventory = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
|
||||||
if old_spawn_icons then
|
|
||||||
local mobname = mob:gsub("mobs_mc:","")
|
|
||||||
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
|
|
||||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
|
||||||
invimg = fn
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if addegg == 1 then
|
|
||||||
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
|
||||||
"^[mask:mobs_chicken_egg_overlay.png)"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- register old stackable mob egg
|
|
||||||
minetest.register_craftitem(mob, {
|
|
||||||
|
|
||||||
description = desc,
|
|
||||||
inventory_image = invimg,
|
|
||||||
groups = grp,
|
|
||||||
|
|
||||||
_doc_items_longdesc = S("This allows you to place a single mob."),
|
|
||||||
_doc_items_usagehelp = S("Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns."),
|
|
||||||
|
|
||||||
on_place = function(itemstack, placer, pointed_thing)
|
|
||||||
|
|
||||||
local pos = pointed_thing.above
|
|
||||||
|
|
||||||
-- am I clicking on something with existing on_rightclick function?
|
|
||||||
local under = minetest.get_node(pointed_thing.under)
|
|
||||||
local def = minetest.registered_nodes[under.name]
|
|
||||||
if def and def.on_rightclick then
|
|
||||||
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
|
||||||
end
|
|
||||||
|
|
||||||
if pos
|
|
||||||
and within_limits(pos, 0)
|
|
||||||
and not minetest.is_protected(pos, placer:get_player_name()) then
|
|
||||||
|
|
||||||
local name = placer:get_player_name()
|
|
||||||
local privs = minetest.get_player_privs(name)
|
|
||||||
if under.name == "mcl_mobspawners:spawner" then
|
|
||||||
if minetest.is_protected(pointed_thing.under, name) then
|
|
||||||
minetest.record_protection_violation(pointed_thing.under, name)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
if not privs.maphack then
|
|
||||||
minetest.chat_send_player(name, S("You need the “maphack” privilege to change the mob spawner."))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
mcl_mobspawners.setup_spawner(pointed_thing.under, itemstack:get_name())
|
|
||||||
if not minetest.is_creative_enabled(name) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
if not minetest.registered_entities[mob] then
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
|
||||||
and minetest.registered_entities[mob].type == "monster" then
|
|
||||||
minetest.chat_send_player(name, S("Only peaceful mobs allowed!"))
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
pos.y = pos.y - 0.5
|
|
||||||
|
|
||||||
local mob = minetest.add_entity(pos, mob)
|
|
||||||
local entityname = itemstack:get_name()
|
|
||||||
minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos))
|
|
||||||
local ent = mob:get_luaentity()
|
|
||||||
|
|
||||||
-- don't set owner if monster or sneak pressed
|
|
||||||
if ent.type ~= "monster"
|
|
||||||
and not placer:get_player_control().sneak then
|
|
||||||
ent.owner = placer:get_player_name()
|
|
||||||
ent.tamed = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set nametag
|
|
||||||
local nametag = itemstack:get_meta():get_string("name")
|
|
||||||
if nametag ~= "" then
|
|
||||||
if string.len(nametag) > MAX_MOB_NAME_LENGTH then
|
|
||||||
nametag = string.sub(nametag, 1, MAX_MOB_NAME_LENGTH)
|
|
||||||
end
|
|
||||||
ent.nametag = nametag
|
|
||||||
update_tag(ent)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if not in creative then take item
|
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return itemstack
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,103 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
--- Item and armor management
|
|
||||||
|
|
||||||
local function player_near(pos)
|
|
||||||
for _,o in pairs(minetest.get_objects_inside_radius(pos,2)) do
|
|
||||||
if o:is_player() then return true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_armor_texture(armor_name)
|
|
||||||
if armor_name == "" then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
if armor_name=="blank.png" then
|
|
||||||
return "blank.png"
|
|
||||||
end
|
|
||||||
local seperator = string.find(armor_name, ":")
|
|
||||||
return "mcl_armor_"..string.sub(armor_name, seperator+1, -1)..".png^"
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:set_armor_texture()
|
|
||||||
if self.armor_list then
|
|
||||||
local chestplate=minetest.registered_items[self.armor_list.chestplate] or {name=""}
|
|
||||||
local boots=minetest.registered_items[self.armor_list.boots] or {name=""}
|
|
||||||
local leggings=minetest.registered_items[self.armor_list.leggings] or {name=""}
|
|
||||||
local helmet=minetest.registered_items[self.armor_list.helmet] or {name=""}
|
|
||||||
|
|
||||||
if helmet.name=="" and chestplate.name=="" and leggings.name=="" and boots.name=="" then
|
|
||||||
helmet={name="blank.png"}
|
|
||||||
end
|
|
||||||
local texture = get_armor_texture(chestplate.name)..get_armor_texture(helmet.name)..get_armor_texture(boots.name)..get_armor_texture(leggings.name)
|
|
||||||
if string.sub(texture, -1,-1) == "^" then
|
|
||||||
texture=string.sub(texture,1,-2)
|
|
||||||
end
|
|
||||||
if self.textures[self.wears_armor] then
|
|
||||||
self.textures[self.wears_armor]=texture
|
|
||||||
end
|
|
||||||
self.object:set_properties({textures=self.textures})
|
|
||||||
|
|
||||||
local armor_
|
|
||||||
if type(self.armor) == "table" then
|
|
||||||
armor_ = table.copy(self.armor)
|
|
||||||
armor_.immortal = 1
|
|
||||||
else
|
|
||||||
armor_ = {immortal=1, fleshy = self.armor}
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,item in pairs(self.armor_list) do
|
|
||||||
if not item then return end
|
|
||||||
if type(minetest.get_item_group(item, "mcl_armor_points")) == "number" then
|
|
||||||
armor_.fleshy=armor_.fleshy-(minetest.get_item_group(item, "mcl_armor_points")*3.5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.object:set_armor_groups(armor_)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_item_pickup()
|
|
||||||
if self.pick_up and #self.pick_up > 0 or self.wears_armor then
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
if not p then return end
|
|
||||||
for _,o in pairs(minetest.get_objects_inside_radius(p,2)) do
|
|
||||||
local l=o:get_luaentity()
|
|
||||||
if l and l.name == "__builtin:item" then
|
|
||||||
if not player_near(p) and l.itemstring:find("mcl_armor") and self.wears_armor then
|
|
||||||
local armor_type
|
|
||||||
if l.itemstring:find("chestplate") then
|
|
||||||
armor_type = "chestplate"
|
|
||||||
elseif l.itemstring:find("boots") then
|
|
||||||
armor_type = "boots"
|
|
||||||
elseif l.itemstring:find("leggings") then
|
|
||||||
armor_type = "leggings"
|
|
||||||
elseif l.itemstring:find("helmet") then
|
|
||||||
armor_type = "helmet"
|
|
||||||
end
|
|
||||||
if not armor_type then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not self.armor_list then
|
|
||||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
|
||||||
elseif self.armor_list[armor_type] and self.armor_list[armor_type] ~= "" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self.armor_list[armor_type]=ItemStack(l.itemstring):get_name()
|
|
||||||
o:remove()
|
|
||||||
end
|
|
||||||
if self.pick_up then
|
|
||||||
for k,v in pairs(self.pick_up) do
|
|
||||||
if not player_near(p) and self.on_pick_up and l.itemstring:find(v) then
|
|
||||||
local r = self.on_pick_up(self,l)
|
|
||||||
if r and r.is_empty and not r:is_empty() then
|
|
||||||
l.itemstring = r:to_string()
|
|
||||||
elseif r and r.is_empty and r:is_empty() then
|
|
||||||
o:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# textdomain: mcl_mobs
|
|
||||||
Peaceful mode active! No monsters will spawn.=ピースフルモード有効! モンスターは出現しません。
|
|
||||||
This allows you to place a single mob.=これにより、1体のMOBを配置できます。
|
|
||||||
Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns.=MOBを出現させたい場所に、置くだけです。スニークキーを押しながら配置しない限り、動物は飼いならされた状態でスポーンします。MOBスポナーに設置すると、スポーンするMOBが変わります。
|
|
||||||
You need the “maphack” privilege to change the mob spawner.=MOBスポナーを変更するには、"maphack"権限が必要です。
|
|
||||||
Name Tag=名札
|
|
||||||
A name tag is an item to name a mob.=名札は、MOBに名前をつけるためのアイテムです。
|
|
||||||
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=名札を使用する前に、金床で名前の設定をする必要があります。その後、MOBに名前をつけるために名札が使えます。これで名札は使い切ります。
|
|
||||||
Only peaceful mobs allowed!=平和的なMOBのみ許可!
|
|
||||||
Give names to mobs=MOBに名前を付与
|
|
||||||
Set name at anvil=金床で名前の設定
|
|
|
@ -1,5 +1,4 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
-- lib_mount by Blert2112 (edited by TenPlus1)
|
||||||
|
|
||||||
local enable_crash = false
|
local enable_crash = false
|
||||||
|
@ -207,7 +206,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||||
-- move forwards
|
-- move forwards
|
||||||
if ctrl.up then
|
if ctrl.up then
|
||||||
|
|
||||||
entity.v = entity.v + entity.accel / 10 * entity.run_velocity / 2.6
|
entity.v = entity.v + entity.accel / 10
|
||||||
|
|
||||||
-- move backwards
|
-- move backwards
|
||||||
elseif ctrl.down then
|
elseif ctrl.down then
|
||||||
|
@ -386,6 +385,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||||
entity.v2 = v
|
entity.v2 = v
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||||
|
|
||||||
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||||
|
@ -446,18 +446,3 @@ function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_an
|
||||||
mcl_mobs:set_animation(entity, moving_anim)
|
mcl_mobs:set_animation(entity, moving_anim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs.mob_class.drive = mcl_mobs.drive
|
|
||||||
mcl_mobs.mob_class.fly = mcl_mobs.fly
|
|
||||||
mcl_mobs.mob_class.attach = mcl_mobs.attach
|
|
||||||
|
|
||||||
function mob_class:on_detach_child(child)
|
|
||||||
if self.detach_child then
|
|
||||||
if self.detach_child(self, child) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.driver == child then
|
|
||||||
self.driver = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,382 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
|
||||||
local PATHFINDING = "gowp"
|
|
||||||
local enable_pathfinding = true
|
|
||||||
|
|
||||||
local LOG_MODULE = "[Mobs]"
|
|
||||||
local function mcl_log (message)
|
|
||||||
if LOGGING_ON and message then
|
|
||||||
minetest.log(LOG_MODULE .. " " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function output_table (wp)
|
|
||||||
if not wp then return end
|
|
||||||
mcl_log("wp items: ".. tostring(#wp))
|
|
||||||
for a,b in pairs(wp) do
|
|
||||||
mcl_log(a.. ": ".. tostring(b))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function append_paths (wp1, wp2)
|
|
||||||
mcl_log("Start append")
|
|
||||||
if not wp1 or not wp2 then
|
|
||||||
mcl_log("Cannot append wp's")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
output_table(wp1)
|
|
||||||
output_table(wp2)
|
|
||||||
for _,a in pairs (wp2) do
|
|
||||||
table.insert(wp1, a)
|
|
||||||
end
|
|
||||||
mcl_log("End append")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function output_enriched (wp_out)
|
|
||||||
mcl_log("Output enriched path")
|
|
||||||
local i = 0
|
|
||||||
for _,outy in pairs (wp_out) do
|
|
||||||
i = i + 1
|
|
||||||
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
|
||||||
|
|
||||||
local action = outy["action"]
|
|
||||||
if action then
|
|
||||||
mcl_log("type: " .. action["type"])
|
|
||||||
mcl_log("action: " .. action["action"])
|
|
||||||
mcl_log("target: " .. minetest.pos_to_string(action["target"]))
|
|
||||||
end
|
|
||||||
mcl_log("failed attempts: " .. outy["failed_attempts"])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This function will take a list of paths, and enrich it with:
|
|
||||||
-- a var for failed attempts
|
|
||||||
-- an action, such as to open or close a door where we know that pos requires that action
|
|
||||||
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
|
||||||
local wp_out = {}
|
|
||||||
for i, cur_pos in pairs(wp_in) do
|
|
||||||
local action = nil
|
|
||||||
|
|
||||||
local one_down = vector.new(0,-1,0)
|
|
||||||
local cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
if door_open_pos and vector.equals (cur_pos, door_open_pos) then
|
|
||||||
mcl_log ("Door open match")
|
|
||||||
--action = {type = "door", action = "open"}
|
|
||||||
action = {}
|
|
||||||
action["type"] = "door"
|
|
||||||
action["action"] = "open"
|
|
||||||
action["target"] = cur_door_pos
|
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
|
||||||
mcl_log ("Door close match")
|
|
||||||
--action = {type = "door", action = "closed"}
|
|
||||||
action = {}
|
|
||||||
action["type"] = "door"
|
|
||||||
action["action"] = "close"
|
|
||||||
action["target"] = cur_door_pos
|
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
|
||||||
mcl_log("Current door pos")
|
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
action = {}
|
|
||||||
action["type"] = "door"
|
|
||||||
action["action"] = "open"
|
|
||||||
action["target"] = cur_door_pos
|
|
||||||
else
|
|
||||||
cur_pos_to_add = cur_pos
|
|
||||||
--mcl_log ("Pos doesn't match")
|
|
||||||
end
|
|
||||||
|
|
||||||
wp_out[i] = {}
|
|
||||||
wp_out[i]["pos"] = cur_pos_to_add
|
|
||||||
wp_out[i]["failed_attempts"] = 0
|
|
||||||
wp_out[i]["action"] = action
|
|
||||||
|
|
||||||
--wp_out[i] = {"pos" = cur_pos, "failed_attempts" = 0, "action" = action}
|
|
||||||
--output_pos(cur_pos, i)
|
|
||||||
end
|
|
||||||
output_enriched(wp_out)
|
|
||||||
return wp_out
|
|
||||||
end
|
|
||||||
|
|
||||||
local plane_adjacents = {
|
|
||||||
vector.new(1,0,0),
|
|
||||||
vector.new(-1,0,0),
|
|
||||||
vector.new(0,0,1),
|
|
||||||
vector.new(0,0,-1),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
|
||||||
local function calculate_path_through_door (p, t, target)
|
|
||||||
-- target is the same as t, just 1 square difference. Maybe we don't need target
|
|
||||||
mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
|
|
||||||
|
|
||||||
local enriched_path = nil
|
|
||||||
|
|
||||||
local cur_door_pos = nil
|
|
||||||
local pos_closest_to_door = nil
|
|
||||||
local other_side_of_door = nil
|
|
||||||
|
|
||||||
--Path to door first
|
|
||||||
local wp = minetest.find_path(p,t,150,1,4)
|
|
||||||
if not wp then
|
|
||||||
mcl_log("No direct path. Path through door")
|
|
||||||
|
|
||||||
-- This could improve. There could be multiple doors. Check you can path from door to target first.
|
|
||||||
local cur_door_pos = minetest.find_node_near(target,16,{"group:door"})
|
|
||||||
if cur_door_pos then
|
|
||||||
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
|
|
||||||
for _,v in pairs(plane_adjacents) do
|
|
||||||
pos_closest_to_door = vector.add(cur_door_pos,v)
|
|
||||||
|
|
||||||
local n = minetest.get_node(pos_closest_to_door)
|
|
||||||
if n.name == "air" then
|
|
||||||
wp = minetest.find_path(p,pos_closest_to_door,150,1,4)
|
|
||||||
if wp then
|
|
||||||
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
|
||||||
other_side_of_door = vector.add(cur_door_pos,-v)
|
|
||||||
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
|
||||||
|
|
||||||
local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4)
|
|
||||||
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
|
||||||
table.insert(wp, cur_door_pos)
|
|
||||||
append_paths (wp, wp_otherside_door_to_target)
|
|
||||||
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
|
||||||
mcl_log("We have a path from outside door to target")
|
|
||||||
else
|
|
||||||
mcl_log("We cannot path from outside door to target")
|
|
||||||
end
|
|
||||||
break
|
|
||||||
else
|
|
||||||
mcl_log("This block next to door doesn't work.")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("Block is not air, it is: ".. n.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("No door found")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("We have a direct route")
|
|
||||||
end
|
|
||||||
|
|
||||||
if wp and not enriched_path then
|
|
||||||
enriched_path = generate_enriched_path(wp)
|
|
||||||
end
|
|
||||||
return enriched_path
|
|
||||||
end
|
|
||||||
|
|
||||||
local gopath_last = os.time()
|
|
||||||
function mob_class:gopath(target,callback_arrived)
|
|
||||||
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
|
||||||
|
|
||||||
if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then
|
|
||||||
mcl_log("We are not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
|
||||||
return
|
|
||||||
else
|
|
||||||
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
|
|
||||||
end
|
|
||||||
|
|
||||||
--if os.time() - gopath_last < 5 then
|
|
||||||
-- mcl_log("Not ready to path yet")
|
|
||||||
-- return
|
|
||||||
--end
|
|
||||||
--gopath_last = os.time()
|
|
||||||
|
|
||||||
self.order = nil
|
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
local t = vector.offset(target,0,1,0)
|
|
||||||
|
|
||||||
local wp = calculate_path_through_door(p, t, target)
|
|
||||||
if not wp then
|
|
||||||
mcl_log("Could not calculate path")
|
|
||||||
self._pf_last_failed = os.time()
|
|
||||||
-- Cover for a flaw in pathfind where it chooses the wrong door and gets stuck. Take a break, allow others.
|
|
||||||
end
|
|
||||||
--output_table(wp)
|
|
||||||
|
|
||||||
if wp and #wp > 0 then
|
|
||||||
self._target = t
|
|
||||||
self.callback_arrived = callback_arrived
|
|
||||||
local current_location = table.remove(wp,1)
|
|
||||||
if current_location and current_location["pos"] then
|
|
||||||
mcl_log("Removing first co-ord? " .. tostring(current_location["pos"]))
|
|
||||||
else
|
|
||||||
mcl_log("Nil pos")
|
|
||||||
end
|
|
||||||
self.current_target = current_location
|
|
||||||
self.waypoints = wp
|
|
||||||
self.state = PATHFINDING
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
self.state = "walk"
|
|
||||||
self.waypoints = nil
|
|
||||||
self.current_target = nil
|
|
||||||
-- minetest.log("no path found")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:interact_with_door(action, target)
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
--local t = minetest.get_timeofday()
|
|
||||||
--local dd = minetest.find_nodes_in_area(vector.offset(p,-1,-1,-1),vector.offset(p,1,1,1),{"group:door"})
|
|
||||||
--for _,d in pairs(dd) do
|
|
||||||
if target then
|
|
||||||
mcl_log("Door target is: ".. minetest.pos_to_string(target))
|
|
||||||
|
|
||||||
local n = minetest.get_node(target)
|
|
||||||
if n.name:find("_b_") or n.name:find("_t_") then
|
|
||||||
mcl_log("Door")
|
|
||||||
local def = minetest.registered_nodes[n.name]
|
|
||||||
local closed = n.name:find("_b_1") or n.name:find("_t_1")
|
|
||||||
--if self.state == PATHFINDING then
|
|
||||||
if closed and action == "open" and def.on_rightclick then
|
|
||||||
mcl_log("Open door")
|
|
||||||
def.on_rightclick(target,n,self)
|
|
||||||
end
|
|
||||||
if not closed and action == "close" and def.on_rightclick then
|
|
||||||
mcl_log("Close door")
|
|
||||||
def.on_rightclick(target,n,self)
|
|
||||||
end
|
|
||||||
--else
|
|
||||||
else
|
|
||||||
mcl_log("Not door")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("no target. cannot try and open or close door")
|
|
||||||
end
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:do_pathfind_action(action)
|
|
||||||
if action then
|
|
||||||
mcl_log("Action present")
|
|
||||||
local type = action["type"]
|
|
||||||
local action_val = action["action"]
|
|
||||||
local target = action["target"]
|
|
||||||
if target then
|
|
||||||
mcl_log("Target: ".. minetest.pos_to_string(target))
|
|
||||||
end
|
|
||||||
if type and type == "door" then
|
|
||||||
mcl_log("Type is door")
|
|
||||||
self:interact_with_door(action_val, target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local gowp_etime = 0
|
|
||||||
|
|
||||||
function mob_class:check_gowp(dtime)
|
|
||||||
gowp_etime = gowp_etime + dtime
|
|
||||||
|
|
||||||
-- 0.1 is optimal.
|
|
||||||
--less frequently = villager will get sent back after passing a point.
|
|
||||||
--more frequently = villager will fail points they shouldn't they just didn't get there yet
|
|
||||||
|
|
||||||
--if gowp_etime < 0.05 then return end
|
|
||||||
--gowp_etime = 0
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
|
|
||||||
-- no destination
|
|
||||||
if not p or not self._target then
|
|
||||||
mcl_log("p: ".. tostring(p))
|
|
||||||
mcl_log("self._target: ".. tostring(self._target))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- arrived at location, finish gowp
|
|
||||||
local distance_to_targ = vector.distance(p,self._target)
|
|
||||||
--mcl_log("Distance to targ: ".. tostring(distance_to_targ))
|
|
||||||
if distance_to_targ < 2 then
|
|
||||||
mcl_log("Arrived at _target")
|
|
||||||
self.waypoints = nil
|
|
||||||
self._target = nil
|
|
||||||
self.current_target = nil
|
|
||||||
self.state = "stand"
|
|
||||||
self.order = "stand"
|
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
if self.callback_arrived then return self.callback_arrived(self) end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- More pathing to be done
|
|
||||||
local distance_to_current_target = 50
|
|
||||||
if self.current_target and self.current_target["pos"] then
|
|
||||||
distance_to_current_target = vector.distance(p,self.current_target["pos"])
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
|
||||||
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
|
||||||
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
|
||||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then
|
|
||||||
-- We have waypoints, and no current target, or we're at it. We need a new current_target.
|
|
||||||
self:do_pathfind_action (self.current_target["action"])
|
|
||||||
|
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
|
||||||
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: " .. distance_to_current_target)
|
|
||||||
|
|
||||||
self.current_target = table.remove(self.waypoints, 1)
|
|
||||||
self:go_to_pos(self.current_target["pos"])
|
|
||||||
return
|
|
||||||
elseif self.current_target and self.current_target["pos"] then
|
|
||||||
-- No waypoints left, but have current target. Potentially last waypoint to go to.
|
|
||||||
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
|
||||||
if failed_attempts >= 50 then
|
|
||||||
mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts)
|
|
||||||
self.state = "stand"
|
|
||||||
self.current_target = nil
|
|
||||||
self.waypoints = nil
|
|
||||||
self._target = nil
|
|
||||||
self._pf_last_failed = os.time()
|
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: ".. distance_to_current_target)
|
|
||||||
self:go_to_pos(self.current_target["pos"])
|
|
||||||
-- Do i just delete current_target, and return so we can find final path.
|
|
||||||
else
|
|
||||||
-- Not at target, no current waypoints or current_target. Through the door and should be able to path to target.
|
|
||||||
-- Is a little sensitive and could take 1 - 7 times. A 10 fail count might be a good exit condition.
|
|
||||||
|
|
||||||
mcl_log("We don't have waypoints or a current target. Let's try to path to target")
|
|
||||||
local final_wp = minetest.find_path(p,self._target,150,1,4)
|
|
||||||
if final_wp then
|
|
||||||
mcl_log("We might be able to get to target here.")
|
|
||||||
-- self.waypoints = final_wp
|
|
||||||
self:go_to_pos(self._target)
|
|
||||||
else
|
|
||||||
-- Abandon route?
|
|
||||||
mcl_log("Cannot plot final route to target")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- I don't think we need the following anymore, but test first.
|
|
||||||
-- Maybe just need something to path to target if no waypoints left
|
|
||||||
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
|
||||||
local updated_p = self.object:get_pos()
|
|
||||||
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
|
||||||
|
|
||||||
mcl_log("Distance to current target: ".. tostring(distance_to_cur_targ))
|
|
||||||
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
|
||||||
|
|
||||||
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
|
||||||
if distance_to_cur_targ > 1.9 then
|
|
||||||
mcl_log("not close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
|
||||||
self:go_to_pos(self._current_target)
|
|
||||||
else
|
|
||||||
mcl_log("close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
|
||||||
self.current_target = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,976 +0,0 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
|
|
||||||
local ENTITY_CRAMMING_MAX = 24
|
|
||||||
local CRAMMING_DAMAGE = 3
|
|
||||||
local DEATH_DELAY = 0.5
|
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
|
||||||
local FLOP_HEIGHT = 6
|
|
||||||
local FLOP_HOR_SPEED = 1.5
|
|
||||||
local PATHFINDING = "gowp"
|
|
||||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
|
|
||||||
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
|
||||||
local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
|
||||||
local show_health = false
|
|
||||||
|
|
||||||
-- get node but use fallback for nil or unknown
|
|
||||||
local node_ok = function(pos, fallback)
|
|
||||||
|
|
||||||
fallback = fallback or mcl_mobs.fallback_node
|
|
||||||
|
|
||||||
local node = minetest.get_node_or_nil(pos)
|
|
||||||
|
|
||||||
if node and minetest.registered_nodes[node.name] then
|
|
||||||
return node
|
|
||||||
end
|
|
||||||
|
|
||||||
return minetest.registered_nodes[fallback]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if within physical map limits (-30911 to 30927)
|
|
||||||
local function within_limits(pos, radius)
|
|
||||||
local wmin, wmax = -30912, 30928
|
|
||||||
if mcl_vars then
|
|
||||||
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
|
||||||
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if radius then
|
|
||||||
wmin = wmin - radius
|
|
||||||
wmax = wmax + radius
|
|
||||||
end
|
|
||||||
if not pos then return true end
|
|
||||||
for _,v in pairs(pos) do
|
|
||||||
if v < wmin or v > wmax then return false end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:player_in_active_range()
|
|
||||||
for _,p in pairs(minetest.get_connected_players()) do
|
|
||||||
if vector.distance(self.object:get_pos(),p:get_pos()) <= mob_active_range then return true end
|
|
||||||
-- slightly larger than the mc 32 since mobs spawn on that circle and easily stand still immediately right after spawning.
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return true if object is in view_range
|
|
||||||
function mob_class:object_in_range(object)
|
|
||||||
if not object then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local factor
|
|
||||||
-- Apply view range reduction for special player armor
|
|
||||||
if object:is_player() then
|
|
||||||
local factors = mcl_armor.player_view_range_factors[object]
|
|
||||||
factor = factors and factors[self.name]
|
|
||||||
end
|
|
||||||
-- Distance check
|
|
||||||
local dist
|
|
||||||
if factor and factor == 0 then
|
|
||||||
return false
|
|
||||||
elseif factor then
|
|
||||||
dist = self.view_range * factor
|
|
||||||
else
|
|
||||||
dist = self.view_range
|
|
||||||
end
|
|
||||||
|
|
||||||
local p1, p2 = self.object:get_pos(), object:get_pos()
|
|
||||||
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:item_drop(cooked, looting_level)
|
|
||||||
|
|
||||||
if not mobs_drop_items then return end
|
|
||||||
|
|
||||||
looting_level = looting_level or 0
|
|
||||||
|
|
||||||
if (self.child and self.type ~= "monster") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj, item, num
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
self.drops = self.drops or {}
|
|
||||||
|
|
||||||
for n = 1, #self.drops do
|
|
||||||
local dropdef = self.drops[n]
|
|
||||||
local chance = 1 / dropdef.chance
|
|
||||||
local looting_type = dropdef.looting
|
|
||||||
|
|
||||||
if looting_level > 0 then
|
|
||||||
local chance_function = dropdef.looting_chance_function
|
|
||||||
if chance_function then
|
|
||||||
chance = chance_function(looting_level)
|
|
||||||
elseif looting_type == "rare" then
|
|
||||||
chance = chance + (dropdef.looting_factor or 0.01) * looting_level
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local num = 0
|
|
||||||
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
|
||||||
if math.random() < chance then
|
|
||||||
num = math.random(dropdef.min or 1, dropdef.max or 1)
|
|
||||||
elseif not dropdef.looting_ignore_chance then
|
|
||||||
do_common_looting = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if do_common_looting then
|
|
||||||
num = num + math.floor(math.random(0, looting_level) + 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
if num > 0 then
|
|
||||||
item = dropdef.name
|
|
||||||
|
|
||||||
if cooked then
|
|
||||||
|
|
||||||
local output = minetest.get_craft_result({
|
|
||||||
method = "cooking", width = 1, items = {item}})
|
|
||||||
|
|
||||||
if output and output.item and not output.item:is_empty() then
|
|
||||||
item = output.item:get_name()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for x = 1, num do
|
|
||||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if obj and obj:get_luaentity() then
|
|
||||||
|
|
||||||
obj:set_velocity({
|
|
||||||
x = math.random(-10, 10) / 9,
|
|
||||||
y = 6,
|
|
||||||
z = math.random(-10, 10) / 9,
|
|
||||||
})
|
|
||||||
elseif obj then
|
|
||||||
obj:remove() -- item does not exist
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.drops = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- collision function borrowed amended from jordan4ibanez open_ai mod
|
|
||||||
function mob_class:collision()
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
if not pos then return {0,0} end
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
local x = 0
|
|
||||||
local z = 0
|
|
||||||
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
|
||||||
|
|
||||||
local ent = object:get_luaentity()
|
|
||||||
if object:is_player() or (ent and ent.is_mob and object ~= self.object) then
|
|
||||||
|
|
||||||
if object:is_player() and mcl_burning.is_burning(self.object) then
|
|
||||||
mcl_burning.set_on_fire(object, 4)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos2 = object:get_pos()
|
|
||||||
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
|
|
||||||
local force = (width + 0.5) - vector.distance(
|
|
||||||
{x = pos.x, y = 0, z = pos.z},
|
|
||||||
{x = pos2.x, y = 0, z = pos2.z})
|
|
||||||
|
|
||||||
x = x + (vec.x * force)
|
|
||||||
z = z + (vec.z * force)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return({x,z})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- move mob in facing direction
|
|
||||||
function mob_class:set_velocity(v)
|
|
||||||
local c_x, c_y = 0, 0
|
|
||||||
|
|
||||||
-- can mob be pushed, if so calculate direction
|
|
||||||
if self.pushable then
|
|
||||||
c_x, c_y = unpack(self:collision())
|
|
||||||
end
|
|
||||||
|
|
||||||
-- halt mob if it has been ordered to stay
|
|
||||||
if self.order == "stand" or self.order == "sit" then
|
|
||||||
self.acc=vector.new(0,0,0)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local yaw = (self.object:get_yaw() or 0) + self.rotate
|
|
||||||
local vv = self.object:get_velocity()
|
|
||||||
if vv then
|
|
||||||
self.acc={
|
|
||||||
x = ((math.sin(yaw) * -v) + c_x)*.27,
|
|
||||||
y = 0,
|
|
||||||
z = ((math.cos(yaw) * v) + c_y)*.27,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- calculate mob velocity
|
|
||||||
function mob_class:get_velocity()
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if v then
|
|
||||||
return (v.x * v.x + v.z * v.z) ^ 0.5
|
|
||||||
end
|
|
||||||
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:update_roll()
|
|
||||||
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]
|
|
||||||
self.object:set_properties({collisionbox = cbox})
|
|
||||||
-- This leads to child mobs having the wrong collisionbox
|
|
||||||
-- and seeing as it seems to be nothing but an easter egg
|
|
||||||
-- i've put it inside the if. Which just makes it be upside
|
|
||||||
-- down lol.
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
local function shortest_term_of_yaw_rotation(self, rot_origin, rot_target, nums)
|
|
||||||
|
|
||||||
if not rot_origin or not rot_target then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
rot_origin = math.deg(rot_origin)
|
|
||||||
rot_target = math.deg(rot_target)
|
|
||||||
|
|
||||||
if rot_origin < rot_target then
|
|
||||||
if math.abs(rot_origin-rot_target)<180 then
|
|
||||||
if nums then
|
|
||||||
return rot_target-rot_origin
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if nums then
|
|
||||||
return -(rot_origin-(rot_target-360))
|
|
||||||
else
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if math.abs(rot_origin-rot_target)<180 then
|
|
||||||
if nums then
|
|
||||||
return rot_target-rot_origin
|
|
||||||
else
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if nums then
|
|
||||||
return (rot_target-(rot_origin-360))
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- set and return valid yaw
|
|
||||||
function mob_class:set_yaw(yaw, delay, dtime)
|
|
||||||
if self.noyaw then return end
|
|
||||||
|
|
||||||
if self.state ~= PATHFINDING then
|
|
||||||
self._turn_to = yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
--mcl_log("Yaw is: \t\t" .. tostring(math.deg(yaw)))
|
|
||||||
--mcl_log("self.object:get_yaw() is: \t" .. tostring(math.deg(self.object:get_yaw())))
|
|
||||||
|
|
||||||
--clamp our yaw to a 360 range
|
|
||||||
if math.deg(self.object:get_yaw()) > 360 then
|
|
||||||
self.object:set_yaw(math.rad(0))
|
|
||||||
elseif math.deg(self.object:get_yaw()) < 0 then
|
|
||||||
self.object:set_yaw(math.rad(360))
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.deg(yaw) > 360 then
|
|
||||||
yaw=yaw%360
|
|
||||||
elseif math.deg(yaw) < 0 then
|
|
||||||
yaw=((360*5)-yaw)%360
|
|
||||||
end
|
|
||||||
|
|
||||||
--calculate the shortest way to turn to find our target
|
|
||||||
local target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, false)
|
|
||||||
local target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, true)
|
|
||||||
|
|
||||||
--turn in the shortest path possible toward our target. if we are attacking, don't dance.
|
|
||||||
if (math.abs(target_shortest_path) > 50 and not self._kb_turn) and (self.attack and self.attack:get_pos() or self.following and self.following:get_pos()) then
|
|
||||||
if self.following then
|
|
||||||
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), true)
|
|
||||||
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), false)
|
|
||||||
else
|
|
||||||
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), true)
|
|
||||||
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ddtime = 0.05 --set_tick_rate
|
|
||||||
|
|
||||||
if dtime then
|
|
||||||
ddtime = dtime
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.abs(target_shortest_path_nums) > 5 then
|
|
||||||
self.object:set_yaw(self.object:get_yaw()+(target_shortest_path*(3.6*ddtime)))
|
|
||||||
if self.acc then
|
|
||||||
self.acc=vector.rotate_around_axis(self.acc,vector.new(0,1,0), target_shortest_path*(3.6*ddtime))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
delay = delay or 0
|
|
||||||
|
|
||||||
yaw = self.object:get_yaw()
|
|
||||||
|
|
||||||
if delay == 0 then
|
|
||||||
if self.shaking and dtime then
|
|
||||||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
|
||||||
end
|
|
||||||
--self:update_roll()
|
|
||||||
return yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
self.target_yaw = yaw
|
|
||||||
self.delay = delay
|
|
||||||
|
|
||||||
return self.target_yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
-- global function to set mob yaw
|
|
||||||
function mcl_mobs.yaw(self, yaw, delay, dtime)
|
|
||||||
return mob_class.set_yaw(self, yaw, delay, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- are we flying in what we are suppose to? (taikedz)
|
|
||||||
function mob_class:flight_check()
|
|
||||||
|
|
||||||
local nod = self.standing_in
|
|
||||||
local def = minetest.registered_nodes[nod]
|
|
||||||
|
|
||||||
if not def then return false end -- nil check
|
|
||||||
|
|
||||||
local fly_in
|
|
||||||
if type(self.fly_in) == "string" then
|
|
||||||
fly_in = { self.fly_in }
|
|
||||||
elseif type(self.fly_in) == "table" then
|
|
||||||
fly_in = self.fly_in
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,checknode in pairs(fly_in) do
|
|
||||||
if nod == checknode or nod == "ignore" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check if mob is dead or only hurt
|
|
||||||
function mob_class:check_for_death(cause, cmi_cause)
|
|
||||||
|
|
||||||
if self.state == "die" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- has health actually changed?
|
|
||||||
if self.health == self.old_health and self.health > 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local damaged = self.health < self.old_health
|
|
||||||
self.old_health = self.health
|
|
||||||
|
|
||||||
-- still got some health?
|
|
||||||
if self.health > 0 then
|
|
||||||
|
|
||||||
-- make sure health isn't higher than max
|
|
||||||
if self.health > self.hp_max then
|
|
||||||
self.health = self.hp_max
|
|
||||||
end
|
|
||||||
|
|
||||||
-- play damage sound if health was reduced and make mob flash red.
|
|
||||||
if damaged then
|
|
||||||
self:add_texture_mod("^[colorize:#d42222:175")
|
|
||||||
minetest.after(1, function(self)
|
|
||||||
if self and self.object then
|
|
||||||
self:remove_texture_mod("^[colorize:#d42222:175")
|
|
||||||
end
|
|
||||||
end, self)
|
|
||||||
self:mob_sound("damage")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- backup nametag so we can show health stats
|
|
||||||
if not self.nametag2 then
|
|
||||||
self.nametag2 = self.nametag or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
if show_health
|
|
||||||
and (cmi_cause and cmi_cause.type == "punch") then
|
|
||||||
|
|
||||||
self.htimer = 2
|
|
||||||
self.nametag = "♥ " .. self.health .. " / " .. self.hp_max
|
|
||||||
|
|
||||||
self:update_tag()
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
self:mob_sound("death")
|
|
||||||
|
|
||||||
local function death_handle(self)
|
|
||||||
-- dropped cooked item if mob died in fire or lava
|
|
||||||
if cause == "lava" or cause == "fire" then
|
|
||||||
self:item_drop(true, 0)
|
|
||||||
else
|
|
||||||
local wielditem = ItemStack()
|
|
||||||
if cause == "hit" then
|
|
||||||
local puncher = cmi_cause.puncher
|
|
||||||
if puncher then
|
|
||||||
wielditem = puncher:get_wielded_item()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local cooked = mcl_burning.is_burning(self.object) or mcl_enchanting.has_enchantment(wielditem, "fire_aspect")
|
|
||||||
local looting = mcl_enchanting.get_enchantment(wielditem, "looting")
|
|
||||||
self:item_drop(cooked, looting)
|
|
||||||
|
|
||||||
if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then
|
|
||||||
mcl_experience.throw_xp(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- execute custom death function
|
|
||||||
if self.on_die then
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local on_die_exit = self.on_die(self, pos, cmi_cause)
|
|
||||||
if on_die_exit ~= true then
|
|
||||||
death_handle(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
if on_die_exit == true then
|
|
||||||
self.state = "die"
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local collisionbox
|
|
||||||
if self.collisionbox then
|
|
||||||
collisionbox = table.copy(self.collisionbox)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.state = "die"
|
|
||||||
self.attack = nil
|
|
||||||
self.v_start = false
|
|
||||||
self.fall_speed = DEFAULT_FALL_SPEED
|
|
||||||
self.timer = 0
|
|
||||||
self.blinktimer = 0
|
|
||||||
self:remove_texture_mod("^[colorize:#FF000040")
|
|
||||||
self:remove_texture_mod("^[brighten")
|
|
||||||
self.passive = true
|
|
||||||
|
|
||||||
self.object:set_properties({
|
|
||||||
pointable = false,
|
|
||||||
collide_with_objects = false,
|
|
||||||
})
|
|
||||||
|
|
||||||
self:set_velocity(0)
|
|
||||||
local acc = self.object:get_acceleration()
|
|
||||||
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
|
|
||||||
self.object:set_acceleration(acc)
|
|
||||||
|
|
||||||
local length
|
|
||||||
-- default death function and die animation (if defined)
|
|
||||||
if self.instant_death then
|
|
||||||
length = 0
|
|
||||||
elseif self.animation
|
|
||||||
and self.animation.die_start
|
|
||||||
and self.animation.die_end then
|
|
||||||
|
|
||||||
local frames = self.animation.die_end - self.animation.die_start
|
|
||||||
local speed = self.animation.die_speed or 15
|
|
||||||
length = math.max(frames / speed, 0) + DEATH_DELAY
|
|
||||||
self:set_animation( "die")
|
|
||||||
else
|
|
||||||
length = 1 + DEATH_DELAY
|
|
||||||
self:set_animation( "stand", true)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Remove body after a few seconds and drop stuff
|
|
||||||
local kill = function(self)
|
|
||||||
if not self.object:get_luaentity() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
death_handle(self)
|
|
||||||
local dpos = self.object:get_pos()
|
|
||||||
local cbox = self.collisionbox
|
|
||||||
local yaw = self.object:get_rotation().y
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
mcl_mobs.death_effect(dpos, yaw, cbox, not self.instant_death)
|
|
||||||
end
|
|
||||||
if length <= 0 then
|
|
||||||
kill(self)
|
|
||||||
else
|
|
||||||
minetest.after(length, kill, self)
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Deal light damage to mob, returns true if mob died
|
|
||||||
function mob_class:deal_light_damage(pos, damage)
|
|
||||||
if not ((mcl_weather.rain.raining or mcl_weather.state == "snow") and mcl_weather.is_outdoor(pos)) then
|
|
||||||
self.health = self.health - damage
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
|
||||||
|
|
||||||
if self:check_for_death("light", {type = "light"}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- environmental damage (water, lava, fire, light etc.)
|
|
||||||
function mob_class:do_env_damage()
|
|
||||||
-- feed/tame text timer (so mob 'full' messages dont spam chat)
|
|
||||||
if self.htimer > 0 then
|
|
||||||
self.htimer = self.htimer - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- reset nametag after showing health stats
|
|
||||||
if self.htimer < 1 and self.nametag2 then
|
|
||||||
|
|
||||||
self.nametag = self.nametag2
|
|
||||||
self.nametag2 = nil
|
|
||||||
|
|
||||||
self:update_tag()
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
if not pos then return end
|
|
||||||
|
|
||||||
self.time_of_day = minetest.get_timeofday()
|
|
||||||
|
|
||||||
-- remove mob if beyond map limits
|
|
||||||
if not within_limits(pos, 0) then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local sunlight = minetest.get_natural_light(pos, self.time_of_day)
|
|
||||||
|
|
||||||
-- bright light harms mob
|
|
||||||
if self.light_damage ~= 0 and (sunlight or 0) > 12 then
|
|
||||||
if self:deal_light_damage(pos, self.light_damage) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
|
||||||
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (sunlight or 0) >= minetest.LIGHT_MAX and dim == "overworld" then
|
|
||||||
if self.armor_list and not self.armor_list.helmet or not self.armor_list or self.armor_list and self.armor_list.helmet and self.armor_list.helmet == "" then
|
|
||||||
if self.ignited_by_sunlight then
|
|
||||||
mcl_burning.set_on_fire(self.object, 10)
|
|
||||||
else
|
|
||||||
self:deal_light_damage(pos, self.sunlight_damage)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local y_level = self.collisionbox[2]
|
|
||||||
|
|
||||||
if self.child then
|
|
||||||
y_level = self.collisionbox[2] * 0.5
|
|
||||||
end
|
|
||||||
|
|
||||||
-- what is mob standing in?
|
|
||||||
pos.y = pos.y + y_level + 0.25 -- foot level
|
|
||||||
local pos2 = {x=pos.x, y=pos.y-1, z=pos.z}
|
|
||||||
self.standing_in = node_ok(pos, "air").name
|
|
||||||
self.standing_on = node_ok(pos2, "air").name
|
|
||||||
|
|
||||||
-- don't fall when on ignore, just stand still
|
|
||||||
if self.standing_in == "ignore" then
|
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
end
|
|
||||||
|
|
||||||
local nodef = minetest.registered_nodes[self.standing_in]
|
|
||||||
|
|
||||||
-- rain
|
|
||||||
if self.rain_damage > 0 then
|
|
||||||
if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
|
|
||||||
|
|
||||||
self.health = self.health - self.rain_damage
|
|
||||||
|
|
||||||
if self:check_for_death("rain", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
pos.y = pos.y + 1 -- for particle effect position
|
|
||||||
|
|
||||||
-- water damage
|
|
||||||
if self.water_damage > 0
|
|
||||||
and nodef.groups.water then
|
|
||||||
|
|
||||||
if self.water_damage ~= 0 then
|
|
||||||
|
|
||||||
self.health = self.health - self.water_damage
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png", nil, nil, 1, nil)
|
|
||||||
|
|
||||||
if self:check_for_death("water", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- lava damage
|
|
||||||
elseif self.lava_damage > 0
|
|
||||||
and (nodef.groups.lava) then
|
|
||||||
|
|
||||||
if self.lava_damage ~= 0 then
|
|
||||||
|
|
||||||
self.health = self.health - self.lava_damage
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
|
||||||
mcl_burning.set_on_fire(self.object, 10)
|
|
||||||
|
|
||||||
if self:check_for_death("lava", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- fire damage
|
|
||||||
elseif self.fire_damage > 0
|
|
||||||
and (nodef.groups.fire) then
|
|
||||||
|
|
||||||
if self.fire_damage ~= 0 then
|
|
||||||
|
|
||||||
self.health = self.health - self.fire_damage
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
|
||||||
mcl_burning.set_on_fire(self.object, 5)
|
|
||||||
|
|
||||||
if self:check_for_death("fire", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- damage_per_second node check
|
|
||||||
elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then
|
|
||||||
|
|
||||||
self.health = self.health - nodef.damage_per_second
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
|
||||||
|
|
||||||
if self:check_for_death("dps", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Drowning damage
|
|
||||||
if self.breath_max ~= -1 then
|
|
||||||
local drowning = false
|
|
||||||
if self.breathes_in_water then
|
|
||||||
if minetest.get_item_group(self.standing_in, "water") == 0 then
|
|
||||||
drowning = true
|
|
||||||
end
|
|
||||||
elseif nodef.drowning > 0 then
|
|
||||||
drowning = true
|
|
||||||
end
|
|
||||||
if drowning then
|
|
||||||
|
|
||||||
self.breath = math.max(0, self.breath - 1)
|
|
||||||
|
|
||||||
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
|
||||||
if self.breath <= 0 then
|
|
||||||
local dmg
|
|
||||||
if nodef.drowning > 0 then
|
|
||||||
dmg = nodef.drowning
|
|
||||||
else
|
|
||||||
dmg = 4
|
|
||||||
end
|
|
||||||
self:damage_effect(dmg)
|
|
||||||
self.health = self.health - dmg
|
|
||||||
end
|
|
||||||
if self:check_for_death("drowning", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.breath = math.min(self.breath_max, self.breath + 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- suffocation inside solid node
|
|
||||||
-- FIXME: Redundant with mcl_playerplus
|
|
||||||
if (self.suffocation == true)
|
|
||||||
and (nodef.walkable == nil or nodef.walkable == true)
|
|
||||||
and (nodef.collision_box == nil or nodef.collision_box.type == "regular")
|
|
||||||
and (nodef.node_box == nil or nodef.node_box.type == "regular")
|
|
||||||
and (nodef.groups.disable_suffocation ~= 1)
|
|
||||||
and (nodef.groups.opaque == 1) then
|
|
||||||
|
|
||||||
-- Short grace period before starting to take suffocation damage.
|
|
||||||
-- This is different from players, who take damage instantly.
|
|
||||||
-- This has been done because mobs might briefly be inside solid nodes
|
|
||||||
-- when e.g. climbing up stairs.
|
|
||||||
-- This is a bit hacky because it assumes that do_env_damage
|
|
||||||
-- is called roughly every second only.
|
|
||||||
self.suffocation_timer = self.suffocation_timer + 1
|
|
||||||
if self.suffocation_timer >= 3 then
|
|
||||||
-- 2 damage per second
|
|
||||||
-- TODO: Deal this damage once every 1/2 second
|
|
||||||
self.health = self.health - 2
|
|
||||||
|
|
||||||
if self:check_for_death("suffocation", {type = "environment",
|
|
||||||
pos = pos, node = self.standing_in}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.suffocation_timer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return self:check_for_death("", {type = "unknown"})
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:damage_mob(reason,damage)
|
|
||||||
if not self.health then return end
|
|
||||||
damage = math.floor(damage)
|
|
||||||
if damage > 0 then
|
|
||||||
self.health = self.health - damage
|
|
||||||
|
|
||||||
mcl_mobs.effect(self.object:get_pos(), 5, "mcl_particles_smoke.png", 1, 2, 2, nil)
|
|
||||||
|
|
||||||
if self:check_for_death(reason, {type = reason}) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_entity_cramming()
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
if not p then return end
|
|
||||||
local oo = minetest.get_objects_inside_radius(p,1)
|
|
||||||
local mobs = {}
|
|
||||||
for _,o in pairs(oo) do
|
|
||||||
local l = o:get_luaentity()
|
|
||||||
if l and l.is_mob and l.health > 0 then table.insert(mobs,l) end
|
|
||||||
end
|
|
||||||
local clear = #mobs < ENTITY_CRAMMING_MAX
|
|
||||||
local ncram = {}
|
|
||||||
for _,l in pairs(mobs) do
|
|
||||||
if l then
|
|
||||||
if clear then
|
|
||||||
l.cram = nil
|
|
||||||
elseif l.cram == nil and not self.child then
|
|
||||||
table.insert(ncram,l)
|
|
||||||
elseif l.cram then
|
|
||||||
l:damage_mob("cramming",CRAMMING_DAMAGE)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for i,l in pairs(ncram) do
|
|
||||||
if i > ENTITY_CRAMMING_MAX then
|
|
||||||
l.cram = true
|
|
||||||
else
|
|
||||||
l.cram = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- falling and fall damage
|
|
||||||
-- returns true if mob died
|
|
||||||
function mob_class:falling(pos)
|
|
||||||
|
|
||||||
if self.fly and self.state ~= "die" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED end
|
|
||||||
|
|
||||||
if mcl_portals ~= nil then
|
|
||||||
if mcl_portals.nether_portal_cooloff(self.object) then
|
|
||||||
return false -- mob has teleported through Nether portal - it's 99% not falling
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- floating in water (or falling)
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
|
|
||||||
if v.y > 0 then
|
|
||||||
|
|
||||||
-- apply gravity when moving up
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = 0,
|
|
||||||
y = DEFAULT_FALL_SPEED,
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
|
|
||||||
elseif v.y <= 0 and v.y > self.fall_speed then
|
|
||||||
|
|
||||||
-- fall downwards at set speed
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = 0,
|
|
||||||
y = self.fall_speed,
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
else
|
|
||||||
-- stop accelerating once max fall speed hit
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
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 / (math.max(1, v.y) ^ 2),
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- in water then float up
|
|
||||||
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
|
||||||
|
|
||||||
if self.floats == 1 then
|
|
||||||
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = 0,
|
|
||||||
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
|
|
||||||
z = 0
|
|
||||||
})
|
|
||||||
end
|
|
||||||
else
|
|
||||||
|
|
||||||
-- fall damage onto solid ground
|
|
||||||
if self.fall_damage == 1
|
|
||||||
and self.object:get_velocity().y == 0 then
|
|
||||||
local n = node_ok(vector.offset(pos,0,-1,0)).name
|
|
||||||
local d = (self.old_y or 0) - self.object:get_pos().y
|
|
||||||
|
|
||||||
if d > 5 and n ~= "air" and n ~= "ignore" then
|
|
||||||
local add = minetest.get_item_group(self.standing_on, "fall_damage_add_percent")
|
|
||||||
local damage = d - 5
|
|
||||||
if add ~= 0 then
|
|
||||||
damage = damage + damage * (add/100)
|
|
||||||
end
|
|
||||||
self:damage_mob("fall",damage)
|
|
||||||
end
|
|
||||||
|
|
||||||
self.old_y = self.object:get_pos().y
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_water_flow()
|
|
||||||
-- Add water flowing for mobs from mcl_item_entity
|
|
||||||
local p, node, nn, def
|
|
||||||
p = self.object:get_pos()
|
|
||||||
node = minetest.get_node_or_nil(p)
|
|
||||||
if node then
|
|
||||||
nn = node.name
|
|
||||||
def = minetest.registered_nodes[nn]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Move item around on flowing liquids
|
|
||||||
if def and def.liquidtype == "flowing" then
|
|
||||||
|
|
||||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
|
||||||
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
|
||||||
Luckily, this is exactly what we need if we only care about water, which has this flowing distance. ]]
|
|
||||||
local vec = flowlib.quick_flow(p, node)
|
|
||||||
-- Just to make sure we don't manipulate the speed for no reason
|
|
||||||
if vec.x ~= 0 or vec.y ~= 0 or vec.z ~= 0 then
|
|
||||||
-- Minecraft Wiki: Flowing speed is "about 1.39 meters per second"
|
|
||||||
local f = 1.39
|
|
||||||
-- Set new item moving speed into the direciton of the liquid
|
|
||||||
local newv = vector.multiply(vec, f)
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
self.object:set_velocity({x = newv.x, y = -0.22, z = newv.z})
|
|
||||||
|
|
||||||
self.physical_state = true
|
|
||||||
self._flowing = true
|
|
||||||
self.object:set_properties({
|
|
||||||
physical = true
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
elseif self._flowing == true then
|
|
||||||
-- Disable flowing physics if not on/in flowing liquid
|
|
||||||
self._flowing = false
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_dying()
|
|
||||||
if ((self.state and self.state=="die") or self:check_for_death()) and not self.animation.die_end then
|
|
||||||
local rot = self.object:get_rotation()
|
|
||||||
rot.z = ((math.pi/2-rot.z)*.2)+rot.z
|
|
||||||
self.object:set_rotation(rot)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mob_class:check_suspend()
|
|
||||||
if not self:player_in_active_range() then
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local node_under = node_ok(vector.offset(pos,0,-1,0)).name
|
|
||||||
local acc = self.object:get_acceleration()
|
|
||||||
self:set_animation( "stand", true)
|
|
||||||
if acc.y > 0 or node_under ~= "air" then
|
|
||||||
self.object:set_acceleration(vector.new(0,0,0))
|
|
||||||
self.object:set_velocity(vector.new(0,0,0))
|
|
||||||
end
|
|
||||||
if acc.y == 0 and node_under == "air" then
|
|
||||||
self:falling(pos)
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,7 +1,4 @@
|
||||||
--lua locals
|
--lua locals
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
|
||||||
local mob_class = mcl_mobs.mob_class
|
|
||||||
|
|
||||||
local get_node = minetest.get_node
|
local get_node = minetest.get_node
|
||||||
local get_item_group = minetest.get_item_group
|
local get_item_group = minetest.get_item_group
|
||||||
local get_node_light = minetest.get_node_light
|
local get_node_light = minetest.get_node_light
|
||||||
|
@ -18,7 +15,7 @@ local math_cos = math.cos
|
||||||
local math_sin = math.sin
|
local math_sin = math.sin
|
||||||
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
|
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
|
||||||
|
|
||||||
local vector_distance = vector.distance
|
--local vector_distance = vector.distance
|
||||||
local vector_new = vector.new
|
local vector_new = vector.new
|
||||||
local vector_floor = vector.floor
|
local vector_floor = vector.floor
|
||||||
|
|
||||||
|
@ -26,27 +23,13 @@ local table_copy = table.copy
|
||||||
local table_remove = table.remove
|
local table_remove = table.remove
|
||||||
|
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
local dbg_spawn_attempts = 0
|
|
||||||
local dbg_spawn_succ = 0
|
|
||||||
local dbg_spawn_counts = {}
|
|
||||||
-- range for mob count
|
|
||||||
local aoc_range = 136
|
|
||||||
local remove_far = true
|
|
||||||
|
|
||||||
local mob_cap = {
|
-- range for mob count
|
||||||
monster = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
local aoc_range = 32
|
||||||
animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10,
|
|
||||||
ambient = tonumber(minetest.settings:get("mcl_mob_cap_ambient")) or 15,
|
|
||||||
water = tonumber(minetest.settings:get("mcl_mob_cap_water")) or 5, --currently unused
|
|
||||||
water_ambient = tonumber(minetest.settings:get("mcl_mob_cap_water_ambient")) or 20, --currently unused
|
|
||||||
player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75,
|
|
||||||
total = tonumber(minetest.settings:get("mcl_mob_cap_total")) or 500,
|
|
||||||
}
|
|
||||||
|
|
||||||
--do mobs spawn?
|
--do mobs spawn?
|
||||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||||
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
|
|
||||||
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
|
|
||||||
|
|
||||||
local noise_params = {
|
local noise_params = {
|
||||||
offset = 0,
|
offset = 0,
|
||||||
|
@ -80,7 +63,6 @@ local list_of_all_biomes = {
|
||||||
"JungleM_underground",
|
"JungleM_underground",
|
||||||
"ExtremeHillsM_underground",
|
"ExtremeHillsM_underground",
|
||||||
"JungleEdgeM_underground",
|
"JungleEdgeM_underground",
|
||||||
"MangroveSwamp_underground",
|
|
||||||
|
|
||||||
-- ocean:
|
-- ocean:
|
||||||
|
|
||||||
|
@ -144,8 +126,6 @@ local list_of_all_biomes = {
|
||||||
"BirchForestM_deep_ocean",
|
"BirchForestM_deep_ocean",
|
||||||
"Taiga_deep_ocean",
|
"Taiga_deep_ocean",
|
||||||
"JungleM_ocean",
|
"JungleM_ocean",
|
||||||
"MangroveSwamp_ocean",
|
|
||||||
"MangroveSwamp_deep_ocean",
|
|
||||||
|
|
||||||
-- water or beach?
|
-- water or beach?
|
||||||
|
|
||||||
|
@ -169,15 +149,10 @@ local list_of_all_biomes = {
|
||||||
"MushroomIslandShore",
|
"MushroomIslandShore",
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
"MangroveSwamp_shore",
|
|
||||||
|
|
||||||
-- dimension biome:
|
-- dimension biome:
|
||||||
|
|
||||||
"Nether",
|
"Nether",
|
||||||
"BasaltDelta",
|
|
||||||
"CrimsonForest",
|
|
||||||
"WarpedForest",
|
|
||||||
"SoulsandValley",
|
|
||||||
"End",
|
"End",
|
||||||
|
|
||||||
-- Overworld regular:
|
-- Overworld regular:
|
||||||
|
@ -187,8 +162,6 @@ local list_of_all_biomes = {
|
||||||
"Swampland",
|
"Swampland",
|
||||||
"Taiga",
|
"Taiga",
|
||||||
"ExtremeHills",
|
"ExtremeHills",
|
||||||
"ExtremeHillsM",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
"Jungle",
|
"Jungle",
|
||||||
"Savanna",
|
"Savanna",
|
||||||
"BirchForest",
|
"BirchForest",
|
||||||
|
@ -207,6 +180,7 @@ local list_of_all_biomes = {
|
||||||
"ExtremeHills+_snowtop",
|
"ExtremeHills+_snowtop",
|
||||||
"MesaPlateauFM_grasstop",
|
"MesaPlateauFM_grasstop",
|
||||||
"JungleEdgeM",
|
"JungleEdgeM",
|
||||||
|
"ExtremeHillsM",
|
||||||
"JungleM",
|
"JungleM",
|
||||||
"BirchForestM",
|
"BirchForestM",
|
||||||
"MesaPlateauF",
|
"MesaPlateauF",
|
||||||
|
@ -215,46 +189,22 @@ local list_of_all_biomes = {
|
||||||
"MesaBryce",
|
"MesaBryce",
|
||||||
"JungleEdge",
|
"JungleEdge",
|
||||||
"SavannaM",
|
"SavannaM",
|
||||||
"MangroveSwamp",
|
"Nether",
|
||||||
|
"WarpedForest",
|
||||||
|
"SoulsandValley"
|
||||||
}
|
}
|
||||||
|
|
||||||
-- count how many mobs are in an area
|
-- count how many mobs are in an area
|
||||||
local function count_mobs(pos,r,mob_type)
|
local function count_mobs(pos)
|
||||||
local num = 0
|
local num = 0
|
||||||
for _,l in pairs(minetest.luaentities) do
|
for _,object in pairs(get_objects_inside_radius(pos, aoc_range)) do
|
||||||
if l and l.is_mob and (mob_type == nil or l.type == mob_type) then
|
if object and object:get_luaentity() and object:get_luaentity().is_mob then
|
||||||
local p = l.object:get_pos()
|
num = num + 1
|
||||||
if p and vector_distance(p,pos) < r then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return num
|
return num
|
||||||
end
|
end
|
||||||
|
|
||||||
local function count_mobs_total(mob_type)
|
|
||||||
local num = 0
|
|
||||||
for _,l in pairs(minetest.luaentities) do
|
|
||||||
if l.is_mob then
|
|
||||||
if mob_type == nil or l.type == mob_type then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return num
|
|
||||||
end
|
|
||||||
|
|
||||||
local function count_mobs_total_cap(mob_type)
|
|
||||||
local num = 0
|
|
||||||
for _,l in pairs(minetest.luaentities) do
|
|
||||||
if l.is_mob then
|
|
||||||
if ( mob_type == nil or l.type == mob_type ) and l.can_despawn and not l.nametag then
|
|
||||||
num = num + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return num
|
|
||||||
end
|
|
||||||
|
|
||||||
-- global functions
|
-- global functions
|
||||||
|
|
||||||
|
@ -342,7 +292,7 @@ function mcl_mobs:spawn_setup(def)
|
||||||
spawn_dictionary[#spawn_dictionary + 1] = {
|
spawn_dictionary[#spawn_dictionary + 1] = {
|
||||||
name = name,
|
name = name,
|
||||||
dimension = dimension,
|
dimension = dimension,
|
||||||
type_of_spawning = type_of_spawning,
|
type_of_spawning = type_of_spawning,
|
||||||
biomes = biomes,
|
biomes = biomes,
|
||||||
min_light = min_light,
|
min_light = min_light,
|
||||||
max_light = max_light,
|
max_light = max_light,
|
||||||
|
@ -429,217 +379,13 @@ local function is_farm_animal(n)
|
||||||
return n == "mobs_mc:pig" or n == "mobs_mc:cow" or n == "mobs_mc:sheep" or n == "mobs_mc:chicken" or n == "mobs_mc:horse" or n == "mobs_mc:donkey"
|
return n == "mobs_mc:pig" or n == "mobs_mc:cow" or n == "mobs_mc:sheep" or n == "mobs_mc:chicken" or n == "mobs_mc:horse" or n == "mobs_mc:donkey"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_water_spawn(p)
|
|
||||||
local nn = minetest.find_nodes_in_area(vector.offset(p,-2,-1,-2),vector.offset(p,2,-15,2),{"group:water"})
|
|
||||||
if nn and #nn > 0 then
|
|
||||||
return nn[math.random(#nn)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function has_room(self,pos)
|
|
||||||
local cb = self.collisionbox
|
|
||||||
local nodes = {}
|
|
||||||
if self.fly_in then
|
|
||||||
local t = type(self.fly_in)
|
|
||||||
if t == "table" then
|
|
||||||
nodes = table.copy(self.fly_in)
|
|
||||||
elseif t == "string" then
|
|
||||||
table.insert(nodes,self.fly_in)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(nodes,"air")
|
|
||||||
local x = cb[4] - cb[1]
|
|
||||||
local y = cb[5] - cb[2]
|
|
||||||
local z = cb[6] - cb[3]
|
|
||||||
local r = math.ceil(x * y * z)
|
|
||||||
local p1 = vector.offset(pos,cb[1],cb[2],cb[3])
|
|
||||||
local p2 = vector.offset(pos,cb[4],cb[5],cb[6])
|
|
||||||
local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0
|
|
||||||
if r > n then
|
|
||||||
minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos)))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function spawn_check(pos,spawn_def,ignore_caps)
|
|
||||||
if not spawn_def then return end
|
|
||||||
dbg_spawn_attempts = dbg_spawn_attempts + 1
|
|
||||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
|
||||||
local mob_def = minetest.registered_entities[spawn_def.name]
|
|
||||||
local mob_type = mob_def.type
|
|
||||||
local gotten_node = get_node(pos).name
|
|
||||||
local gotten_biome = minetest.get_biome_data(pos)
|
|
||||||
if not gotten_node or not gotten_biome then return end
|
|
||||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
|
||||||
|
|
||||||
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
|
||||||
if not is_ground then
|
|
||||||
pos.y = pos.y - 1
|
|
||||||
gotten_node = get_node(pos).name
|
|
||||||
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
|
||||||
end
|
|
||||||
pos.y = pos.y + 1
|
|
||||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
|
||||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
|
||||||
local is_leaf = get_item_group(gotten_node, "leaves") ~= 0
|
|
||||||
local is_bedrock = gotten_node == "mcl_core:bedrock"
|
|
||||||
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
|
|
||||||
local mob_count_wide = 0
|
|
||||||
|
|
||||||
local mob_count = 0
|
|
||||||
if not ignore_caps then
|
|
||||||
mob_count = count_mobs(pos,32,mob_type)
|
|
||||||
mob_count_wide = count_mobs(pos,aoc_range,mob_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
if pos and spawn_def
|
|
||||||
and ( mob_count_wide < (mob_cap[mob_type] or 15) )
|
|
||||||
and ( mob_count < 5 )
|
|
||||||
and pos.y >= spawn_def.min_height
|
|
||||||
and pos.y <= spawn_def.max_height
|
|
||||||
and spawn_def.dimension == dimension
|
|
||||||
and biome_check(spawn_def.biomes, gotten_biome)
|
|
||||||
and (is_ground or spawn_def.type_of_spawning ~= "ground")
|
|
||||||
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf)
|
|
||||||
and has_room(mob_def,pos)
|
|
||||||
and (spawn_def.check_position and spawn_def.check_position(pos) or true)
|
|
||||||
and (not is_farm_animal(spawn_def.name) or is_grass)
|
|
||||||
and (spawn_def.type_of_spawning ~= "water" or is_water)
|
|
||||||
and ( not spawn_protected or not minetest.is_protected(pos, "") )
|
|
||||||
and not is_bedrock then
|
|
||||||
--only need to poll for node light if everything else worked
|
|
||||||
local gotten_light = get_node_light(pos)
|
|
||||||
if gotten_light >= spawn_def.min_light and gotten_light <= spawn_def.max_light then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.spawn(pos,id)
|
|
||||||
local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id]
|
|
||||||
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not dbg_spawn_counts[def.name] then
|
|
||||||
dbg_spawn_counts[def.name] = 1
|
|
||||||
else
|
|
||||||
dbg_spawn_counts[def.name] = dbg_spawn_counts[def.name] + 1
|
|
||||||
end
|
|
||||||
return minetest.add_entity(pos, def.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function spawn_group(p,mob,spawn_on,group_max,group_min)
|
|
||||||
if not group_min then group_min = 1 end
|
|
||||||
local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on)
|
|
||||||
local o
|
|
||||||
table.shuffle(nn)
|
|
||||||
if not nn or #nn < 1 then
|
|
||||||
nn = {}
|
|
||||||
table.insert(nn,p)
|
|
||||||
end
|
|
||||||
for i = 1, math.random(group_min,group_max) do
|
|
||||||
local sp = vector.offset(nn[math.random(#nn)],0,1,0)
|
|
||||||
if spawn_check(nn[math.random(#nn)],mob,true) then
|
|
||||||
if mob.type_of_spawning == "water" then
|
|
||||||
sp = get_water_spawn(sp)
|
|
||||||
end
|
|
||||||
o = mcl_mobs.spawn(sp,mob.name)
|
|
||||||
if o then dbg_spawn_succ = dbg_spawn_succ + 1 end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mobs.spawn_group = spawn_group
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mcl_mobs")
|
|
||||||
|
|
||||||
minetest.register_chatcommand("spawn_mob",{
|
|
||||||
privs = { debug = true },
|
|
||||||
description=S("spawn_mob is a chatcommand that allows you to type in the name of a mob without 'typing mobs_mc:' all the time like so; 'spawn_mob spider'. however, there is more you can do with this special command, currently you can edit any number, boolian, and string variable you choose with this format: spawn_mob 'any_mob:var<mobs_variable=variable_value>:'. any_mob being your mob of choice, mobs_variable being the variable, and variable value being the value of the chosen variable. and example of this format: \n spawn_mob skeleton:var<passive=true>:\n this would spawn a skeleton that wouldn't attack you. REMEMBER-THIS> when changing a number value always prefix it with 'NUM', example: \n spawn_mob skeleton:var<jump_height=NUM10>:\n this setting the skelly's jump height to 10. if you want to make multiple changes to a mob, you can, example: \n spawn_mob skeleton:var<passive=true>::var<jump_height=NUM10>::var<fly_in=air>::var<fly=true>:\n etc."),
|
|
||||||
func = function(n,param)
|
|
||||||
local pos = minetest.get_player_by_name(n):get_pos()
|
|
||||||
|
|
||||||
local modifiers = {}
|
|
||||||
for capture in string.gmatch(param, "%:(.-)%:") do
|
|
||||||
table.insert(modifiers, ":"..capture)
|
|
||||||
end
|
|
||||||
|
|
||||||
local mod1 = string.find(param, ":")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local mobname = param
|
|
||||||
if mod1 then
|
|
||||||
mobname = string.sub(param, 1, mod1-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local mob = mcl_mobs.spawn(pos,mobname)
|
|
||||||
|
|
||||||
for c=1, #modifiers do
|
|
||||||
modifs = modifiers[c]
|
|
||||||
|
|
||||||
local mod1 = string.find(modifs, ":")
|
|
||||||
local mod_start = string.find(modifs, "<")
|
|
||||||
local mod_vals = string.find(modifs, "=")
|
|
||||||
local mod_end = string.find(modifs, ">")
|
|
||||||
local mod_end = string.find(modifs, ">")
|
|
||||||
if mob then
|
|
||||||
local mob_entity = mob:get_luaentity()
|
|
||||||
if string.sub(modifs, mod1+1, mod1+3) == "var" then
|
|
||||||
if mod1 and mod_start and mod_vals and mod_end then
|
|
||||||
local variable = string.sub(modifs, mod_start+1, mod_vals-1)
|
|
||||||
local value = string.sub(modifs, mod_vals+1, mod_end-1)
|
|
||||||
|
|
||||||
number_tag = string.find(value, "NUM")
|
|
||||||
if number_tag then
|
|
||||||
value = tonumber(string.sub(value, 4, -1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if value == "true" then
|
|
||||||
value = true
|
|
||||||
elseif value == "false" then
|
|
||||||
value = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if not mob_entity[variable] then
|
|
||||||
minetest.log("warning", n.." mob variable "..variable.." previously unset")
|
|
||||||
end
|
|
||||||
|
|
||||||
mob_entity[variable] = value
|
|
||||||
|
|
||||||
else
|
|
||||||
minetest.log("warning", n.." couldn't modify "..mobname.." at "..minetest.pos_to_string(pos).. ", missing paramaters")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
minetest.log("warning", n.." couldn't modify "..mobname.." at "..minetest.pos_to_string(pos).. ", missing modification type")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if mob then
|
|
||||||
return true, mobname.." spawned at "..minetest.pos_to_string(pos),
|
|
||||||
minetest.log("action", n.." spawned "..mobname.." at "..minetest.pos_to_string(pos))
|
|
||||||
end
|
|
||||||
return false, "Couldn't spawn "..mobname
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
if mobs_spawn then
|
if mobs_spawn then
|
||||||
|
|
||||||
local perlin_noise
|
local perlin_noise
|
||||||
|
|
||||||
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
--create a disconnected clone of the spawn dictionary
|
local dimension = dimension or mcl_worlds.pos_to_dimension(pos)
|
||||||
--prevents memory leak
|
|
||||||
local mob_library_worker_table = table_copy(spawn_dictionary)
|
|
||||||
local goal_pos = get_next_mob_spawn_pos(pos)
|
local goal_pos = get_next_mob_spawn_pos(pos)
|
||||||
--grab mob that fits into the spawning location
|
|
||||||
--randomly grab a mob, don't exclude any possibilities
|
|
||||||
local spawning_position_list = find_nodes_in_area_under_air(
|
local spawning_position_list = find_nodes_in_area_under_air(
|
||||||
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
||||||
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
||||||
|
@ -648,6 +394,38 @@ if mobs_spawn then
|
||||||
if #spawning_position_list <= 0 then return end
|
if #spawning_position_list <= 0 then return end
|
||||||
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
||||||
|
|
||||||
|
--hard code mob limit in area to 5 for now
|
||||||
|
if count_mobs(spawning_position) >= 5 then return end
|
||||||
|
|
||||||
|
local gotten_node = get_node(spawning_position).name
|
||||||
|
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||||
|
if not gotten_node or not gotten_biome then return end
|
||||||
|
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||||
|
|
||||||
|
--add this so mobs don't spawn inside nodes
|
||||||
|
spawning_position.y = spawning_position.y + 1
|
||||||
|
|
||||||
|
--only need to poll for node light if everything else worked
|
||||||
|
local gotten_light = get_node_light(spawning_position)
|
||||||
|
|
||||||
|
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||||
|
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||||
|
local is_ground = not (is_water or is_lava)
|
||||||
|
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
|
||||||
|
local has_bed = minetest.find_node_near(pos,25,{"group:bed"})
|
||||||
|
|
||||||
|
if not is_ground then
|
||||||
|
spawning_position.y = spawning_position.y - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local mob_def
|
||||||
|
|
||||||
|
--create a disconnected clone of the spawn dictionary
|
||||||
|
--prevents memory leak
|
||||||
|
local mob_library_worker_table = table_copy(spawn_dictionary)
|
||||||
|
|
||||||
|
--grab mob that fits into the spawning location
|
||||||
|
--randomly grab a mob, don't exclude any possibilities
|
||||||
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
||||||
local noise = perlin_noise:get_3d(spawning_position)
|
local noise = perlin_noise:get_3d(spawning_position)
|
||||||
local current_summary_chance = summary_chance
|
local current_summary_chance = summary_chance
|
||||||
|
@ -663,38 +441,24 @@ if mobs_spawn then
|
||||||
step_chance = step_chance + mob_chance
|
step_chance = step_chance + mob_chance
|
||||||
end
|
end
|
||||||
local mob_def = mob_library_worker_table[mob_index]
|
local mob_def = mob_library_worker_table[mob_index]
|
||||||
--minetest.log(mob_def.name.." "..step_chance.. " "..mob_chance)
|
local mob_type = minetest.registered_entities[mob_def.name].type
|
||||||
if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then
|
if mob_def
|
||||||
local spawn_in_group = minetest.registered_entities[mob_def.name].spawn_in_group or 4
|
and spawning_position.y >= mob_def.min_height
|
||||||
local spawn_in_group_min = minetest.registered_entities[mob_def.name].spawn_in_group_min or 1
|
and spawning_position.y <= mob_def.max_height
|
||||||
local mob_type = minetest.registered_entities[mob_def.name].type
|
and mob_def.dimension == dimension
|
||||||
if spawn_check(spawning_position,mob_def) then
|
and biome_check(mob_def.biomes, gotten_biome)
|
||||||
if mob_def.type_of_spawning == "water" then
|
and gotten_light >= mob_def.min_light
|
||||||
spawning_position = get_water_spawn(spawning_position)
|
and gotten_light <= mob_def.max_light
|
||||||
if not spawning_position then
|
and (is_ground or mob_def.type_of_spawning ~= "ground")
|
||||||
minetest.log("warning","[mcl_mobs] no water spawn for mob "..mob_def.name.." found at "..minetest.pos_to_string(vector.round(pos)))
|
and (mob_def.check_position and mob_def.check_position(spawning_position) or true)
|
||||||
return
|
and (not is_farm_animal(mob_def.name) or is_grass)
|
||||||
end
|
and (mob_type ~= "npc" or has_bed)
|
||||||
end
|
then
|
||||||
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(spawning_position) then
|
|
||||||
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
--everything is correct, spawn mob
|
--everything is correct, spawn mob
|
||||||
local object
|
local object = minetest.add_entity(spawning_position, mob_def.name)
|
||||||
if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then
|
if object then
|
||||||
if logging then
|
return mob_def.on_spawn and mob_def.on_spawn(object, pos)
|
||||||
minetest.log("action", "[mcl_mobs] A group of mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
|
||||||
end
|
|
||||||
object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min)
|
|
||||||
|
|
||||||
else
|
|
||||||
if logging then
|
|
||||||
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at ".. minetest.pos_to_string(spawning_position, 1))
|
|
||||||
end
|
|
||||||
object = mcl_mobs.spawn(spawning_position, mob_def.name)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
current_summary_chance = current_summary_chance - mob_chance
|
current_summary_chance = current_summary_chance - mob_chance
|
||||||
table_remove(mob_library_worker_table, mob_index)
|
table_remove(mob_library_worker_table, mob_index)
|
||||||
|
@ -709,56 +473,16 @@ if mobs_spawn then
|
||||||
timer = timer + dtime
|
timer = timer + dtime
|
||||||
if timer < 10 then return end
|
if timer < 10 then return end
|
||||||
timer = 0
|
timer = 0
|
||||||
local players = get_connected_players()
|
for _, player in pairs(get_connected_players()) do
|
||||||
local total_mobs = count_mobs_total_cap()
|
|
||||||
if total_mobs > mob_cap.total or total_mobs > #players * mob_cap.player then
|
|
||||||
minetest.log("action","[mcl_mobs] global mob cap reached. no cycle spawning.")
|
|
||||||
return
|
|
||||||
end --mob cap per player
|
|
||||||
for _, player in pairs(players) do
|
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
local dimension = mcl_worlds.pos_to_dimension(pos)
|
||||||
-- ignore void and unloaded area
|
-- ignore void and unloaded area
|
||||||
if dimension ~= "void" and dimension ~= "default" then
|
if dimension ~= "void" and dimension ~= "default" then
|
||||||
local y_min, y_max = decypher_limits(pos.y)
|
local y_min, y_max = decypher_limits(pos.y)
|
||||||
spawn_a_mob(pos, dimension, y_min, y_max)
|
for i = 1, math_random(1, 4) do
|
||||||
|
spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:check_despawn(pos)
|
|
||||||
-- Despawning: when lifetimer expires, remove mob
|
|
||||||
if remove_far
|
|
||||||
and self.can_despawn == true
|
|
||||||
and ((not self.nametag) or (self.nametag == ""))
|
|
||||||
and self.state ~= "attack"
|
|
||||||
and self.following == nil then
|
|
||||||
if self.despawn_immediately or self.lifetimer <= 0 then
|
|
||||||
if logging then
|
|
||||||
minetest.log("action", "[mcl_mobs] Mob "..self.name.." despawns at "..minetest.pos_to_string(pos, 1) .. " lifetimer ran out")
|
|
||||||
end
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return true
|
|
||||||
elseif self.lifetimer <= 10 then
|
|
||||||
if math.random(10) < 4 then
|
|
||||||
self.despawn_immediately = true
|
|
||||||
else
|
|
||||||
self.lifetimer = 20
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_chatcommand("mobstats",{
|
|
||||||
privs = { debug = true },
|
|
||||||
func = function(n,param)
|
|
||||||
minetest.chat_send_player(n,dump(dbg_spawn_counts))
|
|
||||||
local pos = minetest.get_player_by_name(n):get_pos()
|
|
||||||
minetest.chat_send_player(n,"mobs within 32 radius of player:"..count_mobs(pos,32))
|
|
||||||
minetest.chat_send_player(n,"total mobs:"..count_mobs_total())
|
|
||||||
minetest.chat_send_player(n,"spawning attempts since server start:"..dbg_spawn_attempts)
|
|
||||||
minetest.chat_send_player(n,"successful spawns since server start:"..dbg_spawn_succ)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_paintings
|
|
||||||
Painting=絵画
|
|
|
@ -38,12 +38,6 @@ local function wither_spawn(pos)
|
||||||
if check_schem(p, schem) then
|
if check_schem(p, schem) then
|
||||||
remove_schem(p, schem)
|
remove_schem(p, schem)
|
||||||
minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither")
|
minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither")
|
||||||
local objects = minetest.get_objects_inside_radius(pos, 20)
|
|
||||||
for _, players in ipairs(objects) do
|
|
||||||
if players:is_player() then
|
|
||||||
awards.unlock(players:get_player_name(), "mcl:witheringHeights")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -52,9 +46,6 @@ end
|
||||||
local wither_head = minetest.registered_nodes["mcl_heads:wither_skeleton"]
|
local wither_head = minetest.registered_nodes["mcl_heads:wither_skeleton"]
|
||||||
local old_on_place = wither_head.on_place
|
local old_on_place = wither_head.on_place
|
||||||
function wither_head.on_place(itemstack, placer, pointed)
|
function wither_head.on_place(itemstack, placer, pointed)
|
||||||
local n = minetest.get_node(vector.offset(pointed.above,0,-1,0))
|
minetest.after(0, wither_spawn, pointed.above)
|
||||||
if n and n.name == "mcl_nether:soul_sand" then
|
old_on_place(itemstack, placer, pointed)
|
||||||
minetest.after(0, wither_spawn, pointed.above)
|
|
||||||
end
|
|
||||||
return old_on_place(itemstack, placer, pointed)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,6 @@ This mod adds mobs which closely resemble the mobs from the game Minecraft, vers
|
||||||
|
|
||||||
### Peaceful mobs
|
### Peaceful mobs
|
||||||
|
|
||||||
* Axolotl
|
|
||||||
* Chicken
|
* Chicken
|
||||||
* Cow
|
* Cow
|
||||||
* Pig
|
* Pig
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
|
||||||
|
|
||||||
local axolotl = {
|
|
||||||
type = "animal",
|
|
||||||
spawn_class = "water",
|
|
||||||
can_despawn = true,
|
|
||||||
passive = false,
|
|
||||||
hp_min = 14,
|
|
||||||
hp_max = 14,
|
|
||||||
xp_min = 1,
|
|
||||||
xp_max = 7,
|
|
||||||
|
|
||||||
head_swivel = "head.control",
|
|
||||||
bone_eye_height = -1,
|
|
||||||
head_eye_height = -0.5,
|
|
||||||
horrizonatal_head_height = 0,
|
|
||||||
curiosity = 10,
|
|
||||||
head_yaw="z",
|
|
||||||
|
|
||||||
armor = 100,
|
|
||||||
rotate = 180,
|
|
||||||
spawn_in_group_min = 1,
|
|
||||||
spawn_in_group = 4,
|
|
||||||
tilt_swim = true,
|
|
||||||
collisionbox = {-0.5, 0.0, -0.5, 0.5, 0.8, 0.5},
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "mobs_mc_axolotl.b3d",
|
|
||||||
textures = {
|
|
||||||
{"mobs_mc_axolotl_brown.png"},
|
|
||||||
{"mobs_mc_axolotl_yellow.png"},
|
|
||||||
{"mobs_mc_axolotl_green.png"},
|
|
||||||
{"mobs_mc_axolotl_pink.png"},
|
|
||||||
{"mobs_mc_axolotl_black.png"},
|
|
||||||
{"mobs_mc_axolotl_purple.png"},
|
|
||||||
{"mobs_mc_axolotl_white.png"}
|
|
||||||
},
|
|
||||||
sounds = {
|
|
||||||
random = "mobs_mc_axolotl",
|
|
||||||
damage = "mobs_mc_axolotl_hurt",
|
|
||||||
distance = 16,
|
|
||||||
},
|
|
||||||
animation = {-- Stand: 1-20; Walk: 20-60; Swim: 61-81
|
|
||||||
stand_start = 61, stand_end = 81, stand_speed = 15,
|
|
||||||
walk_start = 61, walk_end = 81, walk_speed = 15,
|
|
||||||
run_start = 61, run_end = 81, run_speed = 20,
|
|
||||||
},
|
|
||||||
|
|
||||||
follow = {
|
|
||||||
"mcl_fishing:clownfish_raw"
|
|
||||||
},
|
|
||||||
|
|
||||||
view_range = 16,
|
|
||||||
fear_height = 4,
|
|
||||||
|
|
||||||
on_rightclick = function(self, clicker)
|
|
||||||
local bn = clicker:get_wielded_item():get_name()
|
|
||||||
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
|
||||||
if clicker:set_wielded_item("mcl_buckets:bucket_axolotl") then
|
|
||||||
local it = clicker:get_wielded_item()
|
|
||||||
local m = it:get_meta()
|
|
||||||
m:set_string("properties",minetest.serialize(self.object:get_properties()))
|
|
||||||
clicker:set_wielded_item(it)
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:cutestPredator")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if self:feed_tame(clicker, 1, true, false) then return end
|
|
||||||
end,
|
|
||||||
makes_footstep_sound = false,
|
|
||||||
fly = true,
|
|
||||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
|
||||||
breathes_in_water = true,
|
|
||||||
jump = true,
|
|
||||||
damage = 2,
|
|
||||||
reach = 2,
|
|
||||||
attack_type = "dogfight",
|
|
||||||
attack_animals = true,
|
|
||||||
specific_attack = {
|
|
||||||
"extra_mobs_cod",
|
|
||||||
"mobs_mc:sheep",
|
|
||||||
"extra_mobs_glow_squid",
|
|
||||||
"extra_mobs_salmon",
|
|
||||||
"extra_mobs_tropical_fish",
|
|
||||||
"mobs_mc_squid"
|
|
||||||
},
|
|
||||||
runaway = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:axolotl", axolotl)
|
|
||||||
|
|
||||||
local water = 0
|
|
||||||
|
|
||||||
mcl_mobs:spawn_specific(
|
|
||||||
"mobs_mc:axolotl",
|
|
||||||
"overworld",
|
|
||||||
"water",
|
|
||||||
{
|
|
||||||
"Swampland",
|
|
||||||
"MushroomIsland",
|
|
||||||
"RoofedForest",
|
|
||||||
"FlowerForest_beach",
|
|
||||||
"Forest_beach",
|
|
||||||
"StoneBeach",
|
|
||||||
"Taiga_beach",
|
|
||||||
"Savanna_beach",
|
|
||||||
"Plains_beach",
|
|
||||||
"ExtremeHills_beach",
|
|
||||||
"Swampland_shore",
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"JungleM_shore",
|
|
||||||
"Jungle_shore",
|
|
||||||
"RoofedForest_ocean",
|
|
||||||
"JungleEdgeM_ocean",
|
|
||||||
"BirchForestM_ocean",
|
|
||||||
"BirchForest_ocean",
|
|
||||||
"IcePlains_deep_ocean",
|
|
||||||
"Jungle_deep_ocean",
|
|
||||||
"Savanna_ocean",
|
|
||||||
"MesaPlateauF_ocean",
|
|
||||||
"SunflowerPlains_ocean",
|
|
||||||
"Swampland_ocean",
|
|
||||||
"ExtremeHillsM_ocean",
|
|
||||||
"Mesa_ocean",
|
|
||||||
"StoneBeach_ocean",
|
|
||||||
"Plains_ocean",
|
|
||||||
"MesaPlateauFM_ocean",
|
|
||||||
"MushroomIsland_ocean",
|
|
||||||
"MegaTaiga_ocean",
|
|
||||||
"StoneBeach_deep_ocean",
|
|
||||||
"SavannaM_ocean",
|
|
||||||
"ExtremeHills_ocean",
|
|
||||||
"Forest_ocean",
|
|
||||||
"JungleEdge_ocean",
|
|
||||||
"MesaBryce_ocean",
|
|
||||||
"MegaSpruceTaiga_ocean",
|
|
||||||
"ExtremeHills+_ocean",
|
|
||||||
"Jungle_ocean",
|
|
||||||
"FlowerForest_ocean",
|
|
||||||
"Desert_ocean",
|
|
||||||
"Taiga_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",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
"LushCaves",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
4000,
|
|
||||||
3,
|
|
||||||
water-16,
|
|
||||||
water+1)
|
|
||||||
|
|
||||||
-- spawn eggs
|
|
||||||
mcl_mobs.register_egg("mobs_mc:axolotl", S("Axolotl"), "#e890bf", "#b83D7e", 0)
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:bat", {
|
mcl_mobs:register_mob("mobs_mc:bat", {
|
||||||
description = S("Bat"),
|
description = S("Bat"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "ambient",
|
spawn_class = "ambient",
|
||||||
can_despawn = true,
|
can_despawn = true,
|
||||||
spawn_in_group = 8,
|
|
||||||
passive = true,
|
passive = true,
|
||||||
hp_min = 6,
|
hp_min = 6,
|
||||||
hp_max = 6,
|
hp_max = 6,
|
||||||
|
@ -144,4 +143,4 @@ mobs_mc.water_level-1)
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:bat", S("Bat"), "#4c3e30", "#0f0f0f", 0)
|
mcl_mobs:register_egg("mobs_mc:bat", S("Bat"), "mobs_mc_spawn_icon_bat.png", 0)
|
||||||
|
|
|
@ -12,12 +12,10 @@ local mod_target = minetest.get_modpath("mcl_target")
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:blaze", {
|
mcl_mobs:register_mob("mobs_mc:blaze", {
|
||||||
description = S("Blaze"),
|
description = S("Blaze"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
spawn_in_group_min = 2,
|
|
||||||
spawn_in_group = 3,
|
|
||||||
hp_min = 20,
|
hp_min = 20,
|
||||||
hp_max = 20,
|
hp_max = 20,
|
||||||
xp_min = 10,
|
xp_min = 10,
|
||||||
|
@ -26,12 +24,6 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
||||||
rotate = -180,
|
rotate = -180,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_blaze.b3d",
|
mesh = "mobs_mc_blaze.b3d",
|
||||||
head_swivel = "head.control",
|
|
||||||
bone_eye_height = 4,
|
|
||||||
head_eye_height = 3.5,
|
|
||||||
curiosity = 10,
|
|
||||||
head_yaw_offset = 180,
|
|
||||||
head_pitch_multiplier=-1,
|
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_blaze.png"},
|
{"mobs_mc_blaze.png"},
|
||||||
},
|
},
|
||||||
|
@ -153,7 +145,7 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- Blaze fireball
|
-- Blaze fireball
|
||||||
mcl_mobs.register_arrow("mobs_mc:blaze_fireball", {
|
mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 0.3, y = 0.3},
|
visual_size = {x = 0.3, y = 0.3},
|
||||||
textures = {"mcl_fire_fire_charge.png"},
|
textures = {"mcl_fire_fire_charge.png"},
|
||||||
|
@ -208,4 +200,4 @@ mcl_mobs.register_arrow("mobs_mc:blaze_fireball", {
|
||||||
})
|
})
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:blaze", S("Blaze"), "#f6b201", "#fff87e", 0)
|
mcl_mobs:register_egg("mobs_mc:blaze", S("Blaze"), "mobs_mc_spawn_icon_blaze.png", 0)
|
||||||
|
|
|
@ -8,7 +8,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:chicken", {
|
mcl_mobs:register_mob("mobs_mc:chicken", {
|
||||||
description = S("Chicken"),
|
description = S("Chicken"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -20,18 +20,12 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
||||||
runaway = true,
|
runaway = true,
|
||||||
floats = 1,
|
floats = 1,
|
||||||
head_swivel = "head.control",
|
|
||||||
bone_eye_height = 4,
|
|
||||||
head_eye_height = 1.5,
|
|
||||||
horrizonatal_head_height = -.3,
|
|
||||||
curiosity = 10,
|
|
||||||
head_yaw="z",
|
|
||||||
visual_size = {x=1,y=1},
|
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_chicken.b3d",
|
mesh = "mobs_mc_chicken.b3d",
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_chicken.png"},
|
{"mobs_mc_chicken.png"},
|
||||||
},
|
},
|
||||||
|
visual_size = {x=2.2, y=2.2},
|
||||||
|
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
walk_velocity = 1,
|
walk_velocity = 1,
|
||||||
|
@ -64,15 +58,12 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
},
|
},
|
||||||
animation = {
|
animation = {
|
||||||
stand_start = 0, stand_end = 0,
|
stand_speed = 25, walk_speed = 25, run_speed = 50,
|
||||||
walk_start = 0, walk_end = 20, walk_speed = 25,
|
stand_start = 0, stand_end = 0,
|
||||||
run_start = 0, run_end = 20, run_speed = 50,
|
walk_start = 0, walk_end = 40,
|
||||||
},
|
run_start = 0, run_end = 40,
|
||||||
child_animations = {
|
|
||||||
stand_start = 31, stand_end = 31,
|
|
||||||
walk_start = 31, walk_end = 51, walk_speed = 37,
|
|
||||||
run_start = 31, run_end = 51, run_speed = 75,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
follow = {
|
follow = {
|
||||||
"mcl_farming:wheat_seeds",
|
"mcl_farming:wheat_seeds",
|
||||||
"mcl_farming:melon_seeds",
|
"mcl_farming:melon_seeds",
|
||||||
|
@ -83,7 +74,7 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
|
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if self:feed_tame(clicker, 1, true, false) then return end
|
if mcl_mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
||||||
end,
|
end,
|
||||||
|
@ -131,6 +122,8 @@ mcl_mobs:spawn_specific(
|
||||||
"ExtremeHills_beach",
|
"ExtremeHills_beach",
|
||||||
"ExtremeHillsM",
|
"ExtremeHillsM",
|
||||||
"ExtremeHills+",
|
"ExtremeHills+",
|
||||||
|
"ExtremeHills+_snowtop",
|
||||||
|
"StoneBeach",
|
||||||
"Plains",
|
"Plains",
|
||||||
"Plains_beach",
|
"Plains_beach",
|
||||||
"SunflowerPlains",
|
"SunflowerPlains",
|
||||||
|
@ -163,4 +156,4 @@ mobs_mc.water_level,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:chicken", S("Chicken"), "#a1a1a1", "#ff0000", 0)
|
mcl_mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
--MCmobs v0.4
|
|
||||||
--maikerumine
|
|
||||||
--made for MC like Survival game
|
|
||||||
--License for code WTFPL and otherwise stated in readmes
|
|
||||||
|
|
||||||
local pi = math.pi
|
|
||||||
local atann = math.atan
|
|
||||||
local atan = function(x)
|
|
||||||
if not x or x ~= x then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return atann(x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local dir_to_pitch = function(dir)
|
|
||||||
local dir2 = vector.normalize(dir)
|
|
||||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
|
||||||
return -math.atan2(-dir.y, xz)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function degrees(rad)
|
|
||||||
return rad * 180.0 / math.pi
|
|
||||||
end
|
|
||||||
|
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
|
||||||
|
|
||||||
--###################
|
|
||||||
--################### cod
|
|
||||||
--###################
|
|
||||||
|
|
||||||
local cod = {
|
|
||||||
type = "animal",
|
|
||||||
spawn_class = "water",
|
|
||||||
can_despawn = true,
|
|
||||||
passive = true,
|
|
||||||
hp_min = 3,
|
|
||||||
hp_max = 3,
|
|
||||||
xp_min = 1,
|
|
||||||
xp_max = 3,
|
|
||||||
armor = 100,
|
|
||||||
rotate = 180,
|
|
||||||
spawn_in_group_min = 3,
|
|
||||||
spawn_in_group = 8,
|
|
||||||
tilt_swim = true,
|
|
||||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "extra_mobs_cod.b3d",
|
|
||||||
textures = {
|
|
||||||
{"extra_mobs_cod.png"}
|
|
||||||
},
|
|
||||||
sounds = {
|
|
||||||
},
|
|
||||||
animation = {
|
|
||||||
stand_start = 1,
|
|
||||||
stand_end = 20,
|
|
||||||
walk_start = 1,
|
|
||||||
walk_end = 20,
|
|
||||||
run_start = 1,
|
|
||||||
run_end = 20,
|
|
||||||
},
|
|
||||||
drops = {
|
|
||||||
{name = "mcl_fishing:fish_raw",
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 1,},
|
|
||||||
{name = "mcl_dye:white",
|
|
||||||
chance = 20,
|
|
||||||
min = 1,
|
|
||||||
max = 1,},
|
|
||||||
},
|
|
||||||
visual_size = {x=3, y=3},
|
|
||||||
makes_footstep_sound = false,
|
|
||||||
fly = true,
|
|
||||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
|
||||||
breathes_in_water = true,
|
|
||||||
jump = false,
|
|
||||||
view_range = 16,
|
|
||||||
runaway = true,
|
|
||||||
fear_height = 4,
|
|
||||||
do_custom = function(self)
|
|
||||||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
|
||||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
|
||||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
|
||||||
if self.object:get_velocity().y < 5 then
|
|
||||||
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 10)) do
|
|
||||||
local lp = object:get_pos()
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local vec = {
|
|
||||||
x = lp.x - s.x,
|
|
||||||
y = lp.y - s.y,
|
|
||||||
z = lp.z - s.z
|
|
||||||
}
|
|
||||||
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mobs_mc:cod" then
|
|
||||||
self.state = "runaway"
|
|
||||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_rightclick = function(self, clicker)
|
|
||||||
local bn = clicker:get_wielded_item():get_name()
|
|
||||||
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
|
||||||
self.object:remove()
|
|
||||||
clicker:set_wielded_item("mcl_buckets:bucket_cod")
|
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:cod", cod)
|
|
||||||
|
|
||||||
|
|
||||||
--spawning TODO: in schools
|
|
||||||
|
|
||||||
local water = 0
|
|
||||||
|
|
||||||
mcl_mobs:spawn_specific(
|
|
||||||
"mobs_mc:cod",
|
|
||||||
"overworld",
|
|
||||||
"water",
|
|
||||||
{
|
|
||||||
"Mesa",
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"Jungle",
|
|
||||||
"Savanna",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
|
||||||
"MushroomIsland",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"IcePlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"JungleM",
|
|
||||||
"BirchForestM",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
"JungleEdge",
|
|
||||||
"SavannaM",
|
|
||||||
"FlowerForest_beach",
|
|
||||||
"Forest_beach",
|
|
||||||
"StoneBeach",
|
|
||||||
"ColdTaiga_beach_water",
|
|
||||||
"Taiga_beach",
|
|
||||||
"Savanna_beach",
|
|
||||||
"Plains_beach",
|
|
||||||
"ExtremeHills_beach",
|
|
||||||
"ColdTaiga_beach",
|
|
||||||
"Swampland_shore",
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"JungleM_shore",
|
|
||||||
"Jungle_shore",
|
|
||||||
"MesaPlateauFM_sandlevel",
|
|
||||||
"MesaPlateauF_sandlevel",
|
|
||||||
"MesaBryce_sandlevel",
|
|
||||||
"Mesa_sandlevel",
|
|
||||||
"RoofedForest_ocean",
|
|
||||||
"JungleEdgeM_ocean",
|
|
||||||
"BirchForestM_ocean",
|
|
||||||
"BirchForest_ocean",
|
|
||||||
"IcePlains_deep_ocean",
|
|
||||||
"Jungle_deep_ocean",
|
|
||||||
"Savanna_ocean",
|
|
||||||
"MesaPlateauF_ocean",
|
|
||||||
"ExtremeHillsM_deep_ocean",
|
|
||||||
"Savanna_deep_ocean",
|
|
||||||
"SunflowerPlains_ocean",
|
|
||||||
"Swampland_deep_ocean",
|
|
||||||
"Swampland_ocean",
|
|
||||||
"MegaSpruceTaiga_deep_ocean",
|
|
||||||
"ExtremeHillsM_ocean",
|
|
||||||
"JungleEdgeM_deep_ocean",
|
|
||||||
"SunflowerPlains_deep_ocean",
|
|
||||||
"BirchForest_deep_ocean",
|
|
||||||
"IcePlainsSpikes_ocean",
|
|
||||||
"Mesa_ocean",
|
|
||||||
"StoneBeach_ocean",
|
|
||||||
"Plains_deep_ocean",
|
|
||||||
"JungleEdge_deep_ocean",
|
|
||||||
"SavannaM_deep_ocean",
|
|
||||||
"Desert_deep_ocean",
|
|
||||||
"Mesa_deep_ocean",
|
|
||||||
"ColdTaiga_deep_ocean",
|
|
||||||
"Plains_ocean",
|
|
||||||
"MesaPlateauFM_ocean",
|
|
||||||
"Forest_deep_ocean",
|
|
||||||
"JungleM_deep_ocean",
|
|
||||||
"FlowerForest_deep_ocean",
|
|
||||||
"MushroomIsland_ocean",
|
|
||||||
"MegaTaiga_ocean",
|
|
||||||
"StoneBeach_deep_ocean",
|
|
||||||
"IcePlainsSpikes_deep_ocean",
|
|
||||||
"ColdTaiga_ocean",
|
|
||||||
"SavannaM_ocean",
|
|
||||||
"MesaPlateauF_deep_ocean",
|
|
||||||
"MesaBryce_deep_ocean",
|
|
||||||
"ExtremeHills+_deep_ocean",
|
|
||||||
"ExtremeHills_ocean",
|
|
||||||
"MushroomIsland_deep_ocean",
|
|
||||||
"Forest_ocean",
|
|
||||||
"MegaTaiga_deep_ocean",
|
|
||||||
"JungleEdge_ocean",
|
|
||||||
"MesaBryce_ocean",
|
|
||||||
"MegaSpruceTaiga_ocean",
|
|
||||||
"ExtremeHills+_ocean",
|
|
||||||
"Jungle_ocean",
|
|
||||||
"RoofedForest_deep_ocean",
|
|
||||||
"IcePlains_ocean",
|
|
||||||
"FlowerForest_ocean",
|
|
||||||
"ExtremeHills_deep_ocean",
|
|
||||||
"MesaPlateauFM_deep_ocean",
|
|
||||||
"Desert_ocean",
|
|
||||||
"Taiga_ocean",
|
|
||||||
"BirchForestM_deep_ocean",
|
|
||||||
"Taiga_deep_ocean",
|
|
||||||
"JungleM_ocean",
|
|
||||||
"FlowerForest_underground",
|
|
||||||
"JungleEdge_underground",
|
|
||||||
"StoneBeach_underground",
|
|
||||||
"MesaBryce_underground",
|
|
||||||
"Mesa_underground",
|
|
||||||
"RoofedForest_underground",
|
|
||||||
"Jungle_underground",
|
|
||||||
"Swampland_underground",
|
|
||||||
"MushroomIsland_underground",
|
|
||||||
"BirchForest_underground",
|
|
||||||
"Plains_underground",
|
|
||||||
"MesaPlateauF_underground",
|
|
||||||
"ExtremeHills_underground",
|
|
||||||
"MegaSpruceTaiga_underground",
|
|
||||||
"BirchForestM_underground",
|
|
||||||
"SavannaM_underground",
|
|
||||||
"MesaPlateauFM_underground",
|
|
||||||
"Desert_underground",
|
|
||||||
"Savanna_underground",
|
|
||||||
"Forest_underground",
|
|
||||||
"SunflowerPlains_underground",
|
|
||||||
"ColdTaiga_underground",
|
|
||||||
"IcePlains_underground",
|
|
||||||
"IcePlainsSpikes_underground",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
4000,
|
|
||||||
3,
|
|
||||||
water-16,
|
|
||||||
water+1)
|
|
||||||
|
|
||||||
--spawn egg
|
|
||||||
mcl_mobs.register_egg("mobs_mc:cod", S("Cod"), "#c1a76a", "#e5c48b", 0)
|
|
|
@ -6,26 +6,18 @@ local cow_def = {
|
||||||
description = S("Cow"),
|
description = S("Cow"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
passive = true,
|
|
||||||
hp_min = 10,
|
hp_min = 10,
|
||||||
hp_max = 10,
|
hp_max = 10,
|
||||||
xp_min = 1,
|
xp_min = 1,
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.39, 0.45},
|
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.39, 0.45},
|
||||||
spawn_in_group = 8,
|
|
||||||
spawn_in_group_min = 3,
|
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_cow.b3d",
|
mesh = "mobs_mc_cow.b3d",
|
||||||
textures = { {
|
textures = { {
|
||||||
"mobs_mc_cow.png",
|
"mobs_mc_cow.png",
|
||||||
"blank.png",
|
"blank.png",
|
||||||
}, },
|
}, },
|
||||||
head_swivel = "head.control",
|
visual_size = {x=2.8, y=2.8},
|
||||||
bone_eye_height = 10,
|
|
||||||
head_eye_height = 1.1,
|
|
||||||
horrizonatal_head_height=-1.8,
|
|
||||||
curiosity = 2,
|
|
||||||
head_yaw="z",
|
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
walk_velocity = 1,
|
walk_velocity = 1,
|
||||||
drops = {
|
drops = {
|
||||||
|
@ -49,17 +41,14 @@ local cow_def = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
},
|
},
|
||||||
animation = {
|
animation = {
|
||||||
stand_start = 0, stand_end = 0,
|
stand_speed = 25, walk_speed = 40,
|
||||||
walk_start = 0, walk_end = 40, walk_speed = 30,
|
run_speed = 60, stand_start = 0,
|
||||||
run_start = 0, run_end = 40, run_speed = 40,
|
stand_end = 0, walk_start = 0,
|
||||||
},
|
walk_end = 40, run_start = 0,
|
||||||
child_animations = {
|
run_end = 40,
|
||||||
stand_start = 41, stand_end = 41,
|
|
||||||
walk_start = 41, walk_end = 81, walk_speed = 45,
|
|
||||||
run_start = 41, run_end = 81, run_speed = 60,
|
|
||||||
},
|
},
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if self:feed_tame(clicker, 1, true, false) then return end
|
if mcl_mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
if self.child then
|
if self.child then
|
||||||
|
@ -88,16 +77,15 @@ local cow_def = {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:cow", cow_def)
|
mcl_mobs:register_mob("mobs_mc:cow", cow_def)
|
||||||
|
|
||||||
-- Mooshroom
|
-- Mooshroom
|
||||||
local mooshroom_def = table.copy(cow_def)
|
local mooshroom_def = table.copy(cow_def)
|
||||||
mooshroom_def.description = S("Mooshroom")
|
mooshroom_def.description = S("Mooshroom")
|
||||||
mooshroom_def.spawn_in_group_min = 4
|
mooshroom_def.mesh = "mobs_mc_cow.b3d"
|
||||||
mooshroom_def.spawn_in_group = 8
|
|
||||||
mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } }
|
mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } }
|
||||||
mooshroom_def.on_rightclick = function(self, clicker)
|
mooshroom_def.on_rightclick = function(self, clicker)
|
||||||
if self:feed_tame(clicker, 1, true, false) then return end
|
if mcl_mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
if self.child then
|
if self.child then
|
||||||
|
@ -153,17 +141,7 @@ mooshroom_def.on_rightclick = function(self, clicker)
|
||||||
end
|
end
|
||||||
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||||
end
|
end
|
||||||
|
mcl_mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
||||||
mooshroom_def.on_lightning_strike = function(self, pos, pos2, objects)
|
|
||||||
if self.base_texture[1] == "mobs_mc_mooshroom_brown.png" then
|
|
||||||
self.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
|
|
||||||
else
|
|
||||||
self.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
|
|
||||||
end
|
|
||||||
self.object:set_properties({ textures = self.base_texture })
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
mcl_mobs.register_mob("mobs_mc:mooshroom", mooshroom_def)
|
|
||||||
|
|
||||||
|
|
||||||
-- Spawning
|
-- Spawning
|
||||||
|
@ -173,12 +151,17 @@ mcl_mobs:spawn_specific(
|
||||||
"ground",
|
"ground",
|
||||||
{
|
{
|
||||||
"flat",
|
"flat",
|
||||||
|
"IcePlainsSpikes",
|
||||||
|
"ColdTaiga",
|
||||||
|
"ColdTaiga_beach",
|
||||||
|
"ColdTaiga_beach_water",
|
||||||
"MegaTaiga",
|
"MegaTaiga",
|
||||||
"MegaSpruceTaiga",
|
"MegaSpruceTaiga",
|
||||||
"ExtremeHills",
|
"ExtremeHills",
|
||||||
"ExtremeHills_beach",
|
"ExtremeHills_beach",
|
||||||
"ExtremeHillsM",
|
"ExtremeHillsM",
|
||||||
"ExtremeHills+",
|
"ExtremeHills+",
|
||||||
|
"ExtremeHills+_snowtop",
|
||||||
"StoneBeach",
|
"StoneBeach",
|
||||||
"Plains",
|
"Plains",
|
||||||
"Plains_beach",
|
"Plains_beach",
|
||||||
|
@ -231,5 +214,5 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mcl_mobs.register_egg("mobs_mc:cow", S("Cow"), "#443626", "#a1a1a1", 0)
|
mcl_mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)
|
||||||
mcl_mobs.register_egg("mobs_mc:mooshroom", S("Mooshroom"), "#a00f10", "#b7b7b7", 0)
|
mcl_mobs:register_egg("mobs_mc:mooshroom", S("Mooshroom"), "mobs_mc_spawn_icon_mooshroom.png", 0)
|
||||||
|
|
|
@ -9,10 +9,9 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:creeper", {
|
mcl_mobs:register_mob("mobs_mc:creeper", {
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
spawn_in_group = 1,
|
|
||||||
hp_min = 20,
|
hp_min = 20,
|
||||||
hp_max = 20,
|
hp_max = 20,
|
||||||
xp_min = 5,
|
xp_min = 5,
|
||||||
|
@ -21,9 +20,6 @@ mcl_mobs.register_mob("mobs_mc:creeper", {
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_creeper.b3d",
|
mesh = "mobs_mc_creeper.b3d",
|
||||||
head_swivel = "Head_Control",
|
|
||||||
bone_eye_height = 2.35,
|
|
||||||
curiosity = 2,
|
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_creeper.png",
|
{"mobs_mc_creeper.png",
|
||||||
"mobs_mc_empty.png"},
|
"mobs_mc_empty.png"},
|
||||||
|
@ -81,7 +77,7 @@ mcl_mobs.register_mob("mobs_mc:creeper", {
|
||||||
if self._forced_explosion_countdown_timer ~= nil then
|
if self._forced_explosion_countdown_timer ~= nil then
|
||||||
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
|
||||||
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -133,7 +129,7 @@ mcl_mobs.register_mob("mobs_mc:creeper", {
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:creeper_charged", {
|
mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
description = S("Creeper"),
|
description = S("Creeper"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -203,7 +199,7 @@ mcl_mobs.register_mob("mobs_mc:creeper_charged", {
|
||||||
if self._forced_explosion_countdown_timer ~= nil then
|
if self._forced_explosion_countdown_timer ~= nil then
|
||||||
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
|
||||||
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -219,10 +215,6 @@ mcl_mobs.register_mob("mobs_mc:creeper_charged", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_lightning_strike = function(self, pos, pos2, objects)
|
|
||||||
mcl_util.replace_mob(self.object, "mobs_mc:creeper_charged")
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
maxdrops = 2,
|
maxdrops = 2,
|
||||||
drops = {
|
drops = {
|
||||||
{name = "mcl_mobitems:gunpowder",
|
{name = "mcl_mobitems:gunpowder",
|
||||||
|
@ -282,6 +274,7 @@ mcl_mobs:spawn_specific(
|
||||||
"Plains",
|
"Plains",
|
||||||
"Desert",
|
"Desert",
|
||||||
"ColdTaiga",
|
"ColdTaiga",
|
||||||
|
"MushroomIsland",
|
||||||
"IcePlainsSpikes",
|
"IcePlainsSpikes",
|
||||||
"SunflowerPlains",
|
"SunflowerPlains",
|
||||||
"IcePlains",
|
"IcePlains",
|
||||||
|
@ -308,6 +301,7 @@ mcl_mobs:spawn_specific(
|
||||||
"ExtremeHills_beach",
|
"ExtremeHills_beach",
|
||||||
"ColdTaiga_beach",
|
"ColdTaiga_beach",
|
||||||
"Swampland_shore",
|
"Swampland_shore",
|
||||||
|
"MushroomIslandShore",
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
"MesaPlateauFM_sandlevel",
|
"MesaPlateauFM_sandlevel",
|
||||||
|
@ -346,6 +340,7 @@ mcl_mobs:spawn_specific(
|
||||||
"Forest_deep_ocean",
|
"Forest_deep_ocean",
|
||||||
"JungleM_deep_ocean",
|
"JungleM_deep_ocean",
|
||||||
"FlowerForest_deep_ocean",
|
"FlowerForest_deep_ocean",
|
||||||
|
"MushroomIsland_ocean",
|
||||||
"MegaTaiga_ocean",
|
"MegaTaiga_ocean",
|
||||||
"StoneBeach_deep_ocean",
|
"StoneBeach_deep_ocean",
|
||||||
"IcePlainsSpikes_deep_ocean",
|
"IcePlainsSpikes_deep_ocean",
|
||||||
|
@ -355,6 +350,7 @@ mcl_mobs:spawn_specific(
|
||||||
"MesaBryce_deep_ocean",
|
"MesaBryce_deep_ocean",
|
||||||
"ExtremeHills+_deep_ocean",
|
"ExtremeHills+_deep_ocean",
|
||||||
"ExtremeHills_ocean",
|
"ExtremeHills_ocean",
|
||||||
|
"MushroomIsland_deep_ocean",
|
||||||
"Forest_ocean",
|
"Forest_ocean",
|
||||||
"MegaTaiga_deep_ocean",
|
"MegaTaiga_deep_ocean",
|
||||||
"JungleEdge_ocean",
|
"JungleEdge_ocean",
|
||||||
|
@ -380,6 +376,7 @@ mcl_mobs:spawn_specific(
|
||||||
"RoofedForest_underground",
|
"RoofedForest_underground",
|
||||||
"Jungle_underground",
|
"Jungle_underground",
|
||||||
"Swampland_underground",
|
"Swampland_underground",
|
||||||
|
"MushroomIsland_underground",
|
||||||
"BirchForest_underground",
|
"BirchForest_underground",
|
||||||
"Plains_underground",
|
"Plains_underground",
|
||||||
"MesaPlateauF_underground",
|
"MesaPlateauF_underground",
|
||||||
|
@ -411,4 +408,4 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:creeper", S("Creeper"), "#0da70a", "#000000", 0)
|
mcl_mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
mcl_mobs
|
|
@ -1,253 +0,0 @@
|
||||||
--MCmobs v0.4
|
|
||||||
--maikerumine
|
|
||||||
--made for MC like Survival game
|
|
||||||
--License for code WTFPL and otherwise stated in readmes
|
|
||||||
|
|
||||||
local pi = math.pi
|
|
||||||
local atann = math.atan
|
|
||||||
local atan = function(x)
|
|
||||||
if not x or x ~= x then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return atann(x)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local dir_to_pitch = function(dir)
|
|
||||||
local dir2 = vector.normalize(dir)
|
|
||||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
|
||||||
return -math.atan2(-dir.y, xz)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function degrees(rad)
|
|
||||||
return rad * 180.0 / math.pi
|
|
||||||
end
|
|
||||||
|
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
|
||||||
|
|
||||||
--###################
|
|
||||||
--################### dolphin
|
|
||||||
--###################
|
|
||||||
|
|
||||||
local dolphin = {
|
|
||||||
type = "animal",
|
|
||||||
spawn_class = "water",
|
|
||||||
can_despawn = true,
|
|
||||||
passive = true,
|
|
||||||
hp_min = 10,
|
|
||||||
hp_max = 10,
|
|
||||||
xp_min = 1,
|
|
||||||
xp_max = 3,
|
|
||||||
armor = 100,
|
|
||||||
walk_chance = 100,
|
|
||||||
breath_max = 120,
|
|
||||||
rotate = 180,
|
|
||||||
spawn_in_group_min = 3,
|
|
||||||
spawn_in_group = 5,
|
|
||||||
tilt_swim = true,
|
|
||||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "extra_mobs_dolphin.b3d",
|
|
||||||
textures = {
|
|
||||||
{"extra_mobs_dolphin.png"}
|
|
||||||
},
|
|
||||||
sounds = {
|
|
||||||
},
|
|
||||||
animation = {
|
|
||||||
stand_start = 20,
|
|
||||||
stand_end = 20,
|
|
||||||
walk_start = 0,
|
|
||||||
walk_end = 15,
|
|
||||||
run_start = 30,
|
|
||||||
run_end = 45,
|
|
||||||
},
|
|
||||||
drops = {
|
|
||||||
{name = "mcl_fishing:fish_raw",
|
|
||||||
chance = 1,
|
|
||||||
min = 0,
|
|
||||||
max = 1,},
|
|
||||||
},
|
|
||||||
visual_size = {x=3, y=3},
|
|
||||||
makes_footstep_sound = false,
|
|
||||||
fly = true,
|
|
||||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
|
||||||
breathes_in_water = true,
|
|
||||||
jump = false,
|
|
||||||
view_range = 16,
|
|
||||||
fear_height = 4,
|
|
||||||
walk_velocity = 3,
|
|
||||||
run_velocity = 6,
|
|
||||||
reach = 2,
|
|
||||||
damage = 2.5,
|
|
||||||
attack_type = "dogfight",
|
|
||||||
do_custom = function(self,dtime)
|
|
||||||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
|
||||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
|
||||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
|
||||||
if self.object:get_velocity().y < 5 then
|
|
||||||
self.object:add_velocity({ x = 0 , y = math.random(-.007, .007), z = 0 })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:dolphin", dolphin)
|
|
||||||
|
|
||||||
|
|
||||||
--spawning TO DO: in schools
|
|
||||||
local water = 0
|
|
||||||
mcl_mobs:spawn_specific(
|
|
||||||
"mobs_mc:dolphin",
|
|
||||||
"overworld",
|
|
||||||
"water",
|
|
||||||
{
|
|
||||||
"Mesa",
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"Jungle",
|
|
||||||
"Savanna",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
|
||||||
"MushroomIsland",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"IcePlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"JungleM",
|
|
||||||
"BirchForestM",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
"JungleEdge",
|
|
||||||
"SavannaM",
|
|
||||||
"FlowerForest_beach",
|
|
||||||
"Forest_beach",
|
|
||||||
"StoneBeach",
|
|
||||||
"Taiga_beach",
|
|
||||||
"Savanna_beach",
|
|
||||||
"Plains_beach",
|
|
||||||
"ExtremeHills_beach",
|
|
||||||
"ColdTaiga_beach",
|
|
||||||
"Swampland_shore",
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"JungleM_shore",
|
|
||||||
"Jungle_shore",
|
|
||||||
"MesaPlateauFM_sandlevel",
|
|
||||||
"MesaPlateauF_sandlevel",
|
|
||||||
"MesaBryce_sandlevel",
|
|
||||||
"Mesa_sandlevel",
|
|
||||||
"RoofedForest_ocean",
|
|
||||||
"JungleEdgeM_ocean",
|
|
||||||
"BirchForestM_ocean",
|
|
||||||
"BirchForest_ocean",
|
|
||||||
"IcePlains_deep_ocean",
|
|
||||||
"Jungle_deep_ocean",
|
|
||||||
"Savanna_ocean",
|
|
||||||
"MesaPlateauF_ocean",
|
|
||||||
"ExtremeHillsM_deep_ocean",
|
|
||||||
"Savanna_deep_ocean",
|
|
||||||
"SunflowerPlains_ocean",
|
|
||||||
"Swampland_deep_ocean",
|
|
||||||
"Swampland_ocean",
|
|
||||||
"MegaSpruceTaiga_deep_ocean",
|
|
||||||
"ExtremeHillsM_ocean",
|
|
||||||
"JungleEdgeM_deep_ocean",
|
|
||||||
"SunflowerPlains_deep_ocean",
|
|
||||||
"BirchForest_deep_ocean",
|
|
||||||
"IcePlainsSpikes_ocean",
|
|
||||||
"Mesa_ocean",
|
|
||||||
"StoneBeach_ocean",
|
|
||||||
"Plains_deep_ocean",
|
|
||||||
"JungleEdge_deep_ocean",
|
|
||||||
"SavannaM_deep_ocean",
|
|
||||||
"Desert_deep_ocean",
|
|
||||||
"Mesa_deep_ocean",
|
|
||||||
"ColdTaiga_deep_ocean",
|
|
||||||
"Plains_ocean",
|
|
||||||
"MesaPlateauFM_ocean",
|
|
||||||
"Forest_deep_ocean",
|
|
||||||
"JungleM_deep_ocean",
|
|
||||||
"FlowerForest_deep_ocean",
|
|
||||||
"MushroomIsland_ocean",
|
|
||||||
"MegaTaiga_ocean",
|
|
||||||
"StoneBeach_deep_ocean",
|
|
||||||
"IcePlainsSpikes_deep_ocean",
|
|
||||||
"ColdTaiga_ocean",
|
|
||||||
"SavannaM_ocean",
|
|
||||||
"MesaPlateauF_deep_ocean",
|
|
||||||
"MesaBryce_deep_ocean",
|
|
||||||
"ExtremeHills+_deep_ocean",
|
|
||||||
"ExtremeHills_ocean",
|
|
||||||
"MushroomIsland_deep_ocean",
|
|
||||||
"Forest_ocean",
|
|
||||||
"MegaTaiga_deep_ocean",
|
|
||||||
"JungleEdge_ocean",
|
|
||||||
"MesaBryce_ocean",
|
|
||||||
"MegaSpruceTaiga_ocean",
|
|
||||||
"ExtremeHills+_ocean",
|
|
||||||
"Jungle_ocean",
|
|
||||||
"RoofedForest_deep_ocean",
|
|
||||||
"IcePlains_ocean",
|
|
||||||
"FlowerForest_ocean",
|
|
||||||
"ExtremeHills_deep_ocean",
|
|
||||||
"MesaPlateauFM_deep_ocean",
|
|
||||||
"Desert_ocean",
|
|
||||||
"Taiga_ocean",
|
|
||||||
"BirchForestM_deep_ocean",
|
|
||||||
"Taiga_deep_ocean",
|
|
||||||
"JungleM_ocean",
|
|
||||||
"FlowerForest_underground",
|
|
||||||
"JungleEdge_underground",
|
|
||||||
"StoneBeach_underground",
|
|
||||||
"MesaBryce_underground",
|
|
||||||
"Mesa_underground",
|
|
||||||
"RoofedForest_underground",
|
|
||||||
"Jungle_underground",
|
|
||||||
"Swampland_underground",
|
|
||||||
"MushroomIsland_underground",
|
|
||||||
"BirchForest_underground",
|
|
||||||
"Plains_underground",
|
|
||||||
"MesaPlateauF_underground",
|
|
||||||
"ExtremeHills_underground",
|
|
||||||
"MegaSpruceTaiga_underground",
|
|
||||||
"BirchForestM_underground",
|
|
||||||
"SavannaM_underground",
|
|
||||||
"MesaPlateauFM_underground",
|
|
||||||
"Desert_underground",
|
|
||||||
"Savanna_underground",
|
|
||||||
"Forest_underground",
|
|
||||||
"SunflowerPlains_underground",
|
|
||||||
"ColdTaiga_underground",
|
|
||||||
"IcePlains_underground",
|
|
||||||
"IcePlainsSpikes_underground",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
4000,
|
|
||||||
3,
|
|
||||||
water-16,
|
|
||||||
water+1)
|
|
||||||
|
|
||||||
--spawn egg
|
|
||||||
mcl_mobs.register_egg("mobs_mc:dolphin", S("Dolphin"), "#223b4d", "#f9f9f9", 0)
|
|
|
@ -4,51 +4,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
local BEAM_CHECK_FREQUENCY = 2
|
mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
||||||
local POS_CHECK_FREQUENCY = 15
|
|
||||||
local HEAL_AMMOUNT = 37
|
|
||||||
|
|
||||||
local function heal(self)
|
|
||||||
local o = self.object
|
|
||||||
self.health = math.min(self.hp_max,self.health + HEAL_AMMOUNT)
|
|
||||||
end
|
|
||||||
local function check_beam(self)
|
|
||||||
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if luaentity and luaentity.name == "mcl_end:crystal" then
|
|
||||||
if luaentity.beam then
|
|
||||||
if luaentity.beam == self.beam then
|
|
||||||
heal(self)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if self.beam then
|
|
||||||
self.beam:remove()
|
|
||||||
end
|
|
||||||
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_pos(self)
|
|
||||||
if self._portal_pos then
|
|
||||||
-- migrate old format
|
|
||||||
if type(self._portal_pos) == "string" then
|
|
||||||
self._portal_pos = minetest.string_to_pos(self._portal_pos)
|
|
||||||
end
|
|
||||||
local portal_center = vector.add(self._portal_pos, vector.new(0, 11, 0))
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
if vector.distance(pos, portal_center) > 50 then
|
|
||||||
self.object:set_pos(self._last_good_pos or portal_center)
|
|
||||||
else
|
|
||||||
self._last_good_pos = pos
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
|
||||||
description = S("Ender Dragon"),
|
description = S("Ender Dragon"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -67,7 +23,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
||||||
{"mobs_mc_dragon.png"},
|
{"mobs_mc_dragon.png"},
|
||||||
},
|
},
|
||||||
visual_size = {x=3, y=3},
|
visual_size = {x=3, y=3},
|
||||||
view_range = 64,
|
view_range = 35,
|
||||||
walk_velocity = 6,
|
walk_velocity = 6,
|
||||||
run_velocity = 6,
|
run_velocity = 6,
|
||||||
can_despawn = false,
|
can_despawn = false,
|
||||||
|
@ -105,33 +61,45 @@ mcl_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,dtime)
|
do_custom = function(self)
|
||||||
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
|
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
|
||||||
if self._pos_timer == nil or self._pos_timer > POS_CHECK_FREQUENCY then
|
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
||||||
self._pos_timer = 0
|
local luaentity = obj:get_luaentity()
|
||||||
check_pos(self)
|
if luaentity and luaentity.name == "mcl_end:crystal" then
|
||||||
end
|
if luaentity.beam then
|
||||||
if self._beam_timer == nil or self._beam_timer > BEAM_CHECK_FREQUENCY then
|
if luaentity.beam == self.beam then
|
||||||
self._beam_timer = 0
|
break
|
||||||
check_beam(self)
|
end
|
||||||
end
|
else
|
||||||
self._beam_timer = self._beam_timer + dtime
|
if self.beam then
|
||||||
self._pos_timer = self._pos_timer + dtime
|
self.beam:remove()
|
||||||
end,
|
end
|
||||||
on_die = function(self, pos, cmi_cause)
|
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
|
||||||
if self._portal_pos then
|
break
|
||||||
mcl_portals.spawn_gateway_portal()
|
end
|
||||||
mcl_structures.place_structure(self._portal_pos,mcl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
|
|
||||||
if self._initial then
|
|
||||||
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
|
|
||||||
minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if self._portal_pos then
|
||||||
-- Free The End Advancement
|
-- migrate old format
|
||||||
for _,players in pairs(minetest.get_objects_inside_radius(pos,64)) do
|
if type(self._portal_pos) == "string" then
|
||||||
if players:is_player() then
|
self._portal_pos = minetest.string_to_pos(self._portal_pos)
|
||||||
awards.unlock(players:get_player_name(), "mcl:freeTheEnd")
|
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_xp(pos, 11500) -- 500 + 11500 = 12000
|
||||||
|
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = "mcl_end:dragon_egg"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -142,7 +110,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
|
|
||||||
-- dragon fireball (projectile)
|
-- dragon fireball (projectile)
|
||||||
mcl_mobs.register_arrow("mobs_mc:dragon_fireball", {
|
mcl_mobs:register_arrow("mobs_mc:dragon_fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 1.25, y = 1.25},
|
visual_size = {x = 1.25, y = 1.25},
|
||||||
textures = {"mobs_mc_dragon_fireball.png"},
|
textures = {"mobs_mc_dragon_fireball.png"},
|
||||||
|
@ -166,11 +134,10 @@ mcl_mobs.register_arrow("mobs_mc:dragon_fireball", {
|
||||||
|
|
||||||
-- node hit, explode
|
-- node hit, explode
|
||||||
hit_node = function(self, pos, node)
|
hit_node = function(self, pos, node)
|
||||||
mcl_mobs.mob_class.boom(self,pos, 2)
|
mcl_mobs:boom(self, pos, 2)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs.register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "#252525", "#b313c9", 0, true)
|
mcl_mobs:register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "mobs_mc_spawn_icon_dragon.png", 0, true)
|
||||||
|
|
||||||
|
|
||||||
mcl_wip.register_wip_item("mobs_mc:enderdragon")
|
mcl_wip.register_wip_item("mobs_mc:enderdragon")
|
||||||
|
|
|
@ -24,25 +24,7 @@
|
||||||
-- added rain damage.
|
-- added rain damage.
|
||||||
-- fixed the grass_with_dirt issue.
|
-- fixed the grass_with_dirt issue.
|
||||||
|
|
||||||
minetest.register_entity("mobs_mc:ender_eyes", {
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "mobs_mc_spider.b3d",
|
|
||||||
visual_size = {x=1.01/3, y=1.01/3},
|
|
||||||
textures = {
|
|
||||||
"mobs_mc_enderman_eyes.png",
|
|
||||||
},
|
|
||||||
on_step = function(self)
|
|
||||||
if self and self.object then
|
|
||||||
if not self.object:get_attach() then
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
glow = 50,
|
|
||||||
})
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
local enable_damage = minetest.settings:get_bool("enable_damage")
|
|
||||||
|
|
||||||
local telesound = function(pos, is_source)
|
local telesound = function(pos, is_source)
|
||||||
local snd
|
local snd
|
||||||
|
@ -237,25 +219,8 @@ local select_enderman_animation = function(animation_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
local psdefs = {{
|
|
||||||
amount = 5,
|
|
||||||
minpos = vector.new(-0.6,0,-0.6),
|
|
||||||
maxpos = vector.new(0.6,3,0.6),
|
|
||||||
minvel = vector.new(-0.25,-0.25,-0.25),
|
|
||||||
maxvel = vector.new(0.25,0.25,0.25),
|
|
||||||
minacc = vector.new(-0.5,-0.5,-0.5),
|
|
||||||
maxacc = vector.new(0.5,0.5,0.5),
|
|
||||||
minexptime = 0.2,
|
|
||||||
maxexptime = 3,
|
|
||||||
minsize = 0.2,
|
|
||||||
maxsize = 1.2,
|
|
||||||
collisiondetection = true,
|
|
||||||
vertical = false,
|
|
||||||
time = 0,
|
|
||||||
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
|
|
||||||
}}
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:enderman", {
|
mcl_mobs:register_mob("mobs_mc:enderman", {
|
||||||
description = S("Enderman"),
|
description = S("Enderman"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -271,19 +236,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
textures = create_enderman_textures(),
|
textures = create_enderman_textures(),
|
||||||
visual_size = {x=3, y=3},
|
visual_size = {x=3, y=3},
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
on_spawn = function(self)
|
|
||||||
local spider_eyes=false
|
|
||||||
for n = 1, #self.object:get_children() do
|
|
||||||
local obj = self.object:get_children()[n]
|
|
||||||
if obj:get_luaentity() and self.object:get_luaentity().name == "mobs_mc:ender_eyes" then
|
|
||||||
spider_eyes = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not spider_eyes then
|
|
||||||
minetest.add_entity(self.object:get_pos(), "mobs_mc:ender_eyes"):set_attach(self.object, "head.top", vector.new(0,2.54,-1.99), vector.new(90,0,180))
|
|
||||||
minetest.add_entity(self.object:get_pos(), "mobs_mc:ender_eyes"):set_attach(self.object, "head.top", vector.new(1,2.54,-1.99), vector.new(90,0,180))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
sounds = {
|
sounds = {
|
||||||
-- TODO: Custom war cry sound
|
-- TODO: Custom war cry sound
|
||||||
war_cry = "mobs_sandmonster",
|
war_cry = "mobs_sandmonster",
|
||||||
|
@ -296,7 +248,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
run_velocity = 3.4,
|
run_velocity = 3.4,
|
||||||
damage = 7,
|
damage = 7,
|
||||||
reach = 2,
|
reach = 2,
|
||||||
particlespawners = psdefs,
|
|
||||||
drops = {
|
drops = {
|
||||||
{name = "mcl_throwing:ender_pearl",
|
{name = "mcl_throwing:ender_pearl",
|
||||||
chance = 1,
|
chance = 1,
|
||||||
|
@ -306,12 +257,23 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
},
|
},
|
||||||
animation = select_enderman_animation("normal"),
|
animation = select_enderman_animation("normal"),
|
||||||
_taken_node = "",
|
_taken_node = "",
|
||||||
can_spawn = function(pos)
|
|
||||||
return #minetest.find_nodes_in_area(vector.offset(pos,0,1,0),vector.offset(pos,0,3,0),{"air"}) > 2
|
|
||||||
end,
|
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
-- RAIN DAMAGE / EVASIVE WARP BEHAVIOUR HERE.
|
-- PARTICLE BEHAVIOUR HERE.
|
||||||
local enderpos = self.object:get_pos()
|
local enderpos = self.object:get_pos()
|
||||||
|
local chanceOfParticle = math.random(0, 1)
|
||||||
|
if chanceOfParticle == 1 then
|
||||||
|
minetest.add_particle({
|
||||||
|
pos = {x=enderpos.x+math.random(-1,1)*math.random()/2,y=enderpos.y+math.random(0,3),z=enderpos.z+math.random(-1,1)*math.random()/2},
|
||||||
|
velocity = {x=math.random(-.25,.25), y=math.random(-.25,.25), z=math.random(-.25,.25)},
|
||||||
|
acceleration = {x=math.random(-.5,.5), y=math.random(-.5,.5), z=math.random(-.5,.5)},
|
||||||
|
expirationtime = math.random(),
|
||||||
|
size = math.random(),
|
||||||
|
collisiondetection = true,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
-- RAIN DAMAGE / EVASIVE WARP BEHAVIOUR HERE.
|
||||||
local dim = mcl_worlds.pos_to_dimension(enderpos)
|
local dim = mcl_worlds.pos_to_dimension(enderpos)
|
||||||
if dim == "overworld" then
|
if dim == "overworld" then
|
||||||
if mcl_weather.state == "rain" or mcl_weather.state == "lightning" then
|
if mcl_weather.state == "rain" or mcl_weather.state == "lightning" then
|
||||||
|
@ -347,25 +309,24 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
else return end
|
else return end
|
||||||
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
|
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
|
||||||
if self.state == "attack" then
|
if self.state == "attack" then
|
||||||
if self.attack then
|
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
|
||||||
local target = self.attack
|
--self:teleport(nil)
|
||||||
local pos = target:get_pos()
|
--self.state = ""
|
||||||
if pos ~= nil then
|
--else
|
||||||
if vector.distance(self.object:get_pos(), target:get_pos()) > 10 then
|
if self.attack then
|
||||||
self:teleport(target)
|
local target = self.attack
|
||||||
|
local pos = target:get_pos()
|
||||||
|
if pos ~= nil then
|
||||||
|
if vector.distance(self.object:get_pos(), target:get_pos()) > 10 then
|
||||||
|
self:teleport(target)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
--end
|
||||||
else --if not attacking try to tp to the dark
|
|
||||||
local light = minetest.get_node_light(enderpos)
|
|
||||||
if light and light > minetest.LIGHT_MAX then
|
|
||||||
self:teleport(nil)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
||||||
-- Check for arrows and people nearby.
|
-- Check for arrows and people nearby.
|
||||||
|
local enderpos = self.object:get_pos()
|
||||||
enderpos = self.object:get_pos()
|
|
||||||
enderpos.y = enderpos.y + 1.5
|
enderpos.y = enderpos.y + 1.5
|
||||||
local objs = minetest.get_objects_inside_radius(enderpos, 2)
|
local objs = minetest.get_objects_inside_radius(enderpos, 2)
|
||||||
for n = 1, #objs do
|
for n = 1, #objs do
|
||||||
|
@ -394,7 +355,7 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
-- self:teleport(nil)
|
-- self:teleport(nil)
|
||||||
-- self.state = ""
|
-- self.state = ""
|
||||||
--else
|
--else
|
||||||
if self.attack ~= nil and enable_damage then
|
if self.attack ~= nil and not minetest.settings:get_bool("creative_mode") then
|
||||||
self.state = 'attack'
|
self.state = 'attack'
|
||||||
end
|
end
|
||||||
--end
|
--end
|
||||||
|
@ -497,7 +458,7 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
self.base_texture = create_enderman_textures(block_type, self._taken_node)
|
self.base_texture = create_enderman_textures(block_type, self._taken_node)
|
||||||
self.object:set_properties({ textures = self.base_texture })
|
self.object:set_properties({ textures = self.base_texture })
|
||||||
self.animation = select_enderman_animation("block")
|
self.animation = select_enderman_animation("block")
|
||||||
self:set_animation(self.animation.current)
|
mcl_mobs:set_animation(self, self.animation.current)
|
||||||
if def.sounds and def.sounds.dug then
|
if def.sounds and def.sounds.dug then
|
||||||
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
|
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
|
||||||
end
|
end
|
||||||
|
@ -520,7 +481,7 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
local def = minetest.registered_nodes[self._taken_node]
|
local def = minetest.registered_nodes[self._taken_node]
|
||||||
-- Update animation accordingly (removes visible block)
|
-- Update animation accordingly (removes visible block)
|
||||||
self.animation = select_enderman_animation("normal")
|
self.animation = select_enderman_animation("normal")
|
||||||
self:set_animation(self.animation.current)
|
mcl_mobs:set_animation(self, self.animation.current)
|
||||||
if def.sounds and def.sounds.place then
|
if def.sounds and def.sounds.place then
|
||||||
minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16}, true)
|
minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16}, true)
|
||||||
end
|
end
|
||||||
|
@ -629,18 +590,14 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
-- End spawn
|
-- End spawn
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
"mobs_mc:enderman",
|
"mobs_mc:enderman",
|
||||||
"end",
|
"end",
|
||||||
"ground",
|
"ground",
|
||||||
{
|
{
|
||||||
"End",
|
"End"
|
||||||
"EndIsland",
|
|
||||||
"EndMidlands",
|
|
||||||
"EndBarrens",
|
|
||||||
"EndBorder",
|
|
||||||
"EndSmallIslands"
|
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
minetest.LIGHT_MAX+1,
|
minetest.LIGHT_MAX+1,
|
||||||
|
@ -670,6 +627,7 @@ mcl_mobs:spawn_specific(
|
||||||
"Plains",
|
"Plains",
|
||||||
"Desert",
|
"Desert",
|
||||||
"ColdTaiga",
|
"ColdTaiga",
|
||||||
|
"MushroomIsland",
|
||||||
"IcePlainsSpikes",
|
"IcePlainsSpikes",
|
||||||
"SunflowerPlains",
|
"SunflowerPlains",
|
||||||
"IcePlains",
|
"IcePlains",
|
||||||
|
@ -696,6 +654,7 @@ mcl_mobs:spawn_specific(
|
||||||
"ExtremeHills_beach",
|
"ExtremeHills_beach",
|
||||||
"ColdTaiga_beach",
|
"ColdTaiga_beach",
|
||||||
"Swampland_shore",
|
"Swampland_shore",
|
||||||
|
"MushroomIslandShore",
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
"MesaPlateauFM_sandlevel",
|
"MesaPlateauFM_sandlevel",
|
||||||
|
@ -734,6 +693,7 @@ mcl_mobs:spawn_specific(
|
||||||
"Forest_deep_ocean",
|
"Forest_deep_ocean",
|
||||||
"JungleM_deep_ocean",
|
"JungleM_deep_ocean",
|
||||||
"FlowerForest_deep_ocean",
|
"FlowerForest_deep_ocean",
|
||||||
|
"MushroomIsland_ocean",
|
||||||
"MegaTaiga_ocean",
|
"MegaTaiga_ocean",
|
||||||
"StoneBeach_deep_ocean",
|
"StoneBeach_deep_ocean",
|
||||||
"IcePlainsSpikes_deep_ocean",
|
"IcePlainsSpikes_deep_ocean",
|
||||||
|
@ -743,6 +703,7 @@ mcl_mobs:spawn_specific(
|
||||||
"MesaBryce_deep_ocean",
|
"MesaBryce_deep_ocean",
|
||||||
"ExtremeHills+_deep_ocean",
|
"ExtremeHills+_deep_ocean",
|
||||||
"ExtremeHills_ocean",
|
"ExtremeHills_ocean",
|
||||||
|
"MushroomIsland_deep_ocean",
|
||||||
"Forest_ocean",
|
"Forest_ocean",
|
||||||
"MegaTaiga_deep_ocean",
|
"MegaTaiga_deep_ocean",
|
||||||
"JungleEdge_ocean",
|
"JungleEdge_ocean",
|
||||||
|
@ -768,6 +729,7 @@ mcl_mobs:spawn_specific(
|
||||||
"RoofedForest_underground",
|
"RoofedForest_underground",
|
||||||
"Jungle_underground",
|
"Jungle_underground",
|
||||||
"Swampland_underground",
|
"Swampland_underground",
|
||||||
|
"MushroomIsland_underground",
|
||||||
"BirchForest_underground",
|
"BirchForest_underground",
|
||||||
"Plains_underground",
|
"Plains_underground",
|
||||||
"MesaPlateauF_underground",
|
"MesaPlateauF_underground",
|
||||||
|
@ -808,7 +770,7 @@ mcl_mobs:spawn_specific(
|
||||||
"SoulsandValley",
|
"SoulsandValley",
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
11,
|
minetest.LIGHT_MAX+1,
|
||||||
30,
|
30,
|
||||||
27500,
|
27500,
|
||||||
4,
|
4,
|
||||||
|
@ -824,7 +786,7 @@ mcl_mobs:spawn_specific(
|
||||||
"WarpedForest"
|
"WarpedForest"
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
11,
|
minetest.LIGHT_MAX+1,
|
||||||
30,
|
30,
|
||||||
5000,
|
5000,
|
||||||
4,
|
4,
|
||||||
|
@ -832,4 +794,4 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:enderman", S("Enderman"), "#252525", "#151515", 0)
|
mcl_mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:endermite", {
|
mcl_mobs:register_mob("mobs_mc:endermite", {
|
||||||
description = S("Endermite"),
|
description = S("Endermite"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -38,4 +38,4 @@ mcl_mobs.register_mob("mobs_mc:endermite", {
|
||||||
reach = 1,
|
reach = 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs.register_egg("mobs_mc:endermite", S("Endermite"), "#161616", "#6d6d6d", 0)
|
mcl_mobs:register_egg("mobs_mc:endermite", S("Endermite"), "mobs_mc_spawn_icon_endermite.png", 0)
|
||||||
|
|
|
@ -10,7 +10,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:ghast", {
|
mcl_mobs:register_mob("mobs_mc:ghast", {
|
||||||
description = S("Ghast"),
|
description = S("Ghast"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -23,7 +23,6 @@ mcl_mobs.register_mob("mobs_mc:ghast", {
|
||||||
collisionbox = {-2, 5, -2, 2, 9, 2},
|
collisionbox = {-2, 5, -2, 2, 9, 2},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_ghast.b3d",
|
mesh = "mobs_mc_ghast.b3d",
|
||||||
spawn_in_group = 1,
|
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_ghast.png"},
|
{"mobs_mc_ghast.png"},
|
||||||
},
|
},
|
||||||
|
@ -65,14 +64,6 @@ mcl_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,
|
||||||
can_spawn = function(pos)
|
|
||||||
if not minetest.get_item_group(minetest.get_node(pos).name,"solid") then return false end
|
|
||||||
local p1=vector.offset(pos,-2,1,-2)
|
|
||||||
local p2=vector.offset(pos,2,5,2)
|
|
||||||
local nn = minetest.find_nodes_in_area(p1,p2,{"air"})
|
|
||||||
if #nn< 41 then return false end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
do_custom = function(self)
|
do_custom = function(self)
|
||||||
if self.firing == true then
|
if self.firing == true then
|
||||||
self.base_texture = {"mobs_mc_ghast_firing.png"}
|
self.base_texture = {"mobs_mc_ghast_firing.png"}
|
||||||
|
@ -103,7 +94,7 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- fireball (projectile)
|
-- fireball (projectile)
|
||||||
mcl_mobs.register_arrow("mobs_mc:fireball", {
|
mcl_mobs:register_arrow("mobs_mc:fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 1, y = 1},
|
visual_size = {x = 1, y = 1},
|
||||||
textures = {"mcl_fire_fire_charge.png"},
|
textures = {"mcl_fire_fire_charge.png"},
|
||||||
|
@ -118,9 +109,9 @@ mcl_mobs.register_arrow("mobs_mc:fireball", {
|
||||||
}, nil)
|
}, nil)
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
if p then
|
if p then
|
||||||
mcl_mobs.mob_class.boom(self,p, 1, true)
|
mcl_mobs:boom(self, p, 1, true)
|
||||||
else
|
else
|
||||||
mcl_mobs.mob_class.boom(self,player:get_pos(), 1, true)
|
mcl_mobs:boom(self, player:get_pos(), 1, true)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -129,11 +120,11 @@ mcl_mobs.register_arrow("mobs_mc:fireball", {
|
||||||
full_punch_interval = 1.0,
|
full_punch_interval = 1.0,
|
||||||
damage_groups = {fleshy = 6},
|
damage_groups = {fleshy = 6},
|
||||||
}, nil)
|
}, nil)
|
||||||
mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1, true)
|
mcl_mobs:boom(self, self.object:get_pos(), 1, true)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
hit_node = function(self, pos, node)
|
hit_node = function(self, pos, node)
|
||||||
mcl_mobs.mob_class.boom(self,pos, 1, true)
|
mcl_mobs:boom(self, pos, 1, true)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -141,4 +132,4 @@ mcl_mobs.register_arrow("mobs_mc:fireball", {
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:ghast", S("Ghast"), "#f9f9f9", "#bcbcbc", 0)
|
mcl_mobs:register_egg("mobs_mc:ghast", S("Ghast"), "mobs_mc_spawn_icon_ghast.png", 0)
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
--MCmobs v0.4
|
|
||||||
--maikerumine
|
|
||||||
--made for MC like Survival game
|
|
||||||
--License for code WTFPL and otherwise stated in readmes
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
|
||||||
|
|
||||||
local base_psdef = {
|
|
||||||
amount = 8,
|
|
||||||
time=0,
|
|
||||||
minpos = vector.new(-1,-1,-1),
|
|
||||||
maxpos = vector.new(1,1,1),
|
|
||||||
minvel = vector.new(-0.25,-0.25,-0.25),
|
|
||||||
maxvel = vector.new(0.25,0.25,0.25),
|
|
||||||
minacc = vector.new(-0.5,-0.5,-0.5),
|
|
||||||
maxacc = vector.new(0.5,0.5,0.5),
|
|
||||||
minexptime = 1,
|
|
||||||
maxexptime = 2,
|
|
||||||
minsize = 0.8,
|
|
||||||
maxsize= 1.5,
|
|
||||||
glow = 5,
|
|
||||||
collisiondetection = true,
|
|
||||||
collision_removal = true,
|
|
||||||
}
|
|
||||||
local psdefs = {}
|
|
||||||
for i=1,4 do
|
|
||||||
local p = table.copy(base_psdef)
|
|
||||||
p.texture = "extra_mobs_glow_squid_glint"..i..".png"
|
|
||||||
table.insert(psdefs,p)
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:glow_squid", {
|
|
||||||
type = "animal",
|
|
||||||
spawn_class = "water",
|
|
||||||
can_despawn = true,
|
|
||||||
passive = true,
|
|
||||||
hp_min = 10,
|
|
||||||
hp_max = 10,
|
|
||||||
xp_min = 1,
|
|
||||||
xp_max = 3,
|
|
||||||
armor = 100,
|
|
||||||
rotate = 0,
|
|
||||||
-- tilt_swim breaks the animations.
|
|
||||||
--tilt_swim = true,
|
|
||||||
-- FIXME: If the qlow squid is near the floor, it turns black
|
|
||||||
collisionbox = { -0.4, 0.0, -0.4, 0.4, 0.9, 0.4 },
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "extra_mobs_glow_squid.b3d",
|
|
||||||
textures = {
|
|
||||||
{ "extra_mobs_glow_squid.png" }
|
|
||||||
},
|
|
||||||
sounds = {
|
|
||||||
damage = { name = "mobs_mc_squid_hurt", gain = 0.3 },
|
|
||||||
death = { name = "mobs_mc_squid_death", gain = 0.4 },
|
|
||||||
flop = "mobs_mc_squid_flop",
|
|
||||||
distance = 16,
|
|
||||||
},
|
|
||||||
animation = {
|
|
||||||
stand_start = 1,
|
|
||||||
stand_end = 60,
|
|
||||||
walk_start = 1,
|
|
||||||
walk_end = 60,
|
|
||||||
run_start = 1,
|
|
||||||
run_end = 60,
|
|
||||||
},
|
|
||||||
drops = {
|
|
||||||
{ name = "mcl_mobitems:glow_ink_sac",
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 3,
|
|
||||||
looting = "common", },
|
|
||||||
},
|
|
||||||
visual_size = { x = 3, y = 3 },
|
|
||||||
makes_footstep_sound = false,
|
|
||||||
swim = true,
|
|
||||||
breathes_in_water = true,
|
|
||||||
jump = false,
|
|
||||||
view_range = 16,
|
|
||||||
runaway = true,
|
|
||||||
fear_height = 4,
|
|
||||||
fly = true,
|
|
||||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
|
||||||
-- don't add "mcl_core:water_flowing", or it won't move vertically.
|
|
||||||
|
|
||||||
glow = minetest.LIGHT_MAX,
|
|
||||||
particlespawners = psdefs,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- spawning
|
|
||||||
local water = mobs_mc.water_level - 1
|
|
||||||
-- local water = mobs_mc.spawn_height.water + 1
|
|
||||||
mcl_mobs:spawn_specific(
|
|
||||||
"mobs_mc:glow_squid",
|
|
||||||
"overworld",
|
|
||||||
"water",
|
|
||||||
{
|
|
||||||
"Mesa",
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"Jungle",
|
|
||||||
"Savanna",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
|
||||||
"MushroomIsland",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"IcePlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"JungleM",
|
|
||||||
"BirchForestM",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
"JungleEdge",
|
|
||||||
"SavannaM",
|
|
||||||
"FlowerForest_beach",
|
|
||||||
"Forest_beach",
|
|
||||||
"StoneBeach",
|
|
||||||
"ColdTaiga_beach_water",
|
|
||||||
"Taiga_beach",
|
|
||||||
"Savanna_beach",
|
|
||||||
"Plains_beach",
|
|
||||||
"ExtremeHills_beach",
|
|
||||||
"ColdTaiga_beach",
|
|
||||||
"Swampland_shore",
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"JungleM_shore",
|
|
||||||
"Jungle_shore",
|
|
||||||
"MesaPlateauFM_sandlevel",
|
|
||||||
"MesaPlateauF_sandlevel",
|
|
||||||
"MesaBryce_sandlevel",
|
|
||||||
"Mesa_sandlevel",
|
|
||||||
"RoofedForest_ocean",
|
|
||||||
"JungleEdgeM_ocean",
|
|
||||||
"BirchForestM_ocean",
|
|
||||||
"BirchForest_ocean",
|
|
||||||
"IcePlains_deep_ocean",
|
|
||||||
"Jungle_deep_ocean",
|
|
||||||
"Savanna_ocean",
|
|
||||||
"MesaPlateauF_ocean",
|
|
||||||
"ExtremeHillsM_deep_ocean",
|
|
||||||
"Savanna_deep_ocean",
|
|
||||||
"SunflowerPlains_ocean",
|
|
||||||
"Swampland_deep_ocean",
|
|
||||||
"Swampland_ocean",
|
|
||||||
"MegaSpruceTaiga_deep_ocean",
|
|
||||||
"ExtremeHillsM_ocean",
|
|
||||||
"JungleEdgeM_deep_ocean",
|
|
||||||
"SunflowerPlains_deep_ocean",
|
|
||||||
"BirchForest_deep_ocean",
|
|
||||||
"IcePlainsSpikes_ocean",
|
|
||||||
"Mesa_ocean",
|
|
||||||
"StoneBeach_ocean",
|
|
||||||
"Plains_deep_ocean",
|
|
||||||
"JungleEdge_deep_ocean",
|
|
||||||
"SavannaM_deep_ocean",
|
|
||||||
"Desert_deep_ocean",
|
|
||||||
"Mesa_deep_ocean",
|
|
||||||
"ColdTaiga_deep_ocean",
|
|
||||||
"Plains_ocean",
|
|
||||||
"MesaPlateauFM_ocean",
|
|
||||||
"Forest_deep_ocean",
|
|
||||||
"JungleM_deep_ocean",
|
|
||||||
"FlowerForest_deep_ocean",
|
|
||||||
"MushroomIsland_ocean",
|
|
||||||
"MegaTaiga_ocean",
|
|
||||||
"StoneBeach_deep_ocean",
|
|
||||||
"IcePlainsSpikes_deep_ocean",
|
|
||||||
"ColdTaiga_ocean",
|
|
||||||
"SavannaM_ocean",
|
|
||||||
"MesaPlateauF_deep_ocean",
|
|
||||||
"MesaBryce_deep_ocean",
|
|
||||||
"ExtremeHills+_deep_ocean",
|
|
||||||
"ExtremeHills_ocean",
|
|
||||||
"MushroomIsland_deep_ocean",
|
|
||||||
"Forest_ocean",
|
|
||||||
"MegaTaiga_deep_ocean",
|
|
||||||
"JungleEdge_ocean",
|
|
||||||
"MesaBryce_ocean",
|
|
||||||
"MegaSpruceTaiga_ocean",
|
|
||||||
"ExtremeHills+_ocean",
|
|
||||||
"Jungle_ocean",
|
|
||||||
"RoofedForest_deep_ocean",
|
|
||||||
"IcePlains_ocean",
|
|
||||||
"FlowerForest_ocean",
|
|
||||||
"ExtremeHills_deep_ocean",
|
|
||||||
"MesaPlateauFM_deep_ocean",
|
|
||||||
"Desert_ocean",
|
|
||||||
"Taiga_ocean",
|
|
||||||
"BirchForestM_deep_ocean",
|
|
||||||
"Taiga_deep_ocean",
|
|
||||||
"JungleM_ocean",
|
|
||||||
"FlowerForest_underground",
|
|
||||||
"JungleEdge_underground",
|
|
||||||
"StoneBeach_underground",
|
|
||||||
"MesaBryce_underground",
|
|
||||||
"Mesa_underground",
|
|
||||||
"RoofedForest_underground",
|
|
||||||
"Jungle_underground",
|
|
||||||
"Swampland_underground",
|
|
||||||
"MushroomIsland_underground",
|
|
||||||
"BirchForest_underground",
|
|
||||||
"Plains_underground",
|
|
||||||
"MesaPlateauF_underground",
|
|
||||||
"ExtremeHills_underground",
|
|
||||||
"MegaSpruceTaiga_underground",
|
|
||||||
"BirchForestM_underground",
|
|
||||||
"SavannaM_underground",
|
|
||||||
"MesaPlateauFM_underground",
|
|
||||||
"Desert_underground",
|
|
||||||
"Savanna_underground",
|
|
||||||
"Forest_underground",
|
|
||||||
"SunflowerPlains_underground",
|
|
||||||
"ColdTaiga_underground",
|
|
||||||
"IcePlains_underground",
|
|
||||||
"IcePlainsSpikes_underground",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX + 1,
|
|
||||||
30,
|
|
||||||
10000,
|
|
||||||
3,
|
|
||||||
water - 16,
|
|
||||||
water)
|
|
||||||
|
|
||||||
-- spawn egg
|
|
||||||
mcl_mobs.register_egg("mobs_mc:glow_squid", S("Glow Squid"), "#095757", "#87f6c0", 0)
|
|
|
@ -4,18 +4,16 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:guardian", {
|
mcl_mobs:register_mob("mobs_mc:guardian", {
|
||||||
description = S("Guardian"),
|
description = S("Guardian"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
spawn_in_group_min = 2,
|
|
||||||
spawn_in_group = 4,
|
|
||||||
hp_min = 30,
|
hp_min = 30,
|
||||||
hp_max = 30,
|
hp_max = 30,
|
||||||
xp_min = 10,
|
xp_min = 10,
|
||||||
xp_max = 10,
|
xp_max = 10,
|
||||||
breath_max = -1,
|
breath_max = -1,
|
||||||
passive = false,
|
passive = false,
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
|
@ -104,4 +102,4 @@ mcl_mobs.register_mob("mobs_mc:guardian", {
|
||||||
--mcl_mobs:spawn_specific("mobs_mc:guardian", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level - 10)
|
--mcl_mobs:spawn_specific("mobs_mc:guardian", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level - 10)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:guardian", S("Guardian"), "#5a8272", "#f17d31", 0)
|
mcl_mobs:register_egg("mobs_mc:guardian", S("Guardian"), "mobs_mc_spawn_icon_guardian.png", 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:guardian_elder", {
|
mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
||||||
description = S("Elder Guardian"),
|
description = S("Elder Guardian"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -112,6 +112,5 @@ mcl_mobs.register_mob("mobs_mc:guardian_elder", {
|
||||||
-- mcl_mobs:spawn_specific("mobs_mc:guardian_elder", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level-18)
|
-- mcl_mobs:spawn_specific("mobs_mc:guardian_elder", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level-18)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "#ceccba", "#747693", 0)
|
mcl_mobs:register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "mobs_mc_spawn_icon_guardian_elder.png", 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,135 +0,0 @@
|
||||||
--MCmobs v0.4
|
|
||||||
--maikerumine
|
|
||||||
--made for MC like Survival game
|
|
||||||
--License for code WTFPL and otherwise stated in readmes
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
|
||||||
|
|
||||||
--###################
|
|
||||||
--################### hoglin
|
|
||||||
--###################
|
|
||||||
|
|
||||||
local hoglin = {
|
|
||||||
type = "monster",
|
|
||||||
passive = false,
|
|
||||||
spawn_class = "hostile",
|
|
||||||
hp_min = 40,
|
|
||||||
hp_max = 40,
|
|
||||||
xp_min = 9,
|
|
||||||
xp_max = 9,
|
|
||||||
armor = {fleshy = 90},
|
|
||||||
attack_type = "dogfight",
|
|
||||||
damage = 4,
|
|
||||||
reach = 3,
|
|
||||||
collisionbox = {-.6, -0.01, -.6, .6, 1.4, .6},
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "extra_mobs_hoglin.b3d",
|
|
||||||
textures = { {
|
|
||||||
"extra_mobs_hoglin.png",
|
|
||||||
} },
|
|
||||||
visual_size = {x=3, y=3},
|
|
||||||
sounds = {
|
|
||||||
random = "extra_mobs_hoglin",
|
|
||||||
damage = "extra_mobs_hoglin_hurt",
|
|
||||||
distance = 16,
|
|
||||||
},
|
|
||||||
jump = true,
|
|
||||||
makes_footstep_sound = true,
|
|
||||||
walk_velocity = 1,
|
|
||||||
run_velocity = 4,
|
|
||||||
drops = {
|
|
||||||
{name = "mobs_mcitems:leather",
|
|
||||||
chance = 1,
|
|
||||||
min = 0,
|
|
||||||
max = 1,},
|
|
||||||
},
|
|
||||||
drops = {
|
|
||||||
{name = "mcl_mobitems:porkchop",
|
|
||||||
chance = 1,
|
|
||||||
min = 2,
|
|
||||||
max = 4,},
|
|
||||||
},
|
|
||||||
animation = {
|
|
||||||
stand_speed = 7,
|
|
||||||
walk_speed = 7,
|
|
||||||
run_speed = 15,
|
|
||||||
stand_start = 24,
|
|
||||||
stand_end = 24,
|
|
||||||
walk_start = 11,
|
|
||||||
walk_end = 21,
|
|
||||||
run_start = 1,
|
|
||||||
run_end = 10,
|
|
||||||
punch_start = 22,
|
|
||||||
punch_end = 32,
|
|
||||||
},
|
|
||||||
fear_height = 4,
|
|
||||||
view_range = 32,
|
|
||||||
floats = 0,
|
|
||||||
custom_attack = function(self)
|
|
||||||
if self.state == "attack" and self.reach > vector.distance(self.object:get_pos(), self.attack:get_pos()) then
|
|
||||||
self.attack:add_velocity({x=0,y=13,z=0})
|
|
||||||
self.attack:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = self.damage}
|
|
||||||
}, nil)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
do_custom = function(self)
|
|
||||||
if self.object:get_pos().y > -100 then
|
|
||||||
local zog = minetest.add_entity(self.object:get_pos(), "mobs_mc:zoglin")
|
|
||||||
zog:set_rotation(self.object:get_rotation())
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
attack_animals = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:hoglin", hoglin)
|
|
||||||
|
|
||||||
local zoglin = table.copy(hoglin)
|
|
||||||
zoglin.fire_resistant = 1
|
|
||||||
zoglin.textures = {"extra_mobs_zoglin.png"}
|
|
||||||
zoglin.do_custom = function()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
zoglin.attacks_monsters = true
|
|
||||||
zoglin.lava_damage = 0
|
|
||||||
zoglin.fire_damage = 0
|
|
||||||
mcl_mobs.register_mob("mobs_mc:zoglin", zoglin)
|
|
||||||
|
|
||||||
-- Baby hoglin.
|
|
||||||
|
|
||||||
local baby_hoglin = table.copy(hoglin)
|
|
||||||
baby_hoglin.collisionbox = {-.3, -0.01, -.3, .3, 0.94, .3}
|
|
||||||
baby_hoglin.xp_min = 20
|
|
||||||
baby_hoglin.xp_max = 20
|
|
||||||
baby_hoglin.visual_size = {x=hoglin.visual_size.x/2, y=hoglin.visual_size.y/2}
|
|
||||||
textures = { {
|
|
||||||
"extra_mobs_hoglin.png",
|
|
||||||
"extra_mobs_trans.png",
|
|
||||||
} }
|
|
||||||
baby_hoglin.walk_velocity = 1.2
|
|
||||||
baby_hoglin.run_velocity = 2.4
|
|
||||||
baby_hoglin.child = 1
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:baby_hoglin", baby_hoglin)
|
|
||||||
|
|
||||||
-- Regular spawning in the Nether
|
|
||||||
mcl_mobs:spawn_specific(
|
|
||||||
"mobs_mc:hoglin",
|
|
||||||
"nether",
|
|
||||||
"ground",
|
|
||||||
{
|
|
||||||
"Nether",
|
|
||||||
"CrimsonForest"
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
minetest.LIGHT_MAX+1,
|
|
||||||
30,
|
|
||||||
6000,
|
|
||||||
3,
|
|
||||||
mcl_vars.mg_nether_min,
|
|
||||||
mcl_vars.mg_nether_max)
|
|
||||||
|
|
||||||
-- spawn eggs
|
|
||||||
mcl_mobs.register_egg("mobs_mc:hoglin", S("Hoglin"), "#85682e", "#2b2140", 0)
|
|
|
@ -34,30 +34,6 @@ local horse_extra_texture = function(horse)
|
||||||
return textures
|
return textures
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function get_drops(self)
|
|
||||||
self.drops = {}
|
|
||||||
table.insert(self.drops,
|
|
||||||
{name = "mcl_mobitems:leather",
|
|
||||||
chance = 1,
|
|
||||||
min = 0,
|
|
||||||
max = 2,
|
|
||||||
looting = "common",
|
|
||||||
})
|
|
||||||
if self._saddle then
|
|
||||||
table.insert(self.drops,{name = "mcl_mobitems:saddle",
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 1,})
|
|
||||||
end
|
|
||||||
if self._chest then
|
|
||||||
table.insert(self.drops,{name = "mcl_chests:chest",
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 1,})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper functions to determine equipment rules
|
-- Helper functions to determine equipment rules
|
||||||
local can_equip_horse_armor = function(entity_id)
|
local can_equip_horse_armor = function(entity_id)
|
||||||
return entity_id == "mobs_mc:horse" or entity_id == "mobs_mc:skeleton_horse" or entity_id == "mobs_mc:zombie_horse"
|
return entity_id == "mobs_mc:horse" or entity_id == "mobs_mc:skeleton_horse" or entity_id == "mobs_mc:zombie_horse"
|
||||||
|
@ -124,8 +100,6 @@ local horse = {
|
||||||
description = S("Horse"),
|
description = S("Horse"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
spawn_in_group_min = 2,
|
|
||||||
spawn_in_group = 6,
|
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_horse.b3d",
|
mesh = "mobs_mc_horse.b3d",
|
||||||
visual_size = {x=3.0, y=3.0},
|
visual_size = {x=3.0, y=3.0},
|
||||||
|
@ -181,12 +155,6 @@ local horse = {
|
||||||
on_spawn = update_textures,
|
on_spawn = update_textures,
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
if not self._horse_speed then
|
|
||||||
self._horse_speed = math.random(486, 1457)/100
|
|
||||||
elseif self.run_velocity ~= self._horse_speed then
|
|
||||||
self.run_velocity = self._horse_speed
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set needed values if not already present
|
-- set needed values if not already present
|
||||||
if not self._regentimer then
|
if not self._regentimer then
|
||||||
self._regentimer = 0
|
self._regentimer = 0
|
||||||
|
@ -267,27 +235,6 @@ local horse = {
|
||||||
local iname = item:get_name()
|
local iname = item:get_name()
|
||||||
local heal = 0
|
local heal = 0
|
||||||
|
|
||||||
if self._inv_id then
|
|
||||||
if not self._chest and item:get_name() == "mcl_chests:chest" then
|
|
||||||
item:take_item()
|
|
||||||
clicker:set_wielded_item(item)
|
|
||||||
self._chest = true
|
|
||||||
-- Update texture
|
|
||||||
if not self._naked_texture then
|
|
||||||
-- Base horse texture without chest or saddle
|
|
||||||
self._naked_texture = self.base_texture[2]
|
|
||||||
end
|
|
||||||
local tex = horse_extra_texture(self)
|
|
||||||
self.base_texture = tex
|
|
||||||
self.object:set_properties({textures = self.base_texture})
|
|
||||||
get_drops(self)
|
|
||||||
return
|
|
||||||
elseif self._chest and clicker:get_player_control().sneak then
|
|
||||||
mcl_entity_invs.show_inv_form(self,clicker)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Taming
|
-- Taming
|
||||||
self.temper = self.temper or (math.random(1,100))
|
self.temper = self.temper or (math.random(1,100))
|
||||||
|
|
||||||
|
@ -337,7 +284,7 @@ local horse = {
|
||||||
elseif (iname == "mcl_farming:carrot_item_gold") then
|
elseif (iname == "mcl_farming:carrot_item_gold") then
|
||||||
heal = 4
|
heal = 4
|
||||||
end
|
end
|
||||||
if heal > 0 and self:feed_tame(clicker, heal, true, false) then
|
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, true, false) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -352,7 +299,7 @@ local horse = {
|
||||||
elseif (iname == "mcl_farming:hay_block") then
|
elseif (iname == "mcl_farming:hay_block") then
|
||||||
heal = 20
|
heal = 20
|
||||||
end
|
end
|
||||||
if heal > 0 and self:feed_tame(clicker, heal, false, false) then
|
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, false, false) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -391,7 +338,6 @@ local horse = {
|
||||||
self.base_texture = tex
|
self.base_texture = tex
|
||||||
self.object:set_properties({textures = self.base_texture})
|
self.object:set_properties({textures = self.base_texture})
|
||||||
minetest.sound_play({name = "mcl_armor_equip_leather"}, {gain=0.5, max_hear_distance=12, pos=self.object:get_pos()}, true)
|
minetest.sound_play({name = "mcl_armor_equip_leather"}, {gain=0.5, max_hear_distance=12, pos=self.object:get_pos()}, true)
|
||||||
get_drops(self)
|
|
||||||
|
|
||||||
-- Put on horse armor if tamed
|
-- Put on horse armor if tamed
|
||||||
elseif can_equip_horse_armor(self.name) and not self.driver and not self._horse_armor
|
elseif can_equip_horse_armor(self.name) and not self.driver and not self._horse_armor
|
||||||
|
@ -441,7 +387,7 @@ local horse = {
|
||||||
|
|
||||||
on_breed = function(parent1, parent2)
|
on_breed = function(parent1, parent2)
|
||||||
local pos = parent1.object:get_pos()
|
local pos = parent1.object:get_pos()
|
||||||
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
local child = mcl_mobs:spawn_child(pos, parent1.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
local p = math.random(1, 2)
|
local p = math.random(1, 2)
|
||||||
|
@ -490,7 +436,7 @@ local horse = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:horse", horse)
|
mcl_mobs:register_mob("mobs_mc:horse", horse)
|
||||||
|
|
||||||
-- Skeleton horse
|
-- Skeleton horse
|
||||||
local skeleton_horse = table.copy(horse)
|
local skeleton_horse = table.copy(horse)
|
||||||
|
@ -513,7 +459,7 @@ skeleton_horse.sounds = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
}
|
}
|
||||||
skeleton_horse.harmed_by_heal = true
|
skeleton_horse.harmed_by_heal = true
|
||||||
mcl_mobs.register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
mcl_mobs:register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
||||||
|
|
||||||
-- Zombie horse
|
-- Zombie horse
|
||||||
local zombie_horse = table.copy(horse)
|
local zombie_horse = table.copy(horse)
|
||||||
|
@ -537,15 +483,13 @@ zombie_horse.sounds = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
}
|
}
|
||||||
zombie_horse.harmed_by_heal = true
|
zombie_horse.harmed_by_heal = true
|
||||||
mcl_mobs.register_mob("mobs_mc:zombie_horse", zombie_horse)
|
mcl_mobs:register_mob("mobs_mc:zombie_horse", zombie_horse)
|
||||||
|
|
||||||
-- Donkey
|
-- Donkey
|
||||||
local d = 0.86 -- donkey scale
|
local d = 0.86 -- donkey scale
|
||||||
local donkey = table.copy(horse)
|
local donkey = table.copy(horse)
|
||||||
donkey.description = S("Donkey")
|
donkey.description = S("Donkey")
|
||||||
donkey.textures = {{"blank.png", "mobs_mc_donkey.png", "blank.png"}}
|
donkey.textures = {{"blank.png", "mobs_mc_donkey.png", "blank.png"}}
|
||||||
donkey.spawn_in_group = 3
|
|
||||||
donkey.spawn_in_group_min = 1
|
|
||||||
donkey.animation = {
|
donkey.animation = {
|
||||||
speed_normal = 25,
|
speed_normal = 25,
|
||||||
stand_start = 0, stand_end = 0,
|
stand_start = 0, stand_end = 0,
|
||||||
|
@ -570,9 +514,8 @@ donkey.collisionbox = {
|
||||||
donkey.jump = true
|
donkey.jump = true
|
||||||
donkey.jump_height = 3.75 -- can clear 1 block height
|
donkey.jump_height = 3.75 -- can clear 1 block height
|
||||||
|
|
||||||
|
mcl_mobs:register_mob("mobs_mc:donkey", donkey)
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:donkey", donkey)
|
|
||||||
mcl_entity_invs.register_inv("mobs_mc:donkey","Donkey",15,true)
|
|
||||||
-- Mule
|
-- Mule
|
||||||
local m = 0.94
|
local m = 0.94
|
||||||
local mule = table.copy(donkey)
|
local mule = table.copy(donkey)
|
||||||
|
@ -589,8 +532,7 @@ mule.collisionbox = {
|
||||||
horse.collisionbox[5] * m,
|
horse.collisionbox[5] * m,
|
||||||
horse.collisionbox[6] * m,
|
horse.collisionbox[6] * m,
|
||||||
}
|
}
|
||||||
mcl_mobs.register_mob("mobs_mc:mule", mule)
|
mcl_mobs:register_mob("mobs_mc:mule", mule)
|
||||||
mcl_entity_invs.register_inv("mobs_mc:mule","Mule",15,true)
|
|
||||||
|
|
||||||
--===========================
|
--===========================
|
||||||
--Spawn Function
|
--Spawn Function
|
||||||
|
@ -632,7 +574,7 @@ mcl_mobs:spawn_specific(
|
||||||
"Savanna_beach",
|
"Savanna_beach",
|
||||||
"Plains_beach",
|
"Plains_beach",
|
||||||
},
|
},
|
||||||
9,
|
0,
|
||||||
minetest.LIGHT_MAX+1,
|
minetest.LIGHT_MAX+1,
|
||||||
30,
|
30,
|
||||||
15000,
|
15000,
|
||||||
|
@ -641,8 +583,8 @@ mobs_mc.water_level+3,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:horse", S("Horse"), "#c09e7d", "#eee500", 0)
|
mcl_mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
|
||||||
mcl_mobs.register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "#68684f", "#e5e5d8", 0)
|
mcl_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"), "#2a5a37", "#84d080", 0)
|
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
|
||||||
mcl_mobs.register_egg("mobs_mc:donkey", S("Donkey"), "#534539", "#867566", 0)
|
mcl_mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
|
||||||
mcl_mobs.register_egg("mobs_mc:mule", S("Mule"), "#1b0200", "#51331d", 0)
|
mcl_mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)
|
||||||
|
|
|
@ -96,7 +96,6 @@ mobs_mc.water_level = tonumber(minetest.settings:get("water_level")) or 0
|
||||||
|
|
||||||
-- Animals
|
-- Animals
|
||||||
local path = minetest.get_modpath("mobs_mc")
|
local path = minetest.get_modpath("mobs_mc")
|
||||||
dofile(path .. "/axolotl.lua") -- Mesh and animation by JoeEnderman, Textures by Nova Wustra, modified by JoeEnderman
|
|
||||||
dofile(path .. "/bat.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/bat.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/rabbit.lua") -- Mesh and animation byExeterDad
|
dofile(path .. "/rabbit.lua") -- Mesh and animation byExeterDad
|
||||||
dofile(path .. "/chicken.lua") -- Mesh and animation by Pavel_S
|
dofile(path .. "/chicken.lua") -- Mesh and animation by Pavel_S
|
||||||
|
@ -115,7 +114,6 @@ dofile(path .. "/squid.lua") -- Animation, sound and egg texture by daufinsyd
|
||||||
dofile(path .. "/villager.lua") -- KrupnoPavel Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager.lua") -- KrupnoPavel Mesh and animation by toby109tt / https://github.com/22i
|
||||||
|
|
||||||
-- Illagers and witch
|
-- Illagers and witch
|
||||||
dofile(path .. "/pillager.lua") -- Mesh by KrupnoPavel and MrRar, animation by MrRar
|
|
||||||
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
|
@ -144,16 +142,3 @@ dofile(path .. "/slime+magma_cube.lua") -- Wuzzy
|
||||||
dofile(path .. "/spider.lua") -- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
dofile(path .. "/spider.lua") -- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
||||||
dofile(path .. "/vex.lua") -- KrupnoPavel
|
dofile(path .. "/vex.lua") -- KrupnoPavel
|
||||||
dofile(path .. "/wither.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/wither.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
|
|
||||||
dofile(path .. "/cod.lua")
|
|
||||||
dofile(path .. "/salmon.lua")
|
|
||||||
dofile(path .. "/tropical_fish.lua")
|
|
||||||
dofile(path .. "/dolphin.lua")
|
|
||||||
|
|
||||||
|
|
||||||
dofile(path .. "/glow_squid.lua")
|
|
||||||
|
|
||||||
dofile(path .. "/piglin.lua")
|
|
||||||
dofile(path .. "/hoglin+zoglin.lua")
|
|
||||||
|
|
||||||
dofile(path .. "/strider.lua")
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
local etime = 0
|
local etime = 0
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
||||||
description = S("Iron Golem"),
|
description = S("Iron Golem"),
|
||||||
type = "npc",
|
type = "npc",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -22,9 +22,6 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 2.69, 0.7},
|
collisionbox = {-0.7, -0.01, -0.7, 0.7, 2.69, 0.7},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_iron_golem.b3d",
|
mesh = "mobs_mc_iron_golem.b3d",
|
||||||
head_swivel = "head.control",
|
|
||||||
bone_eye_height = 3.38,
|
|
||||||
curiosity = 10,
|
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_iron_golem.png"},
|
{"mobs_mc_iron_golem.png"},
|
||||||
},
|
},
|
||||||
|
@ -48,14 +45,13 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
_got_poppy = false,
|
_got_poppy = false,
|
||||||
pick_up = {"mcl_flowers:poppy"},
|
pick_up = {"mcl_flowers:poppy"},
|
||||||
on_pick_up = function(self,n)
|
on_pick_up = function(self,n)
|
||||||
local it = ItemStack(n.itemstring)
|
if n.itemstring:find("mcl_flowers:poppy") then
|
||||||
if it:get_name() == "mcl_flowers:poppy" then
|
|
||||||
if not self._got_poppy then
|
if not self._got_poppy then
|
||||||
self._got_poppy=true
|
self._got_poppy=true
|
||||||
it:take_item(1)
|
return
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
return it
|
|
||||||
end,
|
end,
|
||||||
replace_what = {"mcl_flowers:poppy"},
|
replace_what = {"mcl_flowers:poppy"},
|
||||||
replace_with = {"air"},
|
replace_with = {"air"},
|
||||||
|
@ -89,7 +85,7 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
etime = etime + dtime
|
etime = etime + dtime
|
||||||
if etime > 10 then
|
if etime > 10 then
|
||||||
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
|
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
|
||||||
self:gopath(self._home)
|
mcl_mobs:gopath(self,self._home)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -97,7 +93,8 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:iron_golem", S("Iron Golem"), "#3b3b3b", "#f57223", 0)
|
mcl_mobs:register_egg("mobs_mc:iron_golem", S("Iron Golem"), "mobs_mc_spawn_icon_iron_golem.png", 0)
|
||||||
|
|
||||||
|
|
||||||
--[[ This is to be called when a pumpkin or jack'o lantern has been placed. Recommended: In the on_construct function of the node.
|
--[[ This is to be called when a pumpkin or jack'o lantern has been placed. Recommended: In the on_construct function of the node.
|
||||||
This summons an iron golen if placing the pumpkin created an iron golem summon pattern:
|
This summons an iron golen if placing the pumpkin created an iron golem summon pattern:
|
||||||
|
|
|
@ -24,52 +24,15 @@ local carpets = {
|
||||||
unicolor_light_blue = { "mcl_wool:light_blue_carpet", "light_blue" },
|
unicolor_light_blue = { "mcl_wool:light_blue_carpet", "light_blue" },
|
||||||
}
|
}
|
||||||
|
|
||||||
local function get_drops(self)
|
mcl_mobs:register_mob("mobs_mc:llama", {
|
||||||
self.drops = {}
|
|
||||||
table.insert(self.drops,
|
|
||||||
{name = "mcl_mobitems:leather",
|
|
||||||
chance = 1,
|
|
||||||
min = 0,
|
|
||||||
max = 2,
|
|
||||||
looting = "common",
|
|
||||||
})
|
|
||||||
if self.carpet then
|
|
||||||
table.insert(self.drops,{name = self.carpet,
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 1,})
|
|
||||||
end
|
|
||||||
if self._has_chest then
|
|
||||||
table.insert(self.drops,{name = "mcl_chests:chest",
|
|
||||||
chance = 1,
|
|
||||||
min = 1,
|
|
||||||
max = 1,})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mobs.register_mob("mobs_mc:llama", {
|
|
||||||
description = S("Llama"),
|
description = S("Llama"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
passive = false,
|
|
||||||
attack_type = "shoot",
|
|
||||||
shoot_interval = 5.5,
|
|
||||||
arrow = "mobs_mc:llamaspit",
|
|
||||||
shoot_offset = 1, --3.5 *would* be a good value visually but it somehow messes with the projectiles trajectory
|
|
||||||
spawn_in_group_min = 4,
|
|
||||||
spawn_in_group = 6,
|
|
||||||
|
|
||||||
head_swivel = "head.control",
|
|
||||||
bone_eye_height = 11,
|
|
||||||
head_eye_height = 3,
|
|
||||||
horrizonatal_head_height=0,
|
|
||||||
curiosity = 60,
|
|
||||||
head_yaw = "z",
|
|
||||||
|
|
||||||
hp_min = 15,
|
hp_min = 15,
|
||||||
hp_max = 30,
|
hp_max = 30,
|
||||||
xp_min = 1,
|
xp_min = 1,
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
|
passive = false,
|
||||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.86, 0.45},
|
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.86, 0.45},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_llama.b3d",
|
mesh = "mobs_mc_llama.b3d",
|
||||||
|
@ -80,8 +43,9 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
{"blank.png", "blank.png", "mobs_mc_llama_white.png"},
|
{"blank.png", "blank.png", "mobs_mc_llama_white.png"},
|
||||||
{"blank.png", "blank.png", "mobs_mc_llama.png"},
|
{"blank.png", "blank.png", "mobs_mc_llama.png"},
|
||||||
},
|
},
|
||||||
|
visual_size = {x=3, y=3},
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
runaway = false,
|
runaway = true,
|
||||||
walk_velocity = 1,
|
walk_velocity = 1,
|
||||||
run_velocity = 4.4,
|
run_velocity = 4.4,
|
||||||
follow_velocity = 4.4,
|
follow_velocity = 4.4,
|
||||||
|
@ -101,28 +65,36 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
},
|
},
|
||||||
animation = {
|
animation = {
|
||||||
stand_start = 0, stand_end = 0,
|
speed_normal = 24,
|
||||||
walk_start = 0, walk_end = 40, walk_speed = 35,
|
run_speed = 60,
|
||||||
run_start = 0, run_end = 40, run_speed = 50,
|
run_start = 0,
|
||||||
},
|
run_end = 40,
|
||||||
child_animations = {
|
stand_start = 0,
|
||||||
stand_start = 41, stand_end = 41,
|
stand_end = 0,
|
||||||
walk_start = 41, walk_end = 81, walk_speed = 50,
|
walk_start = 0,
|
||||||
run_start = 41, run_end = 81, run_speed = 75,
|
walk_end = 40,
|
||||||
|
hurt_start = 118,
|
||||||
|
hurt_end = 154,
|
||||||
|
death_start = 154,
|
||||||
|
death_end = 179,
|
||||||
|
eat_start = 49,
|
||||||
|
eat_end = 78,
|
||||||
|
look_start = 78,
|
||||||
|
look_end = 108,
|
||||||
},
|
},
|
||||||
follow = { "mcl_farming:wheat_item", "mcl_farming:hay_block" },
|
follow = { "mcl_farming:wheat_item", "mcl_farming:hay_block" },
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
-- set needed values if not already present
|
-- set needed values if not already present
|
||||||
if not self.v3 then
|
if not self.v2 then
|
||||||
self.v3 = 0
|
self.v2 = 0
|
||||||
self.max_speed_forward = 4
|
self.max_speed_forward = 4
|
||||||
self.max_speed_reverse = 2
|
self.max_speed_reverse = 2
|
||||||
self.accel = 4
|
self.accel = 4
|
||||||
self.terrain_type = 3
|
self.terrain_type = 3
|
||||||
self.driver_attach_at = {x = 0, y = 12.7, z = -5}
|
self.driver_attach_at = {x = 0, y = 4.17, z = -1.5}
|
||||||
self.driver_eye_offset = {x = 0, y = 6, z = 0}
|
self.driver_eye_offset = {x = 0, y = 3, z = 0}
|
||||||
self.driver_scale = {x = 1/self.visual_size.x, y = 1/self.visual_size.y}
|
self.driver_scale = {x = 1/self.visual_size.x, y = 1/self.visual_size.y}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -156,59 +128,62 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
local item = clicker:get_wielded_item()
|
local item = clicker:get_wielded_item()
|
||||||
if item:get_name() == "mcl_farming:hay_block" then
|
if item:get_name() == "mcl_farming:hay_block" then
|
||||||
-- Breed with hay bale
|
-- Breed with hay bale
|
||||||
if self:feed_tame(clicker, 1, true, false) then return end
|
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
||||||
elseif not self._has_chest and item:get_name() == "mcl_chests:chest" then
|
|
||||||
item:take_item()
|
|
||||||
clicker:set_wielded_item(item)
|
|
||||||
self._has_chest = true
|
|
||||||
self.base_texture = table.copy(self.base_texture)
|
|
||||||
self.base_texture[1] = self.base_texture[3]
|
|
||||||
self.object:set_properties({
|
|
||||||
textures = self.base_texture,
|
|
||||||
})
|
|
||||||
get_drops(self)
|
|
||||||
return
|
|
||||||
elseif self._has_chest and clicker:get_player_control().sneak then
|
|
||||||
mcl_entity_invs.show_inv_form(self,clicker," - Strength "..math.floor(self._inv_size / 3))
|
|
||||||
return
|
|
||||||
else
|
else
|
||||||
-- Feed with anything else
|
-- Feed with anything else
|
||||||
if self:feed_tame(clicker, 1, false, true) then return end
|
if mcl_mobs:feed_tame(self, clicker, 1, false, true) then return end
|
||||||
end
|
end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
-- Make sure tamed llama is mature and being clicked by owner only
|
-- Make sure tamed llama is mature and being clicked by owner only
|
||||||
if self.tamed and not self.child and self.owner == clicker:get_player_name() then
|
if self.tamed and not self.child and self.owner == clicker:get_player_name() then
|
||||||
-- Place carpet
|
|
||||||
if minetest.get_item_group(item:get_name(), "carpet") == 1 and not self.carpet then
|
-- Place carpet
|
||||||
for group, carpetdata in pairs(carpets) do
|
if minetest.get_item_group(item:get_name(), "carpet") == 1 and not self.carpet then
|
||||||
if minetest.get_item_group(item:get_name(), group) == 1 then
|
for group, carpetdata in pairs(carpets) do
|
||||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
if minetest.get_item_group(item:get_name(), group) == 1 then
|
||||||
item:take_item()
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
clicker:set_wielded_item(item)
|
item:take_item()
|
||||||
end
|
clicker:set_wielded_item(item)
|
||||||
local substr = carpetdata[2]
|
|
||||||
local tex_carpet = "mobs_mc_llama_decor_"..substr..".png"
|
|
||||||
self.base_texture = table.copy(self.base_texture)
|
|
||||||
self.base_texture[2] = tex_carpet
|
|
||||||
self.object:set_properties({
|
|
||||||
textures = self.base_texture,
|
|
||||||
})
|
|
||||||
self.carpet = item:get_name()
|
|
||||||
get_drops(self)
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
local substr = carpetdata[2]
|
||||||
|
local tex_carpet = "mobs_mc_llama_decor_"..substr..".png"
|
||||||
|
self.base_texture = table.copy(self.base_texture)
|
||||||
|
self.base_texture[2] = tex_carpet
|
||||||
|
self.object:set_properties({
|
||||||
|
textures = self.base_texture,
|
||||||
|
})
|
||||||
|
self.carpet = item:get_name()
|
||||||
|
self.drops = {
|
||||||
|
{name = "mcl_mobitems:leather",
|
||||||
|
chance = 1,
|
||||||
|
min = 0,
|
||||||
|
max = 2,},
|
||||||
|
{name = item:get_name(),
|
||||||
|
chance = 1,
|
||||||
|
min = 1,
|
||||||
|
max = 1,},
|
||||||
|
}
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- detatch player already riding llama
|
-- detatch player already riding llama
|
||||||
if self.driver and clicker == self.driver then
|
if self.driver and clicker == self.driver then
|
||||||
mcl_mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
|
||||||
-- attach player to llama
|
mcl_mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||||
elseif not self.driver then
|
|
||||||
self.object:set_properties({stepheight = 1.1})
|
-- attach player to llama
|
||||||
mcl_mobs.attach(self, clicker)
|
elseif not self.driver then
|
||||||
end
|
|
||||||
|
self.object:set_properties({stepheight = 1.1})
|
||||||
|
mcl_mobs.attach(self, clicker)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Used to capture llama
|
||||||
|
elseif not self.driver and clicker:get_wielded_item():get_name() ~= "" then
|
||||||
|
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -221,7 +196,7 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
else
|
else
|
||||||
parent = parent2
|
parent = parent2
|
||||||
end
|
end
|
||||||
child = mcl_mobs.spawn_child(pos, parent.name)
|
child = mcl_mobs:spawn_child(pos, parent.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
ent_c.base_texture = table.copy(ent_c.base_texture)
|
ent_c.base_texture = table.copy(ent_c.base_texture)
|
||||||
|
@ -233,44 +208,7 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_spawn = function(self)
|
|
||||||
if not self._inv_size then
|
|
||||||
local r = math.random(1000)
|
|
||||||
if r < 80 then
|
|
||||||
self._inv_size = 15
|
|
||||||
elseif r < 160 then
|
|
||||||
self._inv_size = 12
|
|
||||||
elseif r < 488 then
|
|
||||||
self._inv_size = 9
|
|
||||||
elseif r < 816 then
|
|
||||||
self._inv_size = 6
|
|
||||||
else
|
|
||||||
self._inv_size = 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
mcl_entity_invs.register_inv("mobs_mc:llama","Llama",nil,true)
|
|
||||||
|
|
||||||
-- spit arrow (weapon)
|
|
||||||
mcl_mobs.register_arrow("mobs_mc:llamaspit", {
|
|
||||||
visual = "sprite",
|
|
||||||
visual_size = {x = 0.10, y = 0.10},
|
|
||||||
textures = {"mobs_mc_llama_spit.png"},
|
|
||||||
velocity = 5,
|
|
||||||
hit_player = function(self, player)
|
|
||||||
player:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = 1},
|
|
||||||
}, nil)
|
|
||||||
end,
|
|
||||||
|
|
||||||
hit_mob = function(self, mob)
|
|
||||||
end,
|
|
||||||
|
|
||||||
hit_node = function(self, pos, node)
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
|
|
||||||
--spawn
|
--spawn
|
||||||
|
@ -279,15 +217,19 @@ mcl_mobs:spawn_specific(
|
||||||
"overworld",
|
"overworld",
|
||||||
"ground",
|
"ground",
|
||||||
{
|
{
|
||||||
"Savanna",
|
"Mesa",
|
||||||
"SavannaM",
|
"MesaPlateauFM_grasstop",
|
||||||
"SavannaM_beach",
|
"MesaPlateauF",
|
||||||
"Savanna_beach",
|
"MesaPlateauFM",
|
||||||
"Savanna_ocean",
|
"MesaPlateauF_grasstop",
|
||||||
"ExtremeHills",
|
"MesaBryce",
|
||||||
"ExtremeHills_beach",
|
"Jungle",
|
||||||
"ExtremeHillsM",
|
"Jungle_shore",
|
||||||
}, --FIXME: Needs Windswept Forest when that is added.
|
"JungleM",
|
||||||
|
"JungleM_shore",
|
||||||
|
"JungleEdge",
|
||||||
|
"JungleEdgeM",
|
||||||
|
},
|
||||||
0,
|
0,
|
||||||
minetest.LIGHT_MAX+1,
|
minetest.LIGHT_MAX+1,
|
||||||
30,
|
30,
|
||||||
|
@ -297,4 +239,4 @@ mobs_mc.water_level+15,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs.register_egg("mobs_mc:llama", S("Llama"), "#c09e7d", "#995f40", 0)
|
mcl_mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)
|
||||||
|
|
|
@ -1,58 +1,64 @@
|
||||||
# textdomain: mobs_mc
|
# textdomain: mobs_mc
|
||||||
|
Agent=Agente
|
||||||
Bat=Murciélago
|
Bat=Murciélago
|
||||||
Blaze=Blaze
|
Blaze=Blaze
|
||||||
Chicken=Pollo
|
Chicken=Pollo
|
||||||
Cod=Bacalao
|
|
||||||
Cow=Vaca
|
Cow=Vaca
|
||||||
Mooshroom=Champivaca
|
Mooshroom=Champiñaca
|
||||||
Creeper=Creeper
|
Creeper=Creeper
|
||||||
Dolphin=Delfín
|
Ender Dragon=Enderdragón
|
||||||
Ender Dragon=Ender Dragon
|
|
||||||
Enderman=Enderman
|
Enderman=Enderman
|
||||||
Endermite=Endermite
|
Endermite=Endermite
|
||||||
Ghast=Ghast
|
Ghast=Ghast
|
||||||
|
Elder Guardian=Gran guardián
|
||||||
Guardian=Guardián
|
Guardian=Guardián
|
||||||
Elder Guardian=Guardián Anciano
|
|
||||||
Donkey=Burro
|
|
||||||
Horse=Caballo
|
Horse=Caballo
|
||||||
|
Skeleton Horse=Caballo esquelético
|
||||||
|
Zombie Horse=Caballo zombie
|
||||||
|
Donkey=Burro
|
||||||
Mule=Mula
|
Mule=Mula
|
||||||
Skeleton Horse=Caballo esqueleto
|
|
||||||
Zombie Horse=Caballo zombi
|
|
||||||
Iron Golem=Golem de hierro
|
Iron Golem=Golem de hierro
|
||||||
Llama=Llama
|
Llama=Llama
|
||||||
Cat=Gato
|
|
||||||
Ocelot=Ocelote
|
Ocelot=Ocelote
|
||||||
Parrot=Loro
|
Parrot=Loro
|
||||||
Pig=Cerdo
|
Pig=Cerdo
|
||||||
Polar Bear=Oso polar
|
Polar Bear=Oso polar
|
||||||
Killer Bunny=Conejo asesino
|
|
||||||
Rabbit=Conejo
|
Rabbit=Conejo
|
||||||
Salmon=Salmón
|
Killer Bunny=Conejo asesino
|
||||||
Sheep=Oveja
|
Sheep=Oveja
|
||||||
Shulker=Shulker
|
Shulker=Shulker
|
||||||
Silverfish=Lepisma
|
Silverfish=Lepisma
|
||||||
Skeleton=Esqueleto
|
Skeleton=Esqueleto
|
||||||
Stray=Esqueleto glacial
|
Stray=Esqueleto
|
||||||
Wither Skeleton=Esqueleto del Wither
|
Wither Skeleton=Esqueleto wither
|
||||||
Magma Cube=Cubo de Magma
|
Magma Cube=Cubo de Magma
|
||||||
Slime=Slime
|
Slime=Slime
|
||||||
Snow Golem=Golem de nieve
|
Snow Golem=Golem de nieve
|
||||||
Cave Spider=Araña de las cuevas
|
|
||||||
Spider=Araña
|
Spider=Araña
|
||||||
|
Cave Spider=Araña de las cuevas
|
||||||
Squid=Calamar
|
Squid=Calamar
|
||||||
Vex=Ánima
|
Vex=Ánima
|
||||||
Master=Maestro
|
|
||||||
Villager=Aldeano
|
|
||||||
Evoker=Invocador
|
Evoker=Invocador
|
||||||
Illusioner=Illusionista
|
Illusioner=Illusionista
|
||||||
|
Villager=Aldeano
|
||||||
Vindicator=Vindicador
|
Vindicator=Vindicador
|
||||||
Zombie Villager=Aldeano zombi
|
Zombie Villager=Aldeano zombie
|
||||||
Witch=Bruja
|
Witch=Bruja
|
||||||
Wither=Wither
|
Wither=Wither
|
||||||
Wolf=Lobo
|
Wolf=Lobo
|
||||||
Baby Husk=Bebé Zombi Momificado
|
Husk=Husk
|
||||||
Baby Zombie=Bebé Zombi
|
Zombie=Zombie
|
||||||
Husk=Zombi Momificado
|
Zombie Pigman=Cerdo Zombie
|
||||||
Zombie=Zombi
|
Farmer=Granjero
|
||||||
Baby Zombie Pigman=Bebé Hombrecerdo Zombi
|
Fisherman=Pescador
|
||||||
Zombie Pigman=Hombrecerdo Zombi
|
Fletcher=Flechador
|
||||||
|
Shepherd=Sacerdote
|
||||||
|
Librarian=Bibliotecario
|
||||||
|
Cartographer=Cartógrafo
|
||||||
|
Armorer=Armero
|
||||||
|
Leatherworker=Peletero
|
||||||
|
Butcher=Carnicero
|
||||||
|
Weapon Smith=Herrero de Armas
|
||||||
|
Tool Smith=Herrero de Herramientas
|
||||||
|
Cleric=Sacerdote
|
||||||
|
Nitwit=Simple
|
||||||
|
|
|
@ -62,8 +62,3 @@ Weapon Smith=Fabriquant d'arme
|
||||||
Tool Smith=Fabriquant d'outil
|
Tool Smith=Fabriquant d'outil
|
||||||
Cleric=Clerc
|
Cleric=Clerc
|
||||||
Nitwit=Crétin
|
Nitwit=Crétin
|
||||||
Cod=Morue
|
|
||||||
Salmon=Saumon
|
|
||||||
Dolphin=Dauphin
|
|
||||||
Pillager=Pilleur
|
|
||||||
Tropical fish=Poisson tropical
|
|
|
@ -1,70 +0,0 @@
|
||||||
# textdomain: mobs_mc
|
|
||||||
Agent=エージェント
|
|
||||||
Axolotl=ウーパールーパー
|
|
||||||
Bat=コウモリ
|
|
||||||
Blaze=ブレイズ
|
|
||||||
Chicken=ニワトリ
|
|
||||||
Cow=ウシ
|
|
||||||
Mooshroom=ムーシュルーム
|
|
||||||
Creeper=クリーパー
|
|
||||||
Ender Dragon=エンダードラゴン
|
|
||||||
Enderman=エンダーマン
|
|
||||||
Endermite=エンダーマイト
|
|
||||||
Ghast=ガスト
|
|
||||||
Elder Guardian=エルダーガーディアン
|
|
||||||
Guardian=ガーディアン
|
|
||||||
Horse=ウマ
|
|
||||||
Skeleton Horse=スケルトンホース
|
|
||||||
Zombie Horse=ゾンビホース
|
|
||||||
Donkey=ロバ
|
|
||||||
Mule=ラバ
|
|
||||||
Iron Golem=アイアンゴーレム
|
|
||||||
Llama=ラマ
|
|
||||||
Ocelot=ヤマネコ
|
|
||||||
Parrot=オウム
|
|
||||||
Pig=ブタ
|
|
||||||
Polar Bear=シロクマ
|
|
||||||
Rabbit=ウサギ
|
|
||||||
Killer Bunny=殺人ウサギ
|
|
||||||
Sheep=ヒツジ
|
|
||||||
Shulker=シュルカー
|
|
||||||
Silverfish=シルバーフィッシュ
|
|
||||||
Skeleton=スケルトン
|
|
||||||
Stray=ストレイ
|
|
||||||
Wither Skeleton=ウィザースケルトン
|
|
||||||
Magma Cube=マグマキューブ
|
|
||||||
Slime=スライム
|
|
||||||
Snow Golem=スノーゴーレム
|
|
||||||
Spider=クモ
|
|
||||||
Cave Spider=洞窟グモ
|
|
||||||
Squid=イカ
|
|
||||||
Vex=ヴェックス
|
|
||||||
Evoker=エヴォーカー
|
|
||||||
Illusioner=イリュージョナー
|
|
||||||
Villager=村人
|
|
||||||
Vindicator=ヴィンディケーター
|
|
||||||
Zombie Villager=村人ゾンビ
|
|
||||||
Witch=魔女
|
|
||||||
Wither=ウィザー
|
|
||||||
Wolf=オオカミ
|
|
||||||
Husk=ハスク
|
|
||||||
Zombie=ゾンビ
|
|
||||||
Zombie Pigman=ゾンビピッグマン
|
|
||||||
Farmer=農民
|
|
||||||
Fisherman=漁師
|
|
||||||
Fletcher=矢師
|
|
||||||
Shepherd=羊飼い
|
|
||||||
Librarian=司書
|
|
||||||
Cartographer=製図家
|
|
||||||
Armorer=防具鍛冶
|
|
||||||
Leatherworker=革職人
|
|
||||||
Butcher=肉屋
|
|
||||||
Weapon Smith=武器鍛冶
|
|
||||||
Tool Smith=道具鍛冶
|
|
||||||
Cleric=聖職者
|
|
||||||
Nitwit=求職者
|
|
||||||
Cod=タラ
|
|
||||||
Salmon=サケ
|
|
||||||
Dolphin=イルカ
|
|
||||||
Pillager=ピリジャー
|
|
||||||
Tropical fish=クマノミ
|
|
|
@ -1,6 +1,5 @@
|
||||||
# textdomain: mobs_mc
|
# textdomain: mobs_mc
|
||||||
Agent=
|
Agent=
|
||||||
Axolotl=
|
|
||||||
Bat=
|
Bat=
|
||||||
Blaze=
|
Blaze=
|
||||||
Chicken=
|
Chicken=
|
||||||
|
@ -63,8 +62,3 @@ Weapon Smith=
|
||||||
Tool Smith=
|
Tool Smith=
|
||||||
Cleric=
|
Cleric=
|
||||||
Nitwit=
|
Nitwit=
|
||||||
Cod=
|
|
||||||
Salmon=
|
|
||||||
Dolphin=
|
|
||||||
Pillager=
|
|
||||||
Tropical fish=
|
|
|
@ -1,5 +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_core, mcl_util
|
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core
|
||||||
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
|
||||||
|
|
||||||
|
|