Compare commits

..

15 Commits

428 changed files with 5378 additions and 8997 deletions

4
API.md
View File

@ -17,10 +17,6 @@ Items can have these fields:
anvil.
See `mcl_banners` for an example.
Tools can have these fields:
* `_mcl_diggroups`: Specifies the digging groups that a tool can dig and how
efficiently. See `_mcl_autogroup` for more information.
All nodes can have these fields:
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0

View File

@ -7,11 +7,13 @@ But first, some things to note:
MineClone 2's development target is to make a free software clone of Minecraft,
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
MineClone 2 is maintained by three persons. Namely, kay27, EliasFleckenstein and jordan4ibanez. You can find us
in the Minetest forums (forums.minetest.net), in IRC in the #mineclone2
MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
in the Minetest forums (forums.minetest.net), in IRC in the #minetest
channel on irc.freenode.net. And finally, you can send e-mails to
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
There is **no** guarantee we will accept anything from anybody.
By sending us patches or asking us to include your changes in this game,
you agree that they fall under the terms of the LGPLv2.1, which basically
means they will become part of a free software.
@ -24,7 +26,8 @@ For small and medium changes:
* Fork the repository
* Do your change in a new branch
* Create a pull request to get your changes merged into master
* Upload the repository somewhere where it can be accessed from the Internet and
notify us
For small changes, sending us a patch is also good.
@ -38,30 +41,40 @@ reserve the right to revert everything that we don't like.
For bigger changes, we strongly recommend to use feature branches and
discuss with me first.
If your code causes bugs and crashes, it is your responsibility to fix them as soon as possible.
Contributors will be credited in `README.md`.
We mostly use plain merging rather than rebasing or squash merging.
## Quality remarks
Again: There is ***no*** guarantee we will accept anything from anybody.
But we will gladly take in code from others when we feel it saves us work
in the long run.
Your commit names should be relatively descriptive, e.g. when saying "Fix #issueid", the commit message should also contain the title of the issue.
### Inclusion criteria
Depending on what you add, the chances for inclusion vary:
Contributors will be credited in `CREDITS.md`.
### High chance for inclusion
* Gameplay features in Minecraft which are missing in MineClone 2
## Features > 1.12
### Medium chance for inclusion (discuss first)
* Features which don't a impact on gameplay
* GUI improvement
* Features from pocket or console edition
If you want to make a feature that was added in a Minecraft version later than 1.12, you should fork MineClone5 (mineclone5 branch in the repository) and add your changes to this.
### Low chance for inclusion (discuss/optimize first)
* Overhaul of architecture / mod structure
* Mass-itemstring changes all over the place
* Added files have a unusual high file size
* Indentation looks like crazy
* Single commits which add several unrelated things
* Gameplay features which don't exist in Minecraft
## What we accept
### Instant rejection
* Proprietary **anything**
* Code contains `minetest.env` anywhere
* Every MC features up to version 1.12 JE.
* Every already finished and working good features from versions above (only when making a MineClone5 PR / Contribution).
* Except features which couldn't be done easily and bugfree because of Minetest engine limitations. Eg. we CAN extend world boundaries by playing with map chunks, just teleporting player onto next layer after 31000 , but it would cost too much (time, code, bugs, performance, stability, etc).
* Some features, approved by the rest of the community, I mean maybe some voting and really missing any negative feedback.
## What we reject
* Any features which cause critical bugs, sending them to rework/fix or trying to fix immediately.
* Some small portions of big entirely missing features which just definitely break gamplay balance give nothing useful
* Controversial features, which some people support while others do not should be discussed well, with publishing forum announcements, at least during the week. In case if there are still doubts - send them into the mod.
## Coding style guide
* Indentations should reflect the code flow
* Use tabs, not spaces for indentation (tab size = 8)
* Never use `minetest.env`
## Reporting bugs
Report all bugs and missing Minecraft features here:

View File

@ -1,117 +0,0 @@
# Credits
## Creator of MineClone
* davedevils
## Creator of MineClone2
* Wuzzy
## Maintainers
* Fleckenstein
* kay27
* oilboi
## Developers
* bzoss
* AFCMS
* epCode
* ryvnf
* iliekprogrammar
* MysticTempest
* Rootyjr
* Nicu
* aligator
* Code-Sploit
## Contributors
* Laurent Rocher
* HimbeerserverDE
* TechDudie
* Alexander Minges
* ArTee3
* ZeDique la Ruleta
* pitchum
* wuniversales
* Bu-Gee
* David McMackins II
* Nicholas Niro
* Wouters Dorian
* Blue Blancmange
* Jared Moody
* Li0n
* Midgard
* NO11
* Saku Laesvuori
* Yukitty
* ZedekThePD
* aldum
* dBeans
* nickolas360
* yutyo
* ztianyang
* j45
## MineClone5
* kay27
* Debiankaios
* epCode
* NO11
* j45
## Original Mod Authors
* Wuzzy
* Fleckenstein
* BlockMen
* TenPlus1
* PilzAdam
* ryvnf
* stujones11
* Arcelmi
* celeron55
* maikerumine
* GunshipPenguin
* Qwertymine3
* Rochambeau
* rubenwardy
* stu
* oilboi
* 4aiman
* Kahrl
* Krock
* UgnilJoZ
* lordfingle
* 22i
* bzoss
* kilbith
* xeranas
* kddekadenz
* sofar
* 4Evergreen4
* jordan4ibanez
* paramat
## 3D Models
* 22i
* tobyplowy
* epCode
## Textures
* XSSheep
* Wuzzy
* kingoscargames
* leorockway
* xMrVizzy
* yutyo
## Translations
* Wuzzy
* Rocher Laurent
* wuniversales
* kay27
* pitchum
## Special thanks
* celeron55 for creating Minetest
* Jordach for the jukebox music compilation from Big Freaking Dig
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
* Notch and Jeb for being the major forces behind Minecraft

View File

@ -1,52 +0,0 @@
# Legal information
This is a fan game, not developed or endorsed by Mojang AB.
Copying is an act of love. Please copy and share! <3
Here's the detailed legalese for those who need it:
## License of source code
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
is an imitation of Minecraft.
MineClone 2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (in the LICENSE.txt file) for more
details.
In the mods you might find in the read-me or license
text files a different license. This counts as dual-licensing.
You can choose which license applies to you: Either the
license of MineClone 2 (GNU GPLv3) or the mod's license.
MineClone 2 is a direct continuation of the discontinued MineClone
project by davedevils.
Mod credits:
See `README.txt` or `README.md` in each mod directory for information about other authors.
For mods that do not have such a file, the license is the source code license
of MineClone 2 and the author is Wuzzy.
## License of media (textures and sounds)
No non-free licenses are used anywhere.
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
from scratch.
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
All other files, unless mentioned otherwise, fall under:
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
See README.txt in each mod directory for detailed information about other authors.

View File

@ -0,0 +1,40 @@
# Missing features in Minetest to recreate Minecraft features
A side goal of the MineClone 2 project is to find any shortcomings of Minetest which make it impossible to recreate a Minecraft feature exactly.
This file lists some of the missing features in Minetest which MineClone 2 would require.
## No workaround possible
For these features, no easy Lua workaround could be found.
### Lua API
#### Tools/wielded item
- “Lock” hotbar for a brief time after using an item, making it impossible to switch item or to attach/mine/build until the delay is over (For eating with delay)
- Tool charging: Holding down the mouse and releasing it, applying a “power level” (For bow and arrows, more charge = higher arrow range) ([issue 5212](https://github.com/minetest/minetest/issues/5212))
- [Dual Wielding](http://minecraft.gamepedia.com/Dual_wield)
- Eating/drinking animation ([issue 2811](https://github.com/minetest/minetest/issues/2811))
#### Nodes
- Light level 15 for nodes (not sunlight)
- Nodes makes light level drop by 2 or or more per node ([issue 5209](https://github.com/minetest/minetest/issues/5209))
## Interface
- Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster
- Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211))
- Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots
## Workaround theoretically possible
For these features, a workaround (or hack ;-)) by using Lua is theoretically possible. But engine support would be clearly better, more performant, more reliable, etc.
### Lua API
#### Nodes
- Change walking speed on block (soul sand)
- Change jumping height on block (soul sand),
- Change object movement speed *through* a block, but for non-liquids (for cobweb)
- Add `on_walk_over` event
- Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second
- Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second
- Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests
#### Nice-to-haye
- Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos

194
README.md
View File

@ -2,7 +2,7 @@
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
Developed by many people. Not developed or endorsed by Mojang AB.
Version: 0.72.0 (in development)
Version: 0.71.0
### Gameplay
You start in a randomly-generated world made entirely of cubes. You can explore
@ -65,8 +65,16 @@ map builders. They can not be obtained in-game or in the creative inventory.
Use the `/giveme` chat command to obtain them. See the in-game help for
an explanation.
#### Incomplete items
These items do not work yet, but you can get them with `/giveme` for testing:
* Minecart with Chest: `mcl_minecarts:chest_minecart`
* Minecart with Furnace: `mcl_minecarts:furnace_minecart`
* Minecart with Hopper: `mcl_minecarts:hopper_minecart`
* Minecart with Command Block: `mcl_minecarts:command_block_minecart`
## Installation
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
This game requires [Minetest](http://minetest.net) to run (version 5.0.0 or
later). So you need to install Minetest first. Only stable versions of Minetest
are officially supported.
There is no support for running MineClone 2 in development versions of Minetest.
@ -75,37 +83,23 @@ To install MineClone 2 (if you haven't already), move this directory into the
“games” directory of your Minetest data directory. Consult the help of
Minetest to learn more.
## Reporting bugs
Please report all bugs and missing Minecraft features here:
<https://git.minetest.land/MineClone2/MineClone2/issues>
## Chating with the community
Join our discord server at:
<https://discord.gg/84GKcxczG3>
## Project description
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
* MineClone2 also includes Optifine features supported by the Minetest
* In general, Minecraft is aimed to be cloned as good as possible
* Features of later Minecraft versions might sneak in, but they have a low priority
* In general, Minecraft is aimed to be cloned as good as Minetest currently permits (no hacks)
* Cloning the gameplay has highest priority
* MineClone 2 will use different assets, but with a similar style
* Limitations found in Minetest will be documented in the course of development
* Features of later Minecraft versions are collected in the mineclone5 branch
## Using features from newer versions of Minecraft
For > 1.12 features, checkout MineClone5. It includes features from newer Minecraft versions.
Download it here: https://git.minetest.land/MineClone2/MineClone2/src/branch/mineclone5
* MineClone 2 will use different graphics and sounds, but with a similar style
* Cloning the interface has no priority. It will only be roughly imitated
* Limitations found in Minetest will be written down and reported in the course of development
## Completion status
This game is currently in **beta** stage.
It is playable, but not yet feature-complete.
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
If you want to use the git version of MineClone2 in production, consider using the production branch.
It is updated weekly and contains relatively stable code for servers.
This game is currently in **alpha** stage.
It is playable, but unfinished, many bugs are to be expected.
Backwards-compability is *not* guaranteed, updating your world might cause small and
big bugs (such as “missing node” errors or even crashes).
The following main features are available:
@ -134,7 +128,7 @@ The following main features are available:
* Clock
* Compass
* Sponge
* Slime block
* Slime block (does not interact with redstone)
* Small plants and saplings
* Dyes
* Banners
@ -146,19 +140,19 @@ The following main features are available:
* Creative inventory
* Farming
* Writable books
* Commands
* Villages
* The End
* A few server commands
* And more!
The following features are incomplete:
* Generated structures (especially villages)
* Some monsters and animals
* Redstone-related things
* The End
* Special minecarts
* A couple of non-trivial blocks and items
Bonus features (not found in Minecraft 1.12):
Bonus features (not found in Minecraft 1.11):
* Built-in crafting guide which shows you crafting and smelting recipes
* In-game help system containing extensive help about gameplay basics, blocks, items and more
@ -183,14 +177,148 @@ Technical differences from Minecraft:
* Different textures (Pixel Perfection)
* Different sounds (various sources)
* Different engine (Minetest)
* Different easter eggs
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
## Reporting bugs
Please report all bugs and missing Minecraft features here:
<https://git.minetest.land/MineClone2/MineClone2/issues>
## Chating with the community
Join our discord server at:
<https://discord.gg/84GKcxczG3>
## Other readme files
* `LICENSE.txt`: The GPLv3 license text
* `CONTRIBUTING.md`: Information for those who want to contribute
* `MISSING_ENGINE_FEATURES.md`: List of missing features in Minetest which MineClone 2 would need for improvement
* `API.md`: For Minetest modders who want to mod this game
* `LEGAL.md`: Legal information
* `CREDITS.md`: List of everyone who contributed
## Credits
There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game.
### Coding
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired)
* davedevils: Creator of MineClone on which MineClone 2 is based on
* [ex-bart](https://github.com/ex-bart): Redstone comparators
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes
* [aligator](https://github.com/aligator): Improvement of doors
* [ryvnf](https://github.com/ryvnf): Explosion mechanics
* MysticTempest: Bugfixes
* [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand
* kay27 <kay27@bk.ru>: Experience system, bugfixes, optimizations (Current maintainer)
* [EliasFleckenstein03](https://github.com/EliasFleckenstein03): End crystals, enchanting, burning mobs / players, animated chests, bugfixes (Current maintainer)
* epCode: Better player animations, new logo
* 2mac: Fix bug with powered rail
* Lots of other people: TO BE WRITTEN (see mod directories for details)
#### Mod credits (summary)
* `controls`: Arcelmi
* `flowlib`: Qwertymine13
* `walkover`: lordfingle
* `drippingwater`: kddekadenz
* `mobs_mc`: maikerumine, 22i and others
* `awards`: rubenwardy
* `screwdriver`: RealBadAngel, Maciej Kastakin, Minetest contributors
* `xpanes`: Minetest contributors
* `mesecons` mods: Jeija and contributors
* `wieldview`: Stuart Jones
* `mcl_meshhand`: Based on `newhand` by jordan4ibanez
* `mcl_mobs`: Based on Mobs Redo [`mobs`] by TenPlus1 and contributors
* Most other mods: Wuzzy
Detailed credits for each mod can be found in the individual mod directories.
### Graphics
* [XSSheep](http://www.minecraftforum.net/members/XSSheep): Main author; creator of the Pixel Perfection resource pack of Minecraft 1.11
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main menu imagery and various edits and additions of texture pack
* [kingoscargames](https://github.com/kingoscargames): Various edits and additions of existing textures
* [leorockway](https://github.com/leorockway): Some edits of mob textures
* [xMrVizzy](https://minecraft.curseforge.com/members/xMrVizzy): Glazed terracotta (textures are subject to be replaced later)
* yutyo <tanakinci2002@gmail.com>: MineClone 2 logo
* Other authors: GUI images
### Translations
* Wuzzy: German
* Rocher Laurent <rocherl@club-internet.fr>: French
* wuniversales: Spanish
* kay27 <kay27@bk.ru>: Russian
### Models
* [22i](https://github.com/22i): Creator of all models
* [tobyplowy](https://github.com/tobyplowy): UV-mapping fixes to said models
### Sounds and music
Various sources. See the respective mod directories for details.
### Special thanks
* davedevils for starting MineClone, the original version of this game
* Wuzzy for starting and maintaining MineClone2 for several years
* celeron55 for creating Minetest
* Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2
* Jordach for the jukebox music compilation from Big Freaking Dig
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
* Notch and Jeb for being the major forces behind Minecraft
* XSSheep for creating the Pixel Perfection resource pack
* [22i](https://github.com/22i) for providing great models and support
* [maikerumine](http://github.com/maikerumine) for kicking off mobs and biomes
## Info for programmers
You find interesting and useful infos in `API.md`.
## Legal information
This is a fan game, not developed or endorsed by Mojang AB.
Copying is an act of love. Please copy and share! <3
Here's the detailed legalese for those who need it:
### License of source code
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
is an imitation of Minecraft.
MineClone 2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (in the LICENSE.txt file) for more
details.
In the mods you might find in the read-me or license
text files a different license. This counts as dual-licensing.
You can choose which license applies to you: Either the
license of MineClone 2 (GNU GPLv3) or the mod's license.
MineClone 2 is a direct continuation of the discontinued MineClone
project by davedevils.
Mod credits:
See `README.txt` or `README.md` in each mod directory for information about other authors.
For mods that do not have such a file, the license is the source code license
of MineClone 2 and the author is Wuzzy.
### License of media (textures and sounds)
No non-free licenses are used anywhere.
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
from scratch.
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
All other files, unless mentioned otherwise, fall under:
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
See README.txt in each mod directory for detailed information about other authors.

View File

@ -1,8 +1,5 @@
# This is a game specific minetest.conf file, do not edit
# If any of these settings are set in your minetest.conf file in ~/.minetest (Linux) or in the root directory of the game (Run in place/Windows)
# They will override these settings
# Basic game rules
time_speed = 72
@ -36,10 +33,10 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
keepInventory = false
# Performance settings
dedicated_server_step = 0.05 #tick rate
# abm_interval = 0.25
# max_objects_per_block = 4096
# max_packets_per_iteration = 10096
dedicated_server_step = 0.001
abm_interval = 0.25
max_objects_per_block = 4096
max_packets_per_iteration = 10096
# Clientmodding to support official client
enable_client_modding = true

View File

@ -4,11 +4,6 @@ Specifically, this mod has 2 purposes:
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
2) Generating digging time group for all nodes based on node metadata (it's complicated)
This mod also requires another mod called “mcl_autogroup” to function properly.
“mcl_autogroup” exposes the API used to register digging groups, while this mod
uses those digging groups to set the digging time groups for all the nodes and
tools.
See init.lua for more infos.
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.

View File

@ -1,361 +1,169 @@
--[[
This mod implements a HACK to make 100% sure the digging times of all tools
match Minecraft's perfectly. The digging times system of Minetest is very
different, so this weird group trickery has to be used. In Minecraft, each
block has a hardness and the actual Minecraft digging time is determined by
this:
--[[ Mining times. Yeah, mining times … Alright, this is going to be FUN!
This mod does include a HACK to make 100% sure the digging times of all tools match Minecraft's perfectly.
The digging times system of Minetest is very different, so this weird group trickery has to be used.
In Minecraft, each block has a hardness and the actual Minecraft digging time is determined by this:
1) The block's hardness
2) The tool being used (the tool speed and its efficiency level)
3) Whether the tool is considered as "eligible" for the block
2) The tool being used
3) Whether the tool is considered as eligible for the block
(e.g. only diamond pick eligible for obsidian)
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more information.
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more
information.
In MineClone 2, all diggable node have the hardness set in the custom field _mcl_hardness (0 by default).
The nodes are also required to specify the eligible tools in groups like pickaxey, shovely, etc.
This mod then calculates the real digging time based on the node meta data. The real digging times
are then added into mcl_autogroup.digtimes where the table indices are group rating and the values are the
digging times in seconds. These digging times can be then added verbatim into the tool definitions.
How the mod is used
===================
Example:
mcl_autogroup.digtimes.pickaxey_dig_diamond[1] = 0.2
In MineClone 2, all diggable nodes have the hardness set in the custom field
"_mcl_hardness" (0 by default). These values are used together with digging
groups by this mod to create the correct digging times for nodes. Digging
groups are registered using the following code:
This means that when a node has been assigned the group pickaxey_dig_diamond=1, it can be dug by the
diamond pickaxe in 0.2 seconds.
mcl_autogroup.register_diggroup("shovely")
mcl_autogroup.register_diggroup("pickaxey", {
levels = { "wood", "gold", "stone", "iron", "diamond" }
})
The first line registers a simple digging group. The second line registers a
digging group with 5 different levels (in this case one for each material of a
pickaxes).
Nodes indicate that they belong to a particular digging group by being member of
the digging group in their node definition. "mcl_core:dirt" for example has
shovely=1 in its groups. If the digging group has multiple levels the value of
the group indicates which digging level the node requires.
"mcl_core:stone_with_gold" for example has pickaxey=4 because it requires a
pickaxe of level 4 be mined.
This strange setup with mcl_autogroup has been done to minimize the amount of required digging times
a single tool needs to use. If this is not being done, the loading time will increase considerably
(>10s).
For tools to be able to dig nodes of digging groups they need to use the have
the custom field "_mcl_diggroups" function to get the groupcaps. The value of
this field is a table which defines which groups the tool can dig and how
efficiently.
]]
_mcl_diggroups = {
handy = { speed = 1, level = 1, uses = 0 },
pickaxey = { speed = 1, level = 0, uses = 0 },
}
local materials = { "wood", "gold", "stone", "iron", "diamond" }
local basegroups = { "pickaxey", "axey", "shovely" }
local minigroups = { "handy", "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
local divisors = {
["wood"] = 2,
["gold"] = 12,
["stone"] = 4,
["iron"] = 6,
["diamond"] = 8,
["handy"] = 1,
["shearsy"] = 15,
["swordy"] = 1.5,
["shearsy_wool"] = 5,
["swordy_cobweb"] = 15,
}
local max_efficiency_level = 5
The "uses" field indicate how many uses (0 for infinite) a tool has when used on
the specified digging group. The "speed" field is a multiplier to the dig speed
on that digging group.
mcl_autogroup = {}
mcl_autogroup.digtimes = {}
mcl_autogroup.creativetimes = {} -- Copy of digtimes, except that all values are 0. Used for creative mode
The "level" field indicates which levels of the group the tool can harvest. A
level of 0 means that the tool cannot harvest blocks of that node. A level of 1
or above means that the tool can harvest nodes with that level or below. See
"mcl_tools/init.lua" for examples on how "_mcl_diggroups" is used in practice.
Information about the mod
=========================
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
mcl_autogroup contains the API functions used to register custom digging groups.
_mcl_autogroup contains most of the code. The leading underscore in the name
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
as possible. Minetest loads mods in reverse alphabetical order.
This also means that it is very important that no mod adds _mcl_autogroup as a
dependency.
--]]
assert(minetest.get_modpath("mcl_autogroup"), "This mod requires the mod mcl_autogroup to function")
-- Returns a table containing the unique "_mcl_hardness" for nodes belonging to
-- each diggroup.
local function get_hardness_values_for_groups()
local maps = {}
local values = {}
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
maps[g] = {}
values[g] = {}
end
for _, ndef in pairs(minetest.registered_nodes) do
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
if ndef.groups[g] ~= nil then
maps[g][ndef._mcl_hardness or 0] = true
end
end
end
for g, map in pairs(maps) do
for k, _ in pairs(map) do
table.insert(values[g], k)
end
end
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
table.sort(values[g])
end
return values
end
-- Returns a table containing a table indexed by "_mcl_hardness_value" to get
-- its index in the list of unique hardnesses for each diggroup.
local function get_hardness_lookup_for_groups(hardness_values)
local map = {}
for g, values in pairs(hardness_values) do
map[g] = {}
for k, v in pairs(values) do
map[g][v] = k
end
end
return map
end
-- Array of unique hardness values for each group which affects dig time.
local hardness_values = get_hardness_values_for_groups()
-- Map indexed by hardness values which return the index of that value in
-- hardness_value. Used for quick lookup.
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
local function compute_creativetimes(group)
local creativetimes = {}
for index, hardness in pairs(hardness_values[group]) do
table.insert(creativetimes, 0)
end
return creativetimes
end
-- Get the list of digging times for using a specific tool on a specific
-- diggroup.
--
-- Parameters:
-- group - the group which it is digging
-- can_harvest - if the tool can harvest the block
-- speed - dig speed multiplier for tool (default 1)
-- efficiency - efficiency level for the tool if applicable
local function get_digtimes(group, can_harvest, speed, efficiency)
local speed = speed or 1
if efficiency then
speed = speed + efficiency * efficiency + 1
end
local digtimes = {}
for index, hardness in pairs(hardness_values[group]) do
local digtime = (hardness or 0) / speed
if can_harvest then
digtime = digtime * 1.5
else
digtime = digtime * 5
end
if digtime <= 0.05 then
digtime = 0
else
digtime = math.ceil(digtime * 20) / 20
end
table.insert(digtimes, digtime)
end
return digtimes
end
-- Get one groupcap field for using a specific tool on a specific group.
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
return {
times = get_digtimes(group, can_harvest, multiplier, efficiency),
uses = uses,
maxlevel = 0,
}
end
-- Add the groupcaps from a field in "_mcl_diggroups" to the groupcaps of a
-- tool.
local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
if not groupcaps_def then
return
end
for g, capsdef in pairs(groupcaps_def) do
local mult = capsdef.speed or 1
local uses = capsdef.uses
local def = mcl_autogroup.registered_diggroups[g]
local max_level = def.levels and #def.levels or 1
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
local level = math.min(capsdef.level, max_level)
if def.levels then
groupcaps[g .. "_dig_default"] = get_groupcap(g, false, mult, efficiency, uses)
if level > 0 then
groupcaps[g .. "_dig_" .. def.levels[level]] = get_groupcap(g, true, mult, efficiency, uses)
end
else
groupcaps[g .. "_dig"] = get_groupcap(g, level > 0, mult, efficiency, uses)
for m=1, #materials do
for g=1, #basegroups do
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m]] = {}
mcl_autogroup.creativetimes[basegroups[g].."_dig_"..materials[m]] = {}
for e=1, max_efficiency_level do
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m].."_efficiency_"..e] = {}
end
end
end
-- Checks if the given node would drop its useful drop if dug by a given tool.
-- Returns true if it will yield its useful drop, false otherwise.
function mcl_autogroup.can_harvest(nodename, toolname)
local ndef = minetest.registered_nodes[nodename]
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
return true
for g=1, #minigroups do
mcl_autogroup.digtimes[minigroups[g].."_dig"] = {}
mcl_autogroup.creativetimes[minigroups[g].."_dig"] = {}
for e=1, max_efficiency_level do
mcl_autogroup.digtimes[minigroups[g].."_dig_efficiency_"..e] = {}
mcl_autogroup.creativetimes[minigroups[g].."_dig_efficiency_"..e] = {}
end
-- Check if it can be dug by tool
local tdef = minetest.registered_tools[toolname]
if tdef and tdef._mcl_diggroups then
for g, gdef in pairs(tdef._mcl_diggroups) do
if ndef.groups[g] then
if ndef.groups[g] <= gdef.level then
return true
end
end
end
end
-- Check if it can be dug by hand
local tdef = minetest.registered_tools[""]
if tdef then
for g, gdef in pairs(tdef._mcl_diggroups) do
if ndef.groups[g] then
if ndef.groups[g] <= gdef.level then
return true
end
end
end
end
return false
end
-- Get one groupcap field for using a specific tool on a specific group.
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
return {
times = get_digtimes(group, can_harvest, multiplier, efficiency),
uses = uses,
maxlevel = 0,
}
end
-- Returns the tool_capabilities from a tool definition or a default set of
-- tool_capabilities
local function get_tool_capabilities(tdef)
if tdef.tool_capabilities then
return tdef.tool_capabilities
end
-- If the damage group and punch interval from hand is not included,
-- then the user will not be able to attack with the tool.
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
return {
full_punch_interval = hand_toolcaps.full_punch_interval,
damage_groups = hand_toolcaps.damage_groups
}
end
-- Get the groupcaps for a tool. This function returns "groupcaps" table of
-- digging which should be put in the "tool_capabilities" of the tool definition
-- or in the metadata of an enchanted tool.
--
-- Parameters:
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
-- efficiency - The efficiency level the tool is enchanted with (default 0)
--
-- NOTE:
-- This function can only be called after mod initialization. Otherwise a mod
-- would have to add _mcl_autogroup as a dependency which would break the mod
-- loading order.
function mcl_autogroup.get_groupcaps(toolname, efficiency)
local tdef = minetest.registered_tools[toolname]
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
return groupcaps
end
-- Get the wear from using a tool on a digging group.
--
-- Parameters
-- toolname - Name of the tool used
-- diggroup - The name of the diggroup the tool is used on
--
-- NOTE:
-- This function can only be called after mod initialization. Otherwise a mod
-- would have to add _mcl_autogroup as a dependency which would break the mod
-- loading order.
function mcl_autogroup.get_wear(toolname, diggroup)
local tdef = minetest.registered_tools[toolname]
local uses = tdef._mcl_diggroups[diggroup].uses
return math.ceil(65535 / uses)
end
local overwrite = function()
for nname, ndef in pairs(minetest.registered_nodes) do
local groups_changed = false
local newgroups = table.copy(ndef.groups)
if (nname ~= "ignore" and ndef.diggable) then
-- Automatically assign the "solid" group for solid nodes
-- Automatically assign the “solid” group for solid nodes
if (ndef.walkable == nil or ndef.walkable == true)
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
and (ndef.node_box == nil or ndef.node_box.type == "regular")
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
newgroups.solid = 1
groups_changed = true
end
-- Automatically assign the "opaque" group for opaque nodes
-- Automatically assign the “opaque” group for opaque nodes
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
newgroups.opaque = 1
groups_changed = true
end
local creative_breakable = false
local function calculate_group(hardness, material, diggroup, newgroups, actual_rating, expected_rating, efficiency)
local time, validity_factor
if actual_rating >= expected_rating then
-- Valid tool
validity_factor = 1.5
else
-- Wrong tool (higher digging time)
validity_factor = 5
end
local speed_multiplier = divisors[material]
if efficiency then
speed_multiplier = speed_multiplier + efficiency * efficiency + 1
end
time = (hardness * validity_factor) / speed_multiplier
if time <= 0.05 then
time = 0
else
time = math.ceil(time * 20) / 20
end
table.insert(mcl_autogroup.digtimes[diggroup], time)
if not efficiency then
table.insert(mcl_autogroup.creativetimes[diggroup], 0)
end
newgroups[diggroup] = #mcl_autogroup.digtimes[diggroup]
return newgroups
end
-- Assign groups used for digging this node depending on
-- the registered digging groups
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
creative_breakable = true
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
if ndef.groups[g] then
if gdef.levels then
newgroups[g .. "_dig_default"] = index
-- Hack in digging times
local hardness = ndef._mcl_hardness
if not hardness then
hardness = 0
end
for i = ndef.groups[g], #gdef.levels do
newgroups[g .. "_dig_" .. gdef.levels[i]] = index
-- Handle pickaxey, axey and shovely
for _, basegroup in pairs(basegroups) do
if (hardness ~= -1 and ndef.groups[basegroup]) then
for g=1,#materials do
local diggroup = basegroup.."_dig_"..materials[g]
newgroups = calculate_group(hardness, materials[g], diggroup, newgroups, g, ndef.groups[basegroup])
for e=1,max_efficiency_level do
newgroups = calculate_group(hardness, materials[g], diggroup .. "_efficiency_" .. e, newgroups, g, ndef.groups[basegroup], e)
end
else
newgroups[g .. "_dig"] = index
groups_changed = true
end
end
end
for m=1, #minigroups do
local minigroup = minigroups[m]
if hardness ~= -1 then
local diggroup = minigroup.."_dig"
-- actual rating
local ar = ndef.groups[minigroup]
if ar == nil then
ar = 0
end
if (minigroup == "handy")
or
(ndef.groups.shearsy_wool and minigroup == "shearsy_wool" and ndef.groups.wool)
or
(ndef.groups.swordy_cobweb and minigroup == "swordy_cobweb" and nname == "mcl_core:cobweb")
or
(ndef.groups[minigroup] and minigroup ~= "swordy_cobweb" and minigroup ~= "shearsy_wool") then
newgroups = calculate_group(hardness, minigroup, diggroup, newgroups, ar, 1)
for e=1,max_efficiency_level do
newgroups = calculate_group(hardness, minigroup, diggroup .. "_efficiency_" .. e, newgroups, ar, 1, e)
end
groups_changed = true
end
end
end
-- Automatically assign the node to the
-- creative_breakable group if it belongs to any digging
-- group.
newgroups["creative_breakable"] = 1
minetest.override_item(nname, {
groups = newgroups
})
end
end
for tname, tdef in pairs(minetest.registered_tools) do
-- Assign groupcaps for digging the registered digging groups
-- depending on the _mcl_diggroups in the tool definition
if tdef._mcl_diggroups then
local toolcaps = table.copy(get_tool_capabilities(tdef))
toolcaps.groupcaps = mcl_autogroup.get_groupcaps(tname)
minetest.override_item(tname, {
tool_capabilities = toolcaps
})
if groups_changed then
minetest.override_item(nname, {
groups = newgroups
})
end
end
end
end

View File

@ -1,3 +1,3 @@
name = _mcl_autogroup
author = ryvnf
author = Wuzzy
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.

159
mods/CORE/class/init.lua Normal file
View File

@ -0,0 +1,159 @@
local Object = {}
-- Define a getter that caches the result for the next time it is called
-- This is a static method (self = the class); in this class system static methods start with __ by convention
function Object:__cache_getter(name, func)
-- cache key: prevent overriding the getter function itself
local key = "_" .. name
-- add a function to the class
self[name] = function(self)
-- check if the value is present in the cache
local value = self[key]
-- `== nil` instead of `not value` to allow caching boolean values
if value == nil then
-- call the getter function
value = func(self)
end
-- store result in cache
self[key] = value
-- return result
return value
end
end
-- Define a getter / setter
-- If no argument is specified, it will act as a getter, else as a setter
-- The specified function MUST return the new value, if it returns nil, nil will be used as new value
-- Optionally works in combination with a previously defined cache getter and only really makes sense in that context
function Object:__setter(name, func)
-- since the function is overridden, we need to store the old one in case a cache getter is defined
local cache_getter = self[name]
-- use same key as cache getter to modify getter cache if present
local key = "_" .. name
self[name] = function(self, new)
-- check whether an argument was specified
if new == nil then
if cache_getter then
-- call the cache getter if present
return cache_getter(self)
else
-- return the value else
return self[key]
end
end
-- call the setter and set the new value to the result
self[key] = func(self, new)
end
end
-- Define a comparator function
-- Acts like a setter, except that it does not set the new value but rather compares the present and specified values and returns whether they are equal or not
-- Incompatible with setter
-- The function is optional. The == operator is used else.
function Object:__comparator(name, func)
local cache_getter = self[name]
local key = "_" .. name
self[name] = function(self, expected)
-- the current value is needed everytime, no matter whether there is an argument or not
local actual
if cache_getter then
-- call the cache getter if present
actual = cache_getter(self)
else
-- use the value else
actual = self[key]
end
-- act as a getter if there is no argument
if expected == nil then
return actual
end
if func then
-- if a function as specified, call it
return func(actual, expected)
else
-- else, use the == operator to compare the expected value to the actual
return actual == expected
end
end
end
-- Override an already existing function in a way that the old function is called
-- If nil is returned, the old function is called. Else the return value is returned. (Only the first return value is taken into concern here, multiple are supported tho)
-- This works even if it is applied to the instance of a class when the function is defined by the class
-- It also works with overriding functions that are located in superclasses
function Object:__override(name, func)
-- store the old function
local old_func = self[name]
-- redefine the function with variable arguments
self[name] = function(...)
-- call the new function and store the return values in a table
local rvals = {func(...)}
-- if nil was returned, fall back to the old function
if rvals[1] == nil then
-- if present, call the return function with the values the new function returned (converted back to a tuple)
return old_func(...)
else
-- return the values from the new function else
return unpack(rvals)
end
end
end
-- Works like override except that the new function does not modify the output of the old function but rather the input
-- The new function can decide with what arguments by returing them, including the `self` reference
-- If the "self" arg is not returned the old function is not called
-- Note that this way the new function cannot change the return value of the old function
function Object:__pipe(name, func)
local old_func = self[name]
self[name] = function(self, ...)
local rvals = {func(self, ...)}
-- check if self was returned properly
if rvals[1] then
-- if present, call the return function with the values the new function returned (converted back to a tuple)
return old_func(unpack(rvals))
end
end
end
-- Make class available as table to distribute the Object table
class = setmetatable({Object = Object}, {
-- Create a new class by calling class() with an optional superclass argument
__call = function(super)
return setmetatable({}, {
-- Create a new instance of the class when the class is called
__call = function(_class, ...)
-- Check whether the first argument is an instance of the class
-- If that is the case, just return it - this is to allow "making sure something is the instance of a class" by calling the constructor
local argtbl = {...}
local first_arg = args[1]
if first_arg and type(first_arg) == "table" and inst.CLASS = _class then
return inst
end
-- set the metatable and remember which class the object belongs to
local instance = setmetatable({CLASS = _class}, {
__index = _class,
})
-- call the constructor if present
if instance.constructor then
instance:constructor(...)
end
-- return the created instance
return instance
end,
-- Object as superclass of all classes that dont have a different one
__index = super or Object,
})
end
}

3
mods/CORE/class/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = class
author = Fleckenstein
description = A class system for MineClone2

View File

@ -1,23 +0,0 @@
# controls
## controls.players
Table containing player controls at runtime.
WARNING: Never use this table in writing
## controls.register_on_press(func)
Register a function that will be executed with (player, keyname) every time a player press a key.
## controls.registered_on_press
Table containing functions registered with controls.register_on_press().
## controls.register_on_release(func)
Register a function that will be executed with (player, keyname, clock_from_last_press) every time a player release a key.
## controls.registered_on_release
Table containing functions registered with controls.register_on_release().
## controls.register_on_hold(func)
Register a function that will be executed with (player, keyname, clock_from_start_hold) every time a player hold a key.
## controls.registered_on_hold
Table containing functions registered with controls.register_on_hold().

View File

@ -1,45 +0,0 @@
# flowlib
Simple flow functions.
## flowlib.is_touching(realpos, nodepos, radius)
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
* realpos: position
* nodepos: position
* radius: number
## flowlib.is_water(pos)
Return true if node at <pos> is water, false overwise.
* pos: position
## flowlib.node_is_water(node)
Return true if <node> is water, false overwise.
* node: node
## flowlib.is_lava(pos)
Return true if node at <pos> is lava, false overwise.
* pos: position
## flowlib.node_is_lava(node)
Return true if <node> is lava, false overwise.
* node: node
## flowlib.is_liquid(pos)
Return true if node at <pos> is liquid, false overwise.
* pos: position
## flowlib.node_is_liquid(node)
Return true if <node> is liquid, false overwise.
* node: node
## flowlib.quick_flow(pos, node)
Return direction where the water is flowing (to be use to push mobs, items...).
* pos: position
* node: node
## flowlib.move_centre(pos, realpos, node, radius)
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
* pos: position
* realpos: position, position of the entity
* node: node
* radius: number

View File

@ -1,27 +0,0 @@
# mcl_autogroup
This mod emulate digging times from mc.
## mcl_autogroup.can_harvest(nodename, toolname)
Return true if <nodename> can be dig with <toolname>.
* nodename: string, valid nodename
* toolname: (optional) string, valid toolname
## mcl_autogroup.get_groupcaps(toolname, efficiency)
This function is used to calculate diggroups for tools.
WARNING: This function can only be called after mod initialization.
* toolname: string, name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
## mcl_autogroup.get_wear(toolname, diggroup)
Return the max wear of <toolname> with <diggroup>
WARNING: This function can only be called after mod initialization.
* toolname: string, name of the tool used
* diggroup: string, the name of the diggroup the tool is used on
## mcl_autogroup.register_diggroup(group, def)
* group: string, name of the group to register as a digging group
* def: (optional) table, table with information about the diggroup (defaults to {} if unspecified)
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
## mcl_autogroup.registered_diggroups
List of registered diggroups, indexed by name.

View File

@ -1,28 +0,0 @@
--[[
This is one part of a mod to replicate the digging times from Minecraft. This
part only exposes a function to register digging groups. The rest of the mod is
implemented and documented in the _mcl_autogroup.
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
mcl_autogroup contains the API functions used to register custom digging groups.
_mcl_autogroup contains most of the code. The leading underscore in the name
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
as possible. Minetest loads mods in reverse alphabetical order.
--]]
mcl_autogroup = {}
mcl_autogroup.registered_diggroups = {}
assert(minetest.get_modpath("_mcl_autogroup"), "This mod requires the mod _mcl_autogroup to function")
-- Register a group as a digging group.
--
-- Parameters:
-- group - Name of the group to register as a digging group
-- def - Table with information about the diggroup (defaults to {} if unspecified)
--
-- Values in def:
-- level - If specified it is an array containing the names of the different
-- digging levels the digging group supports.
function mcl_autogroup.register_diggroup(group, def)
mcl_autogroup.registered_diggroups[group] = def or {}
end

View File

@ -1,3 +0,0 @@
name = mcl_autogroup
author = ryvnf
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.

View File

@ -117,10 +117,6 @@ function mcl_burning.damage(obj)
end
function mcl_burning.set_on_fire(obj, burn_time, reason)
if obj:get_hp() < 0 then
return
end
local luaentity = obj:get_luaentity()
if luaentity and luaentity.fire_resistant then
return
@ -149,7 +145,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
end
if old_burn_time <= burn_time then
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
local sound_id = mcl_burning.get(obj, "int", "sound_id")
if sound_id == 0 then
sound_id = minetest.sound_play("fire_fire", {
object = obj,
@ -157,7 +153,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
max_hear_distance = 16,
loop = true,
}) + 1
end]]--
end
local hud_id
if obj:is_player() then
@ -167,7 +163,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
hud_elem_type = "image",
position = {x = 0.5, y = 0.5},
scale = {x = -100, y = -100},
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
text = "fire_basic_flame.png",
z_index = 1000,
}) + 1
end
@ -175,7 +171,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
mcl_burning.set(obj, "float", "burn_time", burn_time)
mcl_burning.set(obj, "string", "reason", reason)
mcl_burning.set(obj, "int", "hud_id", hud_id)
--mcl_burning.set(obj, "int", "sound_id", sound_id)
mcl_burning.set(obj, "int", "sound_id", sound_id)
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
local minp, maxp = mcl_burning.get_collisionbox(obj)
@ -198,8 +194,8 @@ end
function mcl_burning.extinguish(obj)
if mcl_burning.is_burning(obj) then
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
--minetest.sound_stop(sound_id)
local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
minetest.sound_stop(sound_id)
if obj:is_player() then
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
@ -210,7 +206,7 @@ function mcl_burning.extinguish(obj)
mcl_burning.set(obj, "float", "burn_time")
mcl_burning.set(obj, "float", "damage_timer")
mcl_burning.set(obj, "int", "hud_id")
--mcl_burning.set(obj, "int", "sound_id")
mcl_burning.set(obj, "int", "sound_id")
end
end

View File

@ -1,8 +0,0 @@
# mcl_colors
Mod providing global table containing legacity minecraft colors to be used in mods.
## mcl_colors.*
Colors by upper name, in hex value.
## mcl_colors.background.*
Background colors by upper name, in hex value.

View File

@ -0,0 +1,29 @@
MCLDamageSource = class()
function MCLDamageSource:constructor(tbl)
if tbl then
for k, v in pairs(tbl) do
self[k] = v
end
end
end
function MCLDamageSource:from_mt(reason)
end
MCLDamageSource:__getter("direct_object", function(self)
local obj = self.raw_source_object
if not obj then
return
end
return mcl_object_mgr.get(obj)
end)
MCLDamageSource:__getter("source_object", function(self)
local direct = self:direct_object()
if not direct then
return
end
return direct.source_object or direct
end)

View File

@ -0,0 +1,44 @@
MCLEntity = class(MCLObject)
MCLEntity:__getter("meta", MCLMetadata)
local last_inv_id = 0
MCLEntity:__getter("inventory", function(self)
local info = self.inventory_info
if not info then
return
end
self.inventory_id = "mcl_entity:" .. last_inv_id
last_inv_id = last_inv_id + 1
local inv = minetest.create_detached_inventory(self.inventory_id, self.inventory_callbacks)
for list, size in pairs(data.sizes) do
inv:set_size(list, size)
end
for list, liststr in pairs(data.lists) do
inv:set_list(list, liststr)
end
return inv
end)
function MCLEntity:on_activate(staticdata)
local data = minetest.deserialize(staticdata)
if data then
self:meta():from_table(data)
self.inventory_info = data.inventory
end
end
function MCLEntity:get_staticdata()
local data = self:meta():to_table()
local inventory_info = self.inventory_info
if inventory_info then
data.inventory = {
sizes = inventory_info.sizes,
lists = self:inventory():get_lists()
}
end
return minetest.serialize(data)
end

View File

@ -0,0 +1,103 @@
MCLEquipment = class()
function MCLEquipment:constructor(inv, idx)
self.inv = inv
self.idx = idx
end
MCLEquipment:__cache_getter("has_main", function(self)
return self.inv and self.idx and self.inv:get_list("main") and true or false
end)
MCLEquipment:__cache_getter("has_right", function(self)
return self.inv and self.inv:get_list("right_hand") and true or false
end)
MCLEquipment:__cache_getter("has_left", function(self)
return self.inv and self.inv:get_list("left_hand") and true or false
end)
MCLEquipment:__cache_getter("has_armor", function(self)
return self.inv and self.inv:get_list("armor") and true or false
end)
function MCLEquipment:mainhand()
if self:has_main() then
return self.inv:get_stack("main", self.idx)
elseif self:has_right() then
return self.inv:get_stack("right_hand", 1)
else
return ItemStack()
end
end
MCLEquipment:__setter("mainhand", function(self, new)
if self:has_main() then
self.inv:set_stack("main", self.idx, stack)
elseif self:has_right() then
self.inv:set_stack("right_hand", 1, stack)
end
end)
function MCLEquipment:offhand()
if self:has_left() then
return self.inv:get_stack("left_hand", 1)
else
return ItemStack()
end
end
MCLEquipment:__setter("offhand", function(self, new)
if self:has_left() then
self.inv:set_stack("left_hand", 1, new)
end
end)
function MCLEquipment:__armor(idx, name)
self[name] = function(self)
if self:has_armor() then
return self.inv:get_stack("armor", idx)
else
return ItemStack()
end
end
self:__setter(name, function(self, new)
if self:has_armor() then
self.inv:set_stack("armor", idx, new)
end
end)
end
local armor_slots = {"helmet", "chestplace", "leggings", "boots"}
for i, name in ipairs(armor_slots) do
MCLEquipment:__armor(idx, name)
end
local function insert(tbl, key, stack)
if stack:get_name() ~= "" then
tbl[key] = stack
end
end
function MCLEquipment:get_armor()
local tbl = {}
if self:has_armor() then
for i, name in ipairs(armor_slots) do
insert(tbl, name, self.inv:get_stack("armor", i))
end
end
return tbl
end
function MCLEquipment:get_all()
local tbl = {}
insert(tbl, "mainhand", self:mainhand())
insert(tbl, "offhand", self:offhand())
for k, v in pairs(self:get_armor()) do
tbl[k] = v
end
return tbl
end

View File

@ -1,15 +0,0 @@
# mcl_explosions
This mod provide helper functions to create explosions.
## mcl_explosions.explode(pos, strength, info, puncher)
* pos: position, initial position of the explosion
* strenght: number, radius of the explosion
* info: table, explosion informations:
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
* sound: bool, if true, the explosion will play a sound (default: true)
* particles: bool, if true, the explosion will create particles (default: true)
* fire: bool, if true, 1/3 nodes become fire (default: false)
* griefing: bool, if true, the explosion will destroy nodes (default: true)
* grief_protected: bool, if true, the explosion will also destroy nodes which have been protected (default: false)
* puncher: (optional) entity, will be used as source for damage done by the explosion

View File

@ -0,0 +1,66 @@
mcl_gamerules = {
__defaults = {},
__rules = {},
}
setmetatable(mcl_gamerules, {__index = mcl_gamerules.__rules})
local worldpath = minetest.get_worldpath()
function mcl_gamerules.__load()
local file = io.open(worldpath .. "gamerules.json", "r")
if file then
local contents = file:read("*all")
file:close()
local data = minetest.parse_json(contents)
local rules = mcl_gamerules.__rules
for rule, default in pairs(mcl_gamerules.__defaults) do
local value = data[rule]
if value == nil then
value = default
end
rules[rule] = value
end
end
end
function mcl_gamerules.__save()
local file = io.open(worldpath .. "gamerules.json", "w")
file:write(minetest.write_json(mcl_gamerules.__rules, true))
file:close()
end
function mcl_gamerules.__set(rule, value)
if not mcl_gamerules.__defaults[rule] then
return false
end
mcl_gamerules.__rules[rule] = value
mcl_gamerules.__save()
return true
end
function mcl_gamerules.__register(rule, default)
mcl_gamerules.__defaults[rule] = default
end
mcl_gamerules.__register("announceAdvancements", true)
mcl_gamerules.__register("commandBlockOutput", true)
mcl_gamerules.__register("doDaylightCycle", true)
mcl_gamerules.__register("doFireTick", true)
mcl_gamerules.__register("doImmediateRespawn", false)
mcl_gamerules.__register("doMobLoot", true)
mcl_gamerules.__register("doMobSpawning", true)
mcl_gamerules.__register("doTileDrops", true)
mcl_gamerules.__register("doWeatherCycle", true)
mcl_gamerules.__register("drowningDamage", true)
mcl_gamerules.__register("fallDamage", true)
mcl_gamerules.__register("fireDamage", true)
mcl_gamerules.__register("keepInventory", false)
mcl_gamerules.__register("logAdminCommands", true)
mcl_gamerules.__register("mobGriefing", true)
mcl_gamerules.__register("naturalRegeneration", true)
mcl_gamerules.__register("pvp", true)
mcl_gamerules.__register("showDeathMessages", true)
mcl_gamerules.__register("tntExplodes", true)
minetest.register_on_mods_loaded(mcl_gamerules.__load)

View File

@ -0,0 +1,32 @@
mcl_groupcache = {
cache = {},
}
local function check_insert(item, group, cache)
if minetest.get_item_group(item, group) ~= 0 then
table.insert(cache, item)
end
end
local old_register_item = minetest.register_item
function minetest.register_item(name, def)
old_register_item(name, def)
for group, cache in pairs(mcl_groupcache.cache) do
check_insert(item, group, cache)
end
end
function mcl_groupcache.init_cache(group)
local cache = {}
for item in pairs(minetest.registered_items) do
check_insert(item, group, cache)
end
return cache
end
function mcl_groupcache.get_items_in_group(group)
local cache = mcl_groupcache.cache[group] or mcl_groupcache.init_cache(group)
mcl_groupcache.cache[group] = cache
return cache
end

View File

@ -0,0 +1,3 @@
name = mcl_groupcache
author = Fleckenstein
description = Keep track of items with certain groups

View File

@ -21,9 +21,6 @@ mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;tru
-- Legacy
mcl_vars.inventory_header = ""
-- Tool wield size
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
-- Mapgen variables
local mg_name = minetest.get_mapgen_setting("mg_name")
local minecraft_height_limit = 256
@ -36,26 +33,25 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, core.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000)
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
local central_chunk_max_pos = central_chunk_min_pos + chunk_size_in_nodes - 1
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
local function coordinate_to_block(x)
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
end
local function coordinate_to_chunk(x)
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize)
end
function mcl_vars.pos_to_block(pos)
@ -74,7 +70,7 @@ function mcl_vars.pos_to_chunk(pos)
}
end
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes)
local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z
@ -178,86 +174,3 @@ minetest.craftitemdef_default.stack_max = 64
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
math.randomseed(os.time())
local chunks = {} -- intervals of chunks generated
function mcl_vars.add_chunk(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
local prev
for i, d in pairs(chunks) do
if n <= d[2] then -- we've found it
if (n == d[2]) or (n >= d[1]) then return end -- already here
if n == d[1]-1 then -- right before:
if prev and (prev[2] == n-1) then
prev[2] = d[2]
table.remove(chunks, i)
return
end
d[1] = n
return
end
if prev and (prev[2] == n-1) then --join to previous
prev[2] = n
return
end
table.insert(chunks, i, {n, n}) -- insert new interval before i
return
end
prev = d
end
chunks[#chunks+1] = {n, n}
end
function mcl_vars.is_generated(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
for i, d in pairs(chunks) do
if n <= d[2] then
return (n >= d[1])
end
end
return false
end
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
-- p: Position, if it's wrong, {name="error"} node will return.
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
--
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
function mcl_vars.get_node(p, force, us_timeout)
-- check initial circumstances
if not p or not p.x or not p.y or not p.z then return {name="error"} end
-- try common way
local node = minetest.get_node(p)
if node.name ~= "ignore" then
return node
end
-- copy table to get sure it won't changed by other threads
local pos = {x=p.x,y=p.y,z=p.z}
-- try LVM
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos)
if node.name ~= "ignore" or not force then
return node
end
-- all ways failed - need to emerge (or forceload if generated)
local us_timeout = us_timeout or 244
if mcl_vars.is_generated(pos) then
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
minetest.forceload_block(pos)
else
minetest.emerge_area(pos, pos)
end
local t = minetest.get_us_time()
node = minetest.get_node(pos)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
node = minetest.get_node(pos)
end
return node
-- it still can return "ignore", LOL, even if force = true, but only after time out
end

View File

@ -0,0 +1,43 @@
MCLItemStack = class()
function MCLItemStack:constructor(stack)
self.stack = stack
end
MCLItemStack:__getter("enchantments", function(self)
return mcl_enchanting.get_enchantments(self.stack)
end)
MCLItemStack:__comparator("enchantments", mcl_types.match_enchantments)
function MCLItemStack:meta()
return self.stack:get_meta()
end
MCLItemStack:__comparator("meta", mcl_types.match_meta)
function MCLItemStack:get_enchantment(name)
return self:enchantments()[name] or 0
end
function MCLItemStack:has_enchantment(name)
return self:get_enchantment(name) > 0
end
function MCLItemStack:total_durability()
end
function MCLItemStack:durability()
local def = self.stack:get_definition()
if def then
local base_uses = def._durability
end
end
function MCLItemStack:use_durability()
end
function MCLItemStack:restore_durability()
end
function MCLItemStack:get_group()
end

View File

@ -0,0 +1,25 @@
MCLMetadata = class()
function MCLMetadata:constructor()
self.fields = {}
end
for _type, default in pairs({string = "", float = 0.0, int = 0}) do
MCLMetadata["set_" .. _type] = function(name, value) do
if value == default then
value = nil
end
self.fields[name] = value
end
MCLMetadata["get_" .. _type] = function(name) do
return self.fields[name] or default
end
end
function MCLMetadata:to_table()
return table.copy(self)
end
function MCLMetadata:from_table(tbl)
self.fields = table.copy(tbl.fields)
end

View File

@ -0,0 +1,18 @@
MCLMob = class(MCLEntity)
function MCLMob:get_hp()
self:meta():get_float("hp")
end
function MCLMob:set_hp()
self:meta():set_float("hp", hp)
end
function MCLMob:on_damage(damage, source)
MCLEntity.on_damage(self, damage, source)
local new_hp = self:get_hp()
if new_hp <= 0 and new_hp + damage > 0 then
self:on_death(source)
end
end

View File

@ -0,0 +1,121 @@
MCLObject = class()
function MCLObject:constructor(obj)
self.object = obj.object or obj
self.IS_MCL_OBJECT = true
end
function MCLObject:on_punch(hitter, time_from_last_punch, tool_capabilities, dir, damage)
local source = MCLDamageSource({is_punch = true, raw_source_object = hitter})
local knockback = {
hitter = hitter,
time_from_last_punch = time_from_last_punch,
tool_capabilities = tool_capabilities,
dir = dir,
}
self:damage(damage, source, knockback)
return true
end
function MCLObject:apply_knockback(strength, dir)
local oldvel = self.object:get_velocity()
local vel = vector.multiply(vector.normalize(vector.new(dir.x, 0, dir.z)), strength)
if self:is_on_ground() then
local old_y = oldvel.y / 2
y = math.min(0.4, old_y / 2 + strenth)
vel.y
end
vel = vector.subtract(vel, vector.divide(vector.new(oldvel.x, 0, oldvel.z), 2))
self.object:add_velocity(vel)
end
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
function MCLObject:damage(damage, source, knockback)
damage = self:damage_modifier(damage, source) or damage
self:set_hp(self:get_hp() - damage)
if type(knockback) == "table" then
knockback = self:calculate_knockback(
knockback.hitter,
knockback.time_from_last_punch,
knockback.tool_capabilities,
knockback.dir or vector.direction(knockback.hitter:get_pos(), self.object:get_pos(),
knockback.distance or vector.distance(knockback.hitter:get_pos(), self.object:get_pos(),
damage,
source)
end
table.insert(self.damage_info, {
damage = damage,
source = source,
knockback = knockback,
})
return damage
end
function MCLObject:wield_index()
end
MCLObject:__getter("equipment", function(self)
return MCLEquipment(self:inventory(), self:wield_index())
end)
function MCLObject:get_hp()
return self.object:get_hp()
end
function MCLObject:set_hp(hp)
self.object:set_hp(hp)
end
function MCLObject:death_drop(inventory, listname, index, stack)
minetest.add_item(self.object:get_pos(), stack)
inventory:set_stack(listname, index, nil)
end
function MCLObject:on_death(source)
local inventory = self:inventory()
if inventory then
for listname, list in pairs(inventory:get_lists()) do
for index, stack in pairs(list) do
if stack:get_name() ~= "" and then
self:death_drop(inventory, listname, index, stack)
end
end
end
end
end
function MCLObject:damage_modifier(damage, source)
if self.invulnerable and not source.bypasses_invulnerability then
return 0
end
end
function MCLObject:on_damage(damage, source, knockback)
end
MCLObject.calculate_knockback = minetest.calculate_knockback
function minetest.calculate_knockback()
return 0
end
function MCLObject:on_step()
local damage_info = self.damage_info
if damage_info then
self.damage_info = nil
self:on_damage(damage_info.damage, damage_info.source)
if damage_info.knockback then
self.object:add_velocity(damage_info.knockback)
end
end
end

View File

@ -0,0 +1,121 @@
mcl_object_mgr = {
players = {}
}
-- functions
function mcl_object_mgr.get(obj)
local rval
if mcl_object_mgr.is_mcl_object(obj) then
rval = obj
elseif mcl_object_mgr.is_player(obj) then
rval = mcl_object_mgr.get_player(obj)
elseif mcl_object_mgr.is_entity(obj) then
rval = mcl_object_mgr.get_entity(obj)
end
return assert(rval, "No matching MCLObject found. This is most likely an error caused by custom mods.")
end
function mcl_object_mgr.is_mcl_object(obj)
return type(obj) == "table" and obj.IS_MCL_OBJECT
end
function mcl_object_mgr.is_player(obj)
return type(obj) == "string" or type(obj) == "userdata" and obj:is_player()
end
function mcl_object_mgr.is_is_entity(obj)
return type(obj) == "table" and obj.object or type(obj) == "userdata" and obj:get_luaentity()
end
function mcl_object_mgr.get_entity(ent)
if type(ent) == "userdata" then
ent = ent:get_luaentity()
end
return ent.mcl_entity
end
function mcl_object_mgr.get_player(name)
if type(name) == "userdata" then
name = name:get_player_name()
end
return mcl_player_mgr.players[name]
end
-- entity wrappers
local function add_entity_wrapper(def, name)
def[name] = function(luaentity, ...)
local func = self.mcl_entity[name]
if func then
return func(self.mcl_entity, ...)
end
end
end
function mcl_object_mgr.register_entity(name, initial_properties, base_class)
local def = {
initial_properties = initial_properties,
on_activate = function(self, ...)
local entity = base_class(self.object)
self.mcl_entity = entity
if entity.on_activate then
entity:on_activate(...)
end
end,
}
add_entity_wrapper(def, "on_deactivate")
add_entity_wrapper(def, "on_step")
add_entity_wrapper(def, "on_punch")
add_entity_wrapper(def, "on_death")
add_entity_wrapper(def, "on_rightclick")
add_entity_wrapper(def, "on_attach_child")
add_entity_wrapper(def, "on_detach_child")
add_entity_wrapper(def, "on_detach")
add_entity_wrapper(def, "get_staticdata")
minetest.register_entity(name, def)
end
-- player wrappers
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
mcl_player_mgr.players[name] = MCLPlayer(player)
mcl_player_mgr.players[name]:on_join()
end)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
mcl_player_mgr.players[name]:on_leave()
mcl_player_mgr.players[name] = nil
end)
local function add_player_wrapper(wrapper, regfunc)
minetest[regfunc or "register_" .. wrapper .. "player"](function(player, ...)
local mclplayer = mcl_player_mgr.players[player:get_player_name()]
local func = mclplayer[funcname or wrapper]
if func then
func(mclplayer, ...)
end
end)
end
add_player_wrapper("on_punch")
add_player_wrapper("on_rightclick")
add_player_wrapper("on_death", "register_on_dieplayer")
add_player_wrapper("on_respawn")
minetest.register_on_player_hpchange(function(player, hp_change, reason)
if not reason.auto then
local mclplayer = mcl_object_mgr.get(player)
local source = MCLDamageSource():from_mt(reason)
mclplayer:damage(hp_change, source)
return true
end
end, true)

View File

@ -66,58 +66,3 @@ function mcl_particles.delete_node_particlespawners(pos)
end
return false
end
-- 3 exptime variants because the animation is not tied to particle expiration time.
-- 3 colorized variants to imitate minecraft's
local smoke_pdef_cached = {}
function mcl_particles.spawn_smoke(pos, name, smoke_pdef_base)
local min = math.min
local new_minpos = vector.add(pos, smoke_pdef_base.minrelpos)
local new_maxpos = vector.add(pos, smoke_pdef_base.maxrelpos)
-- populate the cache
if smoke_pdef_cached[name] then
for i, smoke_pdef in ipairs(smoke_pdef_cached[name]) do
smoke_pdef.minpos = new_minpos
smoke_pdef.maxpos = new_maxpos
mcl_particles.add_node_particlespawner(pos, smoke_pdef, "high")
end
-- cache already populated
else
smoke_pdef_cached[name] = {}
local smoke_pdef = table.copy(smoke_pdef_base)
smoke_pdef.amount = smoke_pdef_base.amount / 9
smoke_pdef.time = 0
smoke_pdef.animation = {
type = "vertical_frames",
aspect_w = 8,
aspect_h = 8,
-- length = 3 exptime variants
}
smoke_pdef.collisiondetection = true
smoke_pdef.minpos = new_minpos
smoke_pdef.maxpos = new_maxpos
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
-- to have varying exptime for each variant.
local exptimes = { 0.175, 0.375, 1.0 }
local colorizes = { "199", "209", "243" } -- round(78%, 82%, 90% of 256) - 1
for _,exptime in ipairs(exptimes) do
for _,colorize in ipairs(colorizes) do
smoke_pdef.maxexptime = exptime * smoke_pdef_base.maxexptime
smoke_pdef.animation.length = exptime + 0.1
-- minexptime must be set such that the last frame is actully rendered,
-- even if its very short. Larger exptime -> larger range
smoke_pdef.minexptime = min(exptime, (7.0/8.0 * (exptime + 0.1) + 0.1))
smoke_pdef.texture = "mcl_particles_smoke_anim.png^[colorize:#000000:" ..colorize
mcl_particles.add_node_particlespawner(pos, smoke_pdef, "high")
table.insert(smoke_pdef_cached[name], table.copy(smoke_pdef))
end
end
end
end

View File

@ -0,0 +1,19 @@
MCLPlayer = class(MCLObject)
MCLPlayer:__cache_getter("meta", function(self)
return self.object:get_meta()
end)
MCLPlayer:__cache_getter("inventory", function(self)
return self.object:get_inventory()
end)
MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
if not mcl_gamerules.keepInventory then
return self, inventory, listname, index, stack
end
end)
function MCLPlayer:on_damage(damage, source, knockback)
MCLObject.on_damage(self, damage, source, knockback)
end

View File

@ -410,7 +410,7 @@ function mcl_util.get_color(colorstr)
local mc_color = mcl_colors[colorstr:upper()]
if mc_color then
colorstr = mc_color
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#" then
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#"then
return
end
local hex = tonumber(colorstr:sub(2, 7), 16)
@ -418,3 +418,31 @@ function mcl_util.get_color(colorstr)
return colorstr, hex
end
end
function mcl_util.registration_function(tbl, func)
return function(name, def)
if func then
local res = func(name, def)
if res == false then
return
elseif res ~= nil then
def = res
end
end
tbl[name] = def
end
end
function mcl_util.rand(pr, ...)
return pr and pr:next(...) or math.random(...)
end
function mcl_util.rand_bool(probability, pr)
if probability >= 1 then
return true
elseif probability <= 0 then
return false
else
return mcl_util.rand(pr, 0, 32767) < probability * 32768
end
end

View File

@ -1,80 +0,0 @@
# mcl_worlds
This mod provides utility functions about positions and dimensions.
## mcl_worlds.is_in_void(pos)
This function returns:
* true, true: if pos is in deep void (deadly)
* true, false: if the pos is in void (non deadly)
* false, false: owerwise
Params:
* pos: position
## mcl_worlds.y_to_layer(y)
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
Mainly used for ore generation.
Takes an Y coordinate as input and returns:
* The corresponding Minecraft layer (can be nil if void)
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if <y> is in the void
If the Y coordinate is not located in any dimension, it will return: nil, "void"
Params:
* y: int
## mcl_worlds.pos_to_dimension(pos)
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
* pos: position
## mcl_worlds.layer_to_y(layer, mc_dimension)
Takes a Minecraft layer and a “dimension” name and returns the corresponding Y coordinate for MineClone 2.
mc_dimension can be "overworld", "nether", "end" (default: "overworld").
* layer: int
* mc_dimension: string
## mcl_worlds.has_weather(pos)
Returns true if <pos> can have weather, false owerwise.
Weather can be only in the overworld.
* pos: position
## mcl_worlds.has_dust(pos)
Returns true if <pos> can have nether dust, false owerwise.
Nether dust can be only in the nether.
* pos: position
## mcl_worlds.compass_works(pos)
Returns true if compasses are working at <pos>, false owerwise.
In mc, you cant use compass in the nether and the end.
* pos: position
## mcl_worlds.compass_works(pos)
Returns true if clock are working at <pos>, false owerwise.
In mc, you cant use clock in the nether and the end.
* pos: position
## mcl_worlds.register_on_dimension_change(function(player, dimension))
Register a callback function func(player, dimension).
It will be called whenever a player changes between dimensions.
The void counts as dimension.
* player: player, the player who changed the dimension
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
## mcl_worlds.registered_on_dimension_change
Table containing all function registered with mcl_worlds.register_on_dimension_change()
## mcl_worlds.dimension_change(player, dimension)
Notify this mod of a dimmension change of <player> to <dimension>
* player: player, player who changed the dimension
* dimension: string, new dimension ("overworld", "nether", "end", "void")

View File

@ -6,7 +6,7 @@ mcl_worlds = {}
function mcl_worlds.is_in_void(pos)
local void =
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
local void_deadly = false
@ -15,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
-- Overworld → Void → End → Void → Nether → Void
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
-- The void between End and Nether. Like usual, but here, the void
-- *above* the Nether also has a small tolerance area, so player
-- can fly above the Nether without getting hurt instantly.
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
elseif pos.y < mcl_vars.mg_nether_min then
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
end
@ -35,7 +35,7 @@ end
function mcl_worlds.y_to_layer(y)
if y >= mcl_vars.mg_overworld_min then
return y - mcl_vars.mg_overworld_min, "overworld"
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then
return y - mcl_vars.mg_nether_min, "nether"
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
return y - mcl_vars.mg_end_min, "end"
@ -73,7 +73,7 @@ end
-- Takes a position and returns true if this position can have Nether dust
function mcl_worlds.has_dust(pos)
-- Weather in the Overworld and the high part of the void below
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
return pos.y <= mcl_vars.mg_nether_max + 64 and pos.y >= mcl_vars.mg_nether_min - 64
end
-- Takes a position (pos) and returns true if compasses are working here

View File

@ -0,0 +1,101 @@
mcl_loottables.register_entry = mcl_util.registration_function(mcl_loottables.entries)
mcl_loottables.register_table = mcl_util.registration_function(mcl_loottables.tables, function(name, def)
local function set_parents(parent)
for _, child in ipairs(parent.children or parent.entries or parent.pools or {}) do
child.parent = parent
set_parents(child)
end
end
set_parents(def)
end)
function mcl_loottables.get_entry_type(entry)
return mcl_loottables.entries[entry.type]
end
function mcl_loottables.get_candidates(entries, data, func)
local candidates = {}
for _, entry in ipairs(entries) do
local success = mcl_predicates.do_predicates(entry.conditions, data)
if success then
local children = entry.children
if children then
table.insert_all(candidates, mcl_loottables.get_candidates(children, data, mcl_loottables.get_entry_type(entry).preprocess))
else
table.insert(candidates, entry)
end
end
if func and func(success, data) then
break
end
end
return candidates
end
function mcl_loottables.do_item_modifiers(itemstack, node, data)
if node then
mcl_item_modifiers.do_item_modifiers(itemstack, node.functions, data)
mcl_loottables.do_item_modifiers(itemstack, node.parent, data)
end
end
function mcl_loottables.do_pools(pools, functions, data)
local luck = data.luck or 0
local stacks = {}
for _, pool in ipairs(pools or {}) do
if mcl_predicates.do_predicates(pool.conditions, data) do
local rolls = mcl_numbers.get_number(pool.rolls, data) + mcl_numbers.get_number(pool.bonus_rolls, data) * luck
for i = 1, rolls do
local candidates = mcl_loottables.get_candidates(pool.entries, data)
if #candidates > 0 then
local total_weight = 0
local weights = {}
for _, candidate in ipairs(candidates)
total_weight = total_weight + math.floor((candidate.weight or 1) + (candidate.quality or 0) * luck)
table.insert(weights, total_weight)
end
local selected
local rnd = mcl_util.rand(data.pr, 0, weight - 1)
for i, w in ipairs(weights) do
if rnd < w then
selected = candidates[i]
break
end
end
local pool_stacks = mcl_loottables.get_entry_type(entry).process(selected, data)
for _, stack in ipairs(pool_stacks) do
mcl_item_modifiers.do_item_modifiers(stack, selected, data)
end
table.insert_all(stacks, pool_stacks)
end
end
end
end
return stacks
end
function mcl_loottables.get_loot(def, data)
if type(def) == "string" then
def = mcl_loottables.tables[def]
end
return mcl_loottables.do_pools(def.pools)
end
function mcl_loottables.drop_loot(def, data)
local loot = mcl_loottables.get_loot(def, data)
local old_loot = table.copy(loot)
for _, stack in ipairs(old_loot) do
local max_stack = stack:get_stack_max()
while max_stack < stack:get_count() do
table.insert(loot, stack:take_items(max_stack))
end
end
return loot
end

View File

@ -0,0 +1,44 @@
mcl_loottables.register_entry("mcl_loottables:alternatives", {
preprocess = function(success, data)
return success
end,
})
mcl_loottables.register_entry("mcl_loottables:group", {
preprocess = function(success, data)
return false
end,
})
mcl_loottables.register_entry("mcl_loottables:sequence", {
preprocess = function(success, data)
return not success
end,
})
mcl_loottables.register_entry("mcl_loottables:tag", function(entry, data)
local stacks = mcl_groupcache.get_items_in_group(entry.name)
if entry.expand then
stacks = {stacks[pr:next(1, #stacks)]}
end
return stacks
end)
mcl_loottables.register_entry("mcl_loottables:loot_table", {
process = function(entry, data)
return mcl_loottables.get_loot(entry.name, data)
end,
})
mcl_loottables.register_entry("mcl_loottables:empty", {
process = function(entry, data)
return {}
end,
})
mcl_loottables.register_entry("mcl_loottables:item", {
process = function(entry, data)
return {item = ItemStack(entry.name)}
end,
})

View File

@ -0,0 +1,9 @@
mcl_loottables = {
tables = {},
entries = {},
}
local modpath = minetest.get_modpath("mcl_loottables")
dofile(modpath .. "/api.lua")
dofile(modpath .. "/entries.lua")

View File

@ -0,0 +1,4 @@
name = mcl_loottables
author = Fleckenstein
description = Provides Minecraft-like loot table definitions
depends = mcl_util, mcl_predicates, mcl_item_modifiers, mcl_groupcache

View File

@ -0,0 +1,19 @@
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
function mcl_numbers.get_number(provider, data)
if type(provider) == "number" then
return provider
else
mcl_numbers.providers[data.type](provider, data)
end
end
function mcl_numbers.match_bounds(actual, expected, data)
if type(expected) == "table" then
expected = {
min = mcl_numbers.get_number(expected.min, data),
max = mcl_numbers.get_number(expected.max, data)
}
end
return mcl_util.match_bounds(actual, expected)
end

View File

@ -0,0 +1,25 @@
mcl_numbers = {
providers = {},
}
dofile(minetest.get_modpath("mcl_numbers") .. "/api.lua")
mcl_numbers.register_provider("mcl_numbers:constant", function(provider)
return provider.value
end)
mcl_numbers.register_provider("mcl_numbers:uniform", function(provider, data)
return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min, data), mcl_numbers.get_number(provider.max, data))
end)
mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data)
local n = mcl_numbers.get_number(provider.n, data)
local num = 0
for i = 1, n do
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then
num = num + 1
end
end
return num
end)

View File

@ -0,0 +1,4 @@
name = mcl_numbers
author = Fleckenstein
description = Minecraft-like number providers
depends = mcl_util

View File

@ -0,0 +1,15 @@
mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates)
function mcl_predicates.do_predicates(predicates, data, or_mode)
or_mode = or_mode or false
for _, func in ipairs(predicates) do
if type(func) == "string" then
func = mcl_predicates.predicates[func]
end
local failure = func and not func(def, data) or false
if or_mode ~= failure then
return or_mode
end
end
return not or_mode or #predicates == 0
end

View File

@ -0,0 +1,104 @@
mcl_predicates = {
predicates = {},
}
local modpath = minetest.get_modpath("mcl_predicates")
dofile(modpath .. "/api.lua")
mcl_predicates.register_predicate("alternative", function(predicate, data)
return mcl_predicates.do_predicates(predicate.terms, data, true)
end)
mcl_predicates.register_predicate("match_node", function(predicate, data)
return mcl_types.match_node(data.node or minetest.get_node(data.pos), predicate, data.pos, data.nodemeta)
end)
mcl_predicates.register_predicate("damage_source_properties", function(predicate, data)
-- ToDo: damage source abstraction
return nil
end)
mcl_predicates.register_predicate("entity_properties", function(predicate, data)
local entity = predicate.entity
if not entity or (entity ~= "this" and entity ~= "killer" and entity ~= "killer_player") then
return false
end
local ref = data[entity]
if not ref then
return false
end
-- ToDo: entity / player abstraction
return nil
end)
mcl_predicates.register_predicate("entity_scores", function(predicate, data)
-- ToDo: scoreboards
return nil
end)
mcl_predicates.register_predicate("inverted", function(predicate, data)
return not mcl_predicates.do_predicates({predicate.term}, data)
end)
mcl_predicates.register_predicate("killed_by_player", function(predicate, data)
return not predicate.inverse ~= not data.killer_player
end)
mcl_predicates.register_predicate("location_check", function(predicate, data)
local pos = vector.add(data.pos, vector.new(predicate.offset_x or 0, predicate.offset_y or 0, predicate.offset_z or 0))
return mcl_location(pos, data.nodemeta):match(predicate.predicate)
end)
mcl_predicates.register_predicate("match_tool", function(predicate, data)
return mcl_predicates.match_tool(data.tool, predicate.predicate)
end)
mcl_predicates.register_predicate("random_chance", function(predicate, data)
return mcl_util.rand_bool(predicate.chance, data.pr)
end)
mcl_predicates.register_predicate("random_chance_with_looting", function(predicate, data)
local chance = predicate.chance -- + (looting_level * looting_multiplier)
-- ToDo: entity / player abstraction
return mcl_util.rand_bool(chance, data.pr)
end)
mcl_predicates.register_predicate("reference", function(predicate, data)
-- ToDo: needs research
return nil
end)
mcl_predicates.register_predicate("survives_explosion", function(predicate, data)
return mcl_util.rand_bool(data.drop_chance or 1, data.pr)
end)
mcl_predicates.register_predicate("table_bonus", function(predicate, data)
-- ToDo: entity / player abstraction
return nil
end)
mcl_predicates.register_predicate("time_check", function(predicate, data)
-- ToDo: needs research
return nil
end)
mcl_predicates.register_predicate("weather_check", function(predicate, data)
local weather = mcl_weather.get_weather()
if predicate.thundering then
return weather == "thunder"
elseif predicate.raining then
return weather == "rain"
else
return true
end
end)
mcl_predicates.register_predicate("value_check", function(predicate, data)
local value = mcl_numbers.get_number(predicate.value, data)
return mcl_numbers.check_in_bounds(value, predicate.range, data)
end)

View File

@ -0,0 +1,4 @@
name = mcl_predicates
author = Fleckenstein
description = Provides MC-like predicates
depends = mcl_util, mcl_numbers

View File

@ -0,0 +1,88 @@
mcl_types = {}
function mcl_types.match_bounds(actual, expected)
if type(expected) == "table" then
return actual <= expected.max and actual >= expected.min
else
return actual == expected
end
end
function mcl_types.match_meta(actual, expected)
for k, v in pairs(expected) do
if actual:get_string(k) ~= v then
return false
end
end
return true
end
function mcl_types.match_vector(actual, expected)
for k, v in pairs(expected) do
if not mcl_types.match_bounds(actual[k], expected[k]) then
return false
end
end
return true
end
function mcl_types.match_enchantments(actual, expected)
for _, v in ipairs(expected) do
if not mcl_types.match_bounds(actual[v.enchantment] or 0, v.levels or {min = 1, max = math.huge}) then
return false
end
end
return true
end
function mcl_types.match_item(actual, expected)
actual = actual or ItemStack()
if expected.item and actual:get_name() ~= expected.item then
return false
elseif expected.group and minetest.get_item_group(actual:get_name(), expected.group) == 0 then
return false
elseif expected.count and not mcl_types.match_bounds(actual:get_count(), expected.count) then
return false
elseif expected.wear and not mcl_types.match_bounds(actual:get_wear(), expected.wear) then
return false
elseif expected.enchantments and not mcl_types.match_enchantments(mcl_enchanting.get_enchantments(actual), expected.enchantments) then
return false
elseif expected.meta and not mcl_types.match_meta(actual:get_meta(), expected.meta) then
return false
else
return true
end
end
function mcl_types.match_node(actual, expected, pos, meta)
if expected.node and actual.name ~= expected.node then
return false
elseif expected.group and minetest.get_item_group(actual.name, expected.group) == 0 then
return false
elseif expected.param1 and actual.param1 ~= compare.param1 then
return false
elseif expected.param2 and actual.param2 ~= compare.param2 then
return false
elseif expected.meta and not mcl_types.match_meta(meta or minetest.get_meta(pos), expected.meta) then
return false
else
return true
end
end
function mcl_types.match_pos(actual, expected, meta)
if expected.pos and not mcl_types.match_vector(actual, expected.pos) then
return false
elseif expected.dimension and mcl_worlds.pos_to_dimension(actual) ~= expected.dimension then
return false
elseif expected.biome and minetest.get_biome_name(minetest.get_biome_data(actual).biome) ~= expected.biome then
return false
elseif expected.node and not mcl_types.match_node(minetest.get_node(actual), expected.node, actual, meta) then
return false
elseif expected.light and not mcl_types.match_bounds(minetest.get_node_light(actual), expected.light) then
return false
else
return true
end
end

View File

@ -0,0 +1,4 @@
name = mcl_types
author = Fleckenstein
description = Offers compare functions for many types, used for MineClone2 datapacks
depends =

2
mods/DATA/modpack.conf Normal file
View File

@ -0,0 +1,2 @@
name = DATA
description = Meta-modpack containing the datapack system for MineClone 2

View File

@ -1,6 +1,6 @@
local S = minetest.get_translator("mcl_boats")
local boat_visual_size = {x = 1, y = 1, z = 1}
local boat_visual_size = {x = 3, y = 3, z = 3}
local paddling_speed = 22
local boat_y_offset = 0.35
local boat_y_offset_ground = boat_y_offset + 0.6
@ -12,7 +12,9 @@ local function is_group(pos, group)
return minetest.get_item_group(nn, group) ~= 0
end
local is_water = flowlib.is_water
local function is_water(pos)
return is_group(pos, "water")
end
local function is_ice(pos)
return is_group(pos, "ice")
@ -245,7 +247,7 @@ function boat.on_step(self, dtime, moveresult)
else
local ctrl = self._passenger:get_player_control()
if ctrl and ctrl.sneak then
detach_object(self._passenger, true)
detach_player(self._passenger, true)
self._passenger = nil
end
end

View File

@ -1,7 +1,7 @@
name = mcl_boats
author = PilzAdam
description = Adds drivable boats.
depends = mcl_player, flowlib
depends = mcl_player
optional_depends = mcl_core, doc_identifier

View File

@ -1,8 +1,5 @@
local S = minetest.get_translator("mcl_falling_nodes")
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
local has_mcl_armor = minetest.get_modpath("mcl_armor")
local is_creative_enabled = minetest.is_creative_enabled
local get_falling_depth = function(self)
if not self._startpos then
@ -16,8 +13,9 @@ local deal_falling_damage = function(self, dtime)
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
return
end
-- Cause damage to any entity it hits.
-- Cause damage to any player it hits.
-- Algorithm based on MC anvils.
-- TODO: Support smashing other objects, too.
local pos = self.object:get_pos()
if not self._startpos then
-- Fallback
@ -25,39 +23,30 @@ local deal_falling_damage = function(self, dtime)
end
local objs = minetest.get_objects_inside_radius(pos, 1)
for _,v in ipairs(objs) do
if v:is_player() then
local hp = v:get_hp()
local hp = v:get_hp()
if v:is_player() and hp ~= 0 then
if not self._hit_players then
self._hit_players = {}
end
local name = v:get_player_name()
if hp ~= 0 then
if not self._hit_players then
self._hit_players = {}
local hit = false
for _,v in ipairs(self._hit_players) do
if name == v then
hit = true
end
local hit = false
for _,v in ipairs(self._hit_players) do
if name == v then
hit = true
end
if not hit then
table.insert(self._hit_players, name)
local way = self._startpos.y - pos.y
local damage = (way - 1) * 2
damage = math.min(40, math.max(0, damage))
if damage >= 1 then
hp = hp - damage
if hp < 0 then
hp = 0
end
end
if not hit then
table.insert(self._hit_players, name)
local way = self._startpos.y - pos.y
local damage = (way - 1) * 2
damage = math.min(40, math.max(0, damage))
if damage >= 1 then
hp = hp - damage
if hp < 0 then
hp = 0
end
-- Reduce damage if wearing a helmet
local inv = v:get_inventory()
local helmet = inv:get_stack("armor", 2)
if has_mcl_armor and not helmet:is_empty() then
hp = hp/4*3
if not is_creative_enabled(name) then
helmet:add_wear(65535/helmet:get_definition().groups.mcl_armor_uses) --TODO: be sure damage is exactly like mc (informations are missing in the mc wiki)
inv:set_stack("armor", 2, helmet)
end
end
if v:is_player() then
-- TODO: Reduce damage if wearing a helmet
local msg
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
@ -67,35 +56,8 @@ local deal_falling_damage = function(self, dtime)
if dmes then
mcl_death_messages.player_damage(v, msg)
end
v:set_hp(hp, { type = "punch", from = "mod" })
end
end
end
else
local hp = v:get_luaentity().health
if hp and hp ~= 0 then
if not self._hit_mobs then
self._hit_mobs = {}
end
local hit = false
for _,mob in ipairs(self._hit_mobs) do
if v == mob then
hit = true
end
end
--TODO: reduce damage for mobs then they will be able to wear armor
if not hit then
table.insert(self._hit_mobs, v)
local way = self._startpos.y - pos.y
local damage = (way - 1) * 2
damage = math.min(40, math.max(0, damage))
if damage >= 1 then
hp = hp - damage
if hp < 0 then
hp = 0
end
v:get_luaentity().health = hp
end
v:set_hp(hp, { type = "punch", from = "mod" })
end
end
end

View File

@ -1,36 +1,10 @@
--these are lua locals, used for higher performance
local minetest,math,vector,ipairs = minetest,math,vector,ipairs
--this is used for the player pool in the sound buffer
local pool = {}
local tick = false
minetest.register_on_joinplayer(function(player)
local name
name = player:get_player_name()
pool[name] = 0
end)
minetest.register_on_leaveplayer(function(player)
local name
name = player:get_player_name()
pool[name] = nil
end)
local has_awards = minetest.get_modpath("awards")
local mcl_item_entity = {}
--basic settings
local item_drop_settings = {} --settings table
item_drop_settings.dug_buffer = 0.65 -- the warm up period before a dug item can be collected
item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting
item_drop_settings.radius_magnet = 2.0 --radius of item magnet. MUST BE LARGER THAN radius_collect!
item_drop_settings.xp_radius_magnet = 7.25 --radius of xp magnet. MUST BE LARGER THAN radius_collect!
item_drop_settings.radius_collect = 0.2 --radius of collection
item_drop_settings.player_collect_height = 0.8 --added to their pos y value
item_drop_settings.player_collect_height = 1.0 --added to their pos y value
item_drop_settings.collection_safety = false --do this to prevent items from flying away on laggy servers
item_drop_settings.random_item_velocity = true --this sets random item velocity if velocity is 0
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
@ -42,33 +16,16 @@ local get_gravity = function()
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
end
local registered_pickup_achievement = {}
--TODO: remove limitation of 1 award per itemname
function mcl_item_entity.register_pickup_achievement(itemname, award)
if not has_awards then
minetest.log("warning", "[mcl_item_entity] Trying to register pickup achievement ["..award.."] for ["..itemname.."] while awards missing")
elseif registered_pickup_achievement[itemname] then
minetest.log("error", "[mcl_item_entity] Trying to register already existing pickup achievement ["..award.."] for ["..itemname.."]")
else
registered_pickup_achievement[itemname] = award
end
end
mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
local check_pickup_achievements = function(object, player)
if has_awards then
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
local playername = player:get_player_name()
for name,award in pairs(registered_pickup_achievement) do
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
awards.unlock(playername, award)
end
end
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
if minetest.get_item_group(itemname, "tree") ~= 0 then
awards.unlock(player:get_player_name(), "mcl:mineWood")
elseif itemname == "mcl_mobitems:blaze_rod" then
awards.unlock(player:get_player_name(), "mcl:blazeRod")
elseif itemname == "mcl_mobitems:leather" then
awards.unlock(player:get_player_name(), "mcl:killCow")
elseif itemname == "mcl_core:diamond" then
awards.unlock(player:get_player_name(), "mcl:diamonds")
end
end
@ -96,71 +53,103 @@ local disable_physics = function(object, luaentity, ignore_check, reset_movement
end
end
minetest.register_globalstep(function(dtime)
tick = not tick
for _,player in pairs(minetest.get_connected_players()) do
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
local name = player:get_player_name()
local pos = player:get_pos()
if tick == true and pool[name] > 0 then
minetest.sound_play("item_drop_pickup", {
pos = pos,
gain = 0.7,
max_hear_distance = 16,
pitch = math.random(70,110)/100
})
if pool[name] > 6 then
pool[name] = 6
else
pool[name] = pool[name] - 1
end
end
local inv = player:get_inventory()
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
--magnet and collection
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
local collected = false
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
-- Collection
if not object:get_luaentity()._removed then
if vector.distance(checkpos, object:get_pos()) <= item_drop_settings.radius_collect and not object:get_luaentity()._removed then
-- Ignore if itemstring is not set yet
if object:get_luaentity().itemstring ~= "" then
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
minetest.sound_play("item_drop_pickup", {
pos = pos,
max_hear_distance = 16,
gain = 1.0,
}, true)
check_pickup_achievements(object, player)
-- Destroy entity
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
object:get_luaentity().target = checkpos
object:get_luaentity()._removed = true
object:set_velocity({x=0,y=0,z=0})
object:set_acceleration({x=0,y=0,z=0})
object:move_to(checkpos)
pool[name] = pool[name] + 1
minetest.after(0.25, function()
--safety check
if object and object:get_luaentity() then
object:remove()
end
end)
object:remove()
collected = true
end
-- Magnet
else
object:get_luaentity()._magnet_active = true
object:get_luaentity()._collector_timer = 0
-- Move object to player
disable_physics(object, object:get_luaentity())
local opos = object:get_pos()
local vec = vector.subtract(checkpos, opos)
vec = vector.add(opos, vector.divide(vec, 2))
object:move_to(vec)
--fix eternally falling items
minetest.after(0, function(object)
local lua = object:get_luaentity()
if lua then
object:set_acceleration({x=0, y=0, z=0})
end
end, object)
--this is a safety to prevent items flying away on laggy servers
if item_drop_settings.collection_safety == true then
if object:get_luaentity().init ~= true then
object:get_luaentity().init = true
minetest.after(1, function(args)
local playername = args[1]
local player = minetest.get_player_by_name(playername)
local object = args[2]
local lua = object:get_luaentity()
if player == nil or not player:is_player() or object == nil or lua == nil or lua.itemstring == nil then
return
end
if inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
if not object:get_luaentity()._removed then
minetest.sound_play("item_drop_pickup", {
pos = pos,
max_hear_distance = 16,
gain = 1.0,
}, true)
end
check_pickup_achievements(object, player)
object:get_luaentity()._removed = true
object:remove()
else
enable_physics(object, object:get_luaentity())
end
end, {player:get_player_name(), object})
end
end
end
end
if not collected then
if object:get_luaentity()._magnet_timer > 1 then
object:get_luaentity()._magnet_timer = -item_drop_settings.magnet_time
object:get_luaentity()._magnet_active = false
elseif object:get_luaentity()._magnet_timer < 0 then
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
end
end
@ -176,6 +165,66 @@ minetest.register_globalstep(function(dtime)
end
end)
local minigroups = { "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
local basegroups = { "pickaxey", "axey", "shovely" }
local materials = { "wood", "gold", "stone", "iron", "diamond" }
-- Checks if the given node would drop its useful drop if dug by a tool
-- with the given tool capabilities. Returns true if it will yield its useful
-- drop, false otherwise.
local check_can_drop = function(node_name, tool_capabilities)
local handy = minetest.get_item_group(node_name, "handy")
local dig_immediate = minetest.get_item_group(node_name, "dig_immediate")
if handy == 1 or dig_immediate == 2 or dig_immediate == 3 then
return true
else
local toolgroupcaps
if tool_capabilities then
toolgroupcaps = tool_capabilities.groupcaps
else
return false
end
-- Compare node groups with tool capabilities
for m=1, #minigroups do
local minigroup = minigroups[m]
local g = minetest.get_item_group(node_name, minigroup)
if g ~= 0 then
local plus = minigroup .. "_dig"
if toolgroupcaps[plus] then
return true
end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end
end
for b=1, #basegroups do
local basegroup = basegroups[b]
local g = minetest.get_item_group(node_name, basegroup)
if g ~= 0 then
for m=g, #materials do
local plus = basegroup .. "_dig_"..materials[m]
if toolgroupcaps[plus] then
return true
end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end
end
end
return false
end
end
-- Stupid workaround to get drops from a drop table:
-- Create a temporary table in minetest.registered_nodes that contains the proper drops,
-- because unfortunately minetest.get_node_drops needs the drop table to be inside a registered node definition
@ -220,33 +269,29 @@ local function get_fortune_drops(fortune_drops, fortune_level)
return drop or {}
end
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
function minetest.handle_node_drops(pos, drops, digger)
-- NOTE: This function override allows digger to be nil.
-- This means there is no digger. This is a special case which allows this function to be called
-- by hand. Creative Mode is intentionally ignored in this case.
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
return
end
-- Check if node will yield its useful drop by the digger's tool
local dug_node = minetest.get_node(pos)
local tooldef
local toolcaps
local tool
if digger ~= nil then
tool = digger:get_wielded_item()
tooldef = minetest.registered_tools[tool:get_name()]
toolcaps = tool:get_tool_capabilities()
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name()) then
if not check_can_drop(dug_node.name, toolcaps) then
return
end
end
local diggroups = tooldef and tooldef._mcl_diggroups
local shearsy_level = diggroups and diggroups.shearsy and diggroups.shearsy.level
--[[ Special node drops when dug by shears by reading _mcl_shears_drop or with a silk touch tool reading _mcl_silk_touch_drop
from the node definition.
Definition of _mcl_shears_drop / _mcl_silk_touch_drop:
@ -258,7 +303,7 @@ function minetest.handle_node_drops(pos, drops, digger)
local silk_touch_drop = false
local nodedef = minetest.registered_nodes[dug_node.name]
if shearsy_level and shearsy_level > 0 and nodedef._mcl_shears_drop then
if toolcaps ~= nil and toolcaps.groupcaps and toolcaps.groupcaps.shearsy_dig and nodedef._mcl_shears_drop then
if nodedef._mcl_shears_drop == true then
drops = { dug_node.name }
else
@ -326,10 +371,6 @@ function minetest.handle_node_drops(pos, drops, digger)
z = -z
end
obj:set_velocity({x=1/x, y=obj:get_velocity().y, z=1/z})
obj:get_luaentity().age = item_drop_settings.dug_buffer
obj:get_luaentity()._insta_collect = false
end
end
end
@ -396,9 +437,6 @@ minetest.register_entity(":__builtin:item", {
-- Number of seconds this item entity has existed so far
age = 0,
-- How old it has become in the collection animation
collection_age = 0,
set_item = function(self, itemstring)
self.itemstring = itemstring
if self.itemstring == "" then
@ -564,11 +602,6 @@ minetest.register_entity(":__builtin:item", {
on_step = function(self, dtime)
if self._removed then
self.object:set_properties({
physical = false
})
self.object:set_velocity({x=0,y=0,z=0})
self.object:set_acceleration({x=0,y=0,z=0})
return
end
self.age = self.age + dtime

View File

@ -1 +0,0 @@
Item_Drop_Pickup - https://freesound.org/people/benniknop/sounds/317848/ (License: CC0)

View File

@ -1,7 +1,5 @@
local S = minetest.get_translator("mcl_minecarts")
local has_mcl_wip = minetest.get_modpath("mcl_wip")
mcl_minecarts = {}
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
mcl_minecarts.speed_max = 10
@ -664,6 +662,8 @@ register_minecart(
"mcl_minecarts_minecart_chest.png",
{"mcl_minecarts:minecart", "mcl_chests:chest"},
nil, nil, false)
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
-- Minecart with Furnace
register_minecart(
@ -719,6 +719,8 @@ register_minecart(
end, nil, false
)
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
-- Minecart with Command Block
register_minecart(
"mcl_minecarts:command_block_minecart",
@ -740,6 +742,8 @@ register_minecart(
nil, nil, false
)
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
-- Minecart with Hopper
register_minecart(
"mcl_minecarts:hopper_minecart",
@ -758,6 +762,8 @@ register_minecart(
nil, nil, false
)
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
-- Minecart with TNT
register_minecart(
"mcl_minecarts:tnt_minecart",
@ -818,34 +824,29 @@ minetest.register_craft({
-- TODO: Re-enable crafting of special minecarts when they have been implemented
if false then
minetest.register_craft({
output = "mcl_minecarts:furnace_minecart",
recipe = {
{"mcl_furnaces:furnace"},
{"mcl_minecarts:minecart"},
},
})
minetest.register_craft({
output = "mcl_minecarts:hopper_minecart",
recipe = {
{"mcl_hoppers:hopper"},
{"mcl_minecarts:minecart"},
},
})
minetest.register_craft({
output = "mcl_minecarts:furnace_minecart",
recipe = {
{"mcl_furnaces:furnace"},
{"mcl_minecarts:minecart"},
},
})
minetest.register_craft({
output = "mcl_minecarts:hopper_minecart",
recipe = {
{"mcl_hoppers:hopper"},
{"mcl_minecarts:minecart"},
},
})
minetest.register_craft({
output = "mcl_minecarts:chest_minecart",
recipe = {
{"mcl_chests:chest"},
{"mcl_minecarts:minecart"},
},
})
minetest.register_craft({
output = "mcl_minecarts:chest_minecart",
recipe = {
{"mcl_chests:chest"},
{"mcl_minecarts:minecart"},
},
})
end
if has_mcl_wip then
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
end

View File

@ -1,6 +1,6 @@
name = mcl_minecarts
author = Krock
description = Minecarts are vehicles to move players quickly on rails.
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
optional_depends = doc_identifier, mcl_wip
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_wip
optional_depends = doc_identifier

View File

@ -61,6 +61,8 @@ end
-- Load settings
local damage_enabled = minetest.settings:get_bool("enable_damage")
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
@ -82,6 +84,11 @@ if minetest.settings:get_bool("only_peaceful_mobs", false) then
end)
end
-- calculate aoc range for mob count
local aosrb = tonumber(minetest.settings:get("active_object_send_range_blocks"))
local abr = tonumber(minetest.settings:get("active_block_range"))
local aoc_range = max(aosrb, abr) * 16
-- pathfinding settings
local enable_pathfinding = true
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
@ -276,33 +283,6 @@ local get_velocity = function(self)
return 0
end
local function update_roll(self)
local is_Fleckenstein = self.nametag == "Fleckenstein"
local was_Fleckenstein = false
local rot = self.object:get_rotation()
rot.z = is_Fleckenstein and pi or 0
self.object:set_rotation(rot)
local cbox = table.copy(self.collisionbox)
local acbox = self.object:get_properties().collisionbox
if math.abs(cbox[2] - acbox[2]) > 0.1 then
was_Fleckenstein = true
end
if is_Fleckenstein ~= was_Fleckenstein then
local pos = self.object:get_pos()
pos.y = pos.y + (acbox[2] + acbox[5])
self.object:set_pos(pos)
end
if is_Fleckenstein then
cbox[2], cbox[5] = -cbox[5], -cbox[2]
end
self.object:set_properties({collisionbox = cbox})
end
-- set and return valid yaw
local set_yaw = function(self, yaw, delay, dtime)
@ -318,7 +298,6 @@ local set_yaw = function(self, yaw, delay, dtime)
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
self.object:set_yaw(yaw)
update_roll(self)
return yaw
end
@ -666,9 +645,9 @@ local update_tag = function(self)
nametag = tag,
})
update_roll(self)
end
-- drop items
local item_drop = function(self, cooked, looting_level)
@ -728,9 +707,7 @@ local item_drop = function(self, cooked, looting_level)
end
-- add item if it exists
for x = 1, num do
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
end
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
if obj and obj:get_luaentity() then
@ -859,12 +836,10 @@ local check_for_death = function(self, cause, cmi_cause)
remove_texture_mod(self, "^[colorize:#FF000040")
remove_texture_mod(self, "^[brighten")
self.passive = true
self.object:set_properties({
pointable = false,
collide_with_objects = false,
})
set_velocity(self, 0)
local acc = self.object:get_acceleration()
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
@ -2814,10 +2789,6 @@ local do_states = function(self, dtime)
local arrow, ent
local v = 1
if not self.shoot_arrow then
self.firing = true
minetest.after(1, function()
self.firing = false
end)
arrow = minetest.add_entity(p, self.arrow)
ent = arrow:get_luaentity()
if ent.velocity then
@ -2855,7 +2826,7 @@ local falling = function(self, pos)
end
if mcl_portals ~= nil then
if mcl_portals.nether_portal_cooloff(self.object) then
if mcl_portals.nether_portal_cooloff[self.object] then
return false -- mob has teleported through Nether portal - it's 99% not falling
end
end
@ -2885,18 +2856,6 @@ local falling = function(self, pos)
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 / (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
@ -3516,11 +3475,18 @@ local mob_step = function(self, dtime)
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
self.object:set_yaw(yaw)
update_roll(self)
end
-- end rotation
-- knockback timer
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
return
end
-- run custom function (defined in mob lua file)
if self.do_custom then
@ -3530,14 +3496,6 @@ local mob_step = function(self, dtime)
end
end
-- knockback timer
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
return
end
-- attack timer
self.timer = self.timer + dtime
@ -3556,7 +3514,7 @@ local mob_step = function(self, dtime)
end
-- mob plays random sound at times
if random(1, 70) == 1 then
if random(1, 100) == 1 then
mob_sound(self, "random", true)
end
@ -3736,8 +3694,6 @@ function mobs:register_mob(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
@ -3817,7 +3773,6 @@ minetest.register_entity(name, {
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,
@ -3901,12 +3856,6 @@ minetest.register_entity(name, {
on_detach_child = mob_detach_child,
on_activate = function(self, staticdata, dtime)
--this is a temporary hack so mobs stop
--glitching and acting really weird with the
--default built in engine collision detection
self.object:set_properties({
collide_with_objects = false,
})
return mob_activate(self, staticdata, def, dtime)
end,
@ -3925,6 +3874,289 @@ end
end -- END mobs:register_mob function
-- count how many mobs of one type are inside an area
local count_mobs = function(pos, mobtype)
local num = 0
local objs = minetest.get_objects_inside_radius(pos, aoc_range)
for n = 1, #objs do
local obj = objs[n]:get_luaentity()
if obj and obj.name and obj._cmi_is_mob then
-- count passive mobs only
if mobtype == "!passive" then
if obj.spawn_class == "passive" then
num = num + 1
end
-- count hostile mobs only
elseif mobtype == "!hostile" then
if obj.spawn_class == "hostile" then
num = num + 1
end
-- count ambient mobs only
elseif mobtype == "!ambient" then
if obj.spawn_class == "ambient" then
num = num + 1
end
-- count water mobs only
elseif mobtype == "!water" then
if obj.spawn_class == "water" then
num = num + 1
end
-- count mob type
elseif mobtype and obj.name == mobtype then
num = num + 1
-- count total mobs
elseif not mobtype then
num = num + 1
end
end
end
return num
end
-- global functions
function mobs:spawn_abm_check(pos, node, name)
-- global function to add additional spawn checks
-- return true to stop spawning mob
end
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
-- Do mobs spawn at all?
if not mobs_spawn then
return
end
-- chance/spawn number override in minetest.conf for registered mob
local numbers = minetest.settings:get(name)
if numbers then
numbers = numbers:split(",")
chance = tonumber(numbers[1]) or chance
aoc = tonumber(numbers[2]) or aoc
if chance == 0 then
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
return
end
minetest.log("action",
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
end
local spawn_action
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
local orig_pos = table.copy(pos)
-- is mob actually registered?
if not mobs.spawning_mobs[name]
or not minetest.registered_entities[name] then
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
return
end
-- additional custom checks for spawning mob
if mobs:spawn_abm_check(pos, node, name) == true then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
return
end
-- count nearby mobs in same spawn class
local entdef = minetest.registered_entities[name]
local spawn_class = entdef and entdef.spawn_class
if not spawn_class then
if entdef.type == "monster" then
spawn_class = "hostile"
else
spawn_class = "passive"
end
end
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
-- do not spawn if too many of same mob in area
if active_object_count_wider >= max_per_block -- large-range mob cap
or (not in_class_cap) -- spawn class mob cap
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
-- too many entities
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
return
end
-- if toggle set to nil then ignore day/night check
if day_toggle ~= nil then
local tod = (minetest.get_timeofday() or 0) * 24000
if tod > 4500 and tod < 19500 then
-- daylight, but mob wants night
if day_toggle == false then
-- mob needs night
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
return
end
else
-- night time but mob wants day
if day_toggle == true then
-- mob needs day
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
return
end
end
end
-- spawn above node
pos.y = pos.y + 1
-- only spawn away from player
local objs = minetest.get_objects_inside_radius(pos, 24)
for n = 1, #objs do
if objs[n]:is_player() then
-- player too close
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
return
end
end
-- mobs cannot spawn in protected areas when enabled
if not spawn_protected
and minetest.is_protected(pos, "") then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
return
end
-- are we spawning within height limits?
if pos.y > max_height
or pos.y < min_height then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
return
end
-- are light levels ok?
local light = minetest.get_node_light(pos)
if not light
or light > max_light
or light < min_light then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
return
end
-- do we have enough space to spawn mob?
local ent = minetest.registered_entities[name]
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
local min_x, max_x
if width_x % 2 == 0 then
max_x = math.floor(width_x/2)
min_x = -(max_x-1)
else
max_x = math.floor(width_x/2)
min_x = -max_x
end
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
local min_z, max_z
if width_z % 2 == 0 then
max_z = math.floor(width_z/2)
min_z = -(max_z-1)
else
max_z = math.floor(width_z/2)
min_z = -max_z
end
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
for y = 0, max_y do
for x = min_x, max_x do
for z = min_z, max_z do
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
-- inside block
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
end
return
end
end
end
end
-- tweak X/Y/Z spawn pos
if width_x % 2 == 0 then
pos.x = pos.x + 0.5
end
if width_z % 2 == 0 then
pos.z = pos.z + 0.5
end
pos.y = pos.y - 0.5
local mob = minetest.add_entity(pos, name)
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
if on_spawn then
local ent = mob:get_luaentity()
on_spawn(ent, pos)
end
end
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
end
minetest.register_abm({
label = name .. " spawning",
nodenames = nodes,
neighbors = neighbors,
interval = interval,
chance = floor(max(1, chance * mobs_spawn_chance)),
catch_up = false,
action = spawn_abm_action,
})
end
-- compatibility with older mob registration
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
chance, active_object_count, -31000, max_height, day_toggle)
end
-- MarkBu's spawn function
function mobs:spawn(def)
local name = def.name
local nodes = def.nodes or {"group:soil", "group:stone"}
local neighbors = def.neighbors or {"air"}
local min_light = def.min_light or 0
local max_light = def.max_light or 15
local interval = def.interval or 30
local chance = def.chance or 5000
local active_object_count = def.active_object_count or 1
local min_height = def.min_height or -31000
local max_height = def.max_height or 31000
local day_toggle = def.day_toggle
local on_spawn = def.on_spawn
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
end
-- register arrow for shoot attack
function mobs:register_arrow(name, def)
@ -3947,11 +4179,6 @@ function mobs:register_arrow(name, def)
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 - (pi / 180)) or false,
@ -4014,7 +4241,7 @@ function mobs:register_arrow(name, def)
if self.hit_player or self.hit_mob or self.hit_object then
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do
if self.hit_player
and player:is_player() then
@ -4069,7 +4296,7 @@ end
-- make explosion with protection and tnt mod check
function mobs:boom(self, pos, strength, fire)
self.object:remove()
if mod_explosions then
if mobs_griefing and not minetest.is_protected(pos, "") then
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
@ -4350,7 +4577,6 @@ function mobs:alias_mob(old_name, new_name)
end
--[[
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
@ -4367,4 +4593,3 @@ minetest.register_globalstep(function(dtime)
end
timer = 0
end)
]]--

View File

@ -4,11 +4,8 @@ local path = minetest.get_modpath(minetest.get_current_modname())
-- Mob API
dofile(path .. "/api.lua")
-- Spawning Algorithm
dofile(path .. "/spawning.lua")
-- Rideable Mobs
dofile(path .. "/mount.lua")
-- Mob Items
dofile(path .. "/crafts.lua")
dofile(path .. "/crafts.lua")

View File

@ -168,20 +168,16 @@ function mobs.detach(player, offset)
mcl_player.player_set_animation(player, "stand" , 30)
--local pos = player:get_pos()
local pos = player:get_pos()
--pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
player:add_velocity(vector.new(math.random(-6,6),math.random(5,8),math.random(-6,6))) --throw the rider off
--[[
minetest.after(0.1, function(name, pos)
local player = minetest.get_player_by_name(name)
if player then
player:set_pos(pos)
end
end, player:get_player_name(), pos)
]]--
end

View File

@ -1,647 +0,0 @@
--lua locals
local get_node = minetest.get_node
local get_item_group = minetest.get_item_group
local get_node_light = minetest.get_node_light
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
local new_vector = vector.new
local math_random = math.random
local get_biome_name = minetest.get_biome_name
local max = math.max
local get_objects_inside_radius = minetest.get_objects_inside_radius
local vector_distance = vector.distance
-- range for mob count
local aoc_range = 32
--[[
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
underground:
"FlowerForest_underground",
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
ocean:
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean",
water or beach?
"MesaPlateauFM_sandlevel",
"MesaPlateauF_sandlevel",
"MesaBryce_sandlevel",
"Mesa_sandlevel",
beach:
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
dimension biome:
"Nether",
"End",
Overworld regular:
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"ExtremeHillsM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
]]--
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
-- count how many mobs of one type are inside an area
local count_mobs = function(pos,mobtype)
local num = 0
local objs = get_objects_inside_radius(pos, aoc_range)
for n = 1, #objs do
local obj = objs[n]:get_luaentity()
if obj and obj.name and obj._cmi_is_mob then
-- count hostile mobs only
if mobtype == "hostile" then
if obj.spawn_class == "hostile" then
num = num + 1
end
-- count passive mobs only
else
num = num + 1
end
end
end
return num
end
-- global functions
function mobs:spawn_abm_check(pos, node, name)
-- global function to add additional spawn checks
-- return true to stop spawning mob
end
--[[
Custom elements changed:
name:
the mobs name
dimension:
"overworld"
"nether"
"end"
types of spawning:
"water"
"ground"
"lava"
biomes: tells the spawner to allow certain mobs to spawn in certain biomes
{"this", "that", "grasslands", "whatever"}
what is aoc??? objects in area
WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
]]--
--this is where all of the spawning information is kept
local spawn_dictionary = {}
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
--print(dump(biomes))
-- Do mobs spawn at all?
if not mobs_spawn then
return
end
-- chance/spawn number override in minetest.conf for registered mob
local numbers = minetest.settings:get(name)
if numbers then
numbers = numbers:split(",")
chance = tonumber(numbers[1]) or chance
aoc = tonumber(numbers[2]) or aoc
if chance == 0 then
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
return
end
minetest.log("action",
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
end
--[[
local spawn_action
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
local orig_pos = table.copy(pos)
-- is mob actually registered?
if not mobs.spawning_mobs[name]
or not minetest.registered_entities[name] then
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
return
end
-- additional custom checks for spawning mob
if mobs:spawn_abm_check(pos, node, name) == true then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
return
end
-- count nearby mobs in same spawn class
local entdef = minetest.registered_entities[name]
local spawn_class = entdef and entdef.spawn_class
if not spawn_class then
if entdef.type == "monster" then
spawn_class = "hostile"
else
spawn_class = "passive"
end
end
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
-- do not spawn if too many of same mob in area
if active_object_count_wider >= max_per_block -- large-range mob cap
or (not in_class_cap) -- spawn class mob cap
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
-- too many entities
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
return
end
-- if toggle set to nil then ignore day/night check
if day_toggle ~= nil then
local tod = (minetest.get_timeofday() or 0) * 24000
if tod > 4500 and tod < 19500 then
-- daylight, but mob wants night
if day_toggle == false then
-- mob needs night
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
return
end
else
-- night time but mob wants day
if day_toggle == true then
-- mob needs day
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
return
end
end
end
-- spawn above node
pos.y = pos.y + 1
-- only spawn away from player
local objs = minetest.get_objects_inside_radius(pos, 24)
for n = 1, #objs do
if objs[n]:is_player() then
-- player too close
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
return
end
end
-- mobs cannot spawn in protected areas when enabled
if not spawn_protected
and minetest.is_protected(pos, "") then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
return
end
-- are we spawning within height limits?
if pos.y > max_height
or pos.y < min_height then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
return
end
-- are light levels ok?
local light = minetest.get_node_light(pos)
if not light
or light > max_light
or light < min_light then
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
return
end
-- do we have enough space to spawn mob?
local ent = minetest.registered_entities[name]
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
local min_x, max_x
if width_x % 2 == 0 then
max_x = math.floor(width_x/2)
min_x = -(max_x-1)
else
max_x = math.floor(width_x/2)
min_x = -max_x
end
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
local min_z, max_z
if width_z % 2 == 0 then
max_z = math.floor(width_z/2)
min_z = -(max_z-1)
else
max_z = math.floor(width_z/2)
min_z = -max_z
end
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
for y = 0, max_y do
for x = min_x, max_x do
for z = min_z, max_z do
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
-- inside block
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
end
return
end
end
end
end
-- tweak X/Y/Z spawn pos
if width_x % 2 == 0 then
pos.x = pos.x + 0.5
end
if width_z % 2 == 0 then
pos.z = pos.z + 0.5
end
pos.y = pos.y - 0.5
local mob = minetest.add_entity(pos, name)
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
if on_spawn then
local ent = mob:get_luaentity()
on_spawn(ent, pos)
end
end
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
end
]]--
local entdef = minetest.registered_entities[name]
local spawn_class
if entdef.type == "monster" then
spawn_class = "hostile"
else
spawn_class = "passive"
end
--load information into the spawn dictionary
local key = #spawn_dictionary + 1
spawn_dictionary[key] = {}
spawn_dictionary[key]["name"] = name
spawn_dictionary[key]["dimension"] = dimension
spawn_dictionary[key]["type_of_spawning"] = type_of_spawning
spawn_dictionary[key]["biomes"] = biomes
spawn_dictionary[key]["min_light"] = min_light
spawn_dictionary[key]["max_light"] = max_light
spawn_dictionary[key]["interval"] = interval
spawn_dictionary[key]["chance"] = chance
spawn_dictionary[key]["aoc"] = aoc
spawn_dictionary[key]["min_height"] = min_height
spawn_dictionary[key]["max_height"] = max_height
spawn_dictionary[key]["day_toggle"] = day_toggle
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
spawn_dictionary[key]["spawn_class"] = spawn_class
--[[
minetest.register_abm({
label = name .. " spawning",
nodenames = nodes,
neighbors = neighbors,
interval = interval,
chance = floor(max(1, chance * mobs_spawn_chance)),
catch_up = false,
action = spawn_abm_action,
})
]]--
end
-- compatibility with older mob registration
-- we're going to forget about this for now -j4i
--[[
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
chance, active_object_count, -31000, max_height, day_toggle)
end
]]--
--Don't disable this yet-j4i
-- MarkBu's spawn function
function mobs:spawn(def)
--does nothing for now
--[[
local name = def.name
local nodes = def.nodes or {"group:soil", "group:stone"}
local neighbors = def.neighbors or {"air"}
local min_light = def.min_light or 0
local max_light = def.max_light or 15
local interval = def.interval or 30
local chance = def.chance or 5000
local active_object_count = def.active_object_count or 1
local min_height = def.min_height or -31000
local max_height = def.max_height or 31000
local day_toggle = def.day_toggle
local on_spawn = def.on_spawn
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
]]--
end
local axis
--inner and outer part of square donut radius
local inner = 1
local outer = 65
local int = {-1,1}
local position_calculation = function(pos)
pos = vector.floor(pos)
--this is used to determine the axis buffer from the player
axis = math.random(0,1)
--cast towards the direction
if axis == 0 then --x
pos.x = pos.x + math.random(inner,outer)*int[math.random(1,2)]
pos.z = pos.z + math.random(-outer,outer)
else --z
pos.z = pos.z + math.random(inner,outer)*int[math.random(1,2)]
pos.x = pos.x + math.random(-outer,outer)
end
return(pos)
end
--[[
local decypher_limits_dictionary = {
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
}
]]--
local function decypher_limits(posy)
--local min_max_table = decypher_limits_dictionary[dimension]
--return min_max_table[1],min_max_table[2]
posy = math.floor(posy)
return posy - 32, posy + 32
end
--a simple helper function for mob_spawn
local function biome_check(biome_list, biome_goal)
for _,data in ipairs(biome_list) do
if data == biome_goal then
return true
end
end
return false
end
--todo mob limiting
--MAIN LOOP
if mobs_spawn then
local timer = 0
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer >= 8 then
timer = 0
for _,player in pairs(minetest.get_connected_players()) do
for i = 1,math_random(3,8) do
repeat -- after this line each "break" means "continue"
local player_pos = player:get_pos()
local _,dimension = mcl_worlds.y_to_layer(player_pos.y)
if dimension == "void" or dimension == "default" then
break -- ignore void and unloaded area
end
local min,max = decypher_limits(player_pos.y)
local goal_pos = position_calculation(player_pos)
local spawning_position_list = find_nodes_in_area_under_air(new_vector(goal_pos.x,min,goal_pos.z), vector.new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
--couldn't find node
if #spawning_position_list <= 0 then
break
end
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
--Prevent strange behavior/too close to player
if not spawning_position or vector_distance(player_pos, spawning_position) < 15 then
break
end
local gotten_node = get_node(spawning_position).name
if not gotten_node or gotten_node == "air" then --skip air nodes
break
end
local gotten_biome = minetest.get_biome_data(spawning_position)
if not gotten_biome then
break --skip if in unloaded area
end
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
--grab random mob
local mob_def = spawn_dictionary[math.random(1,#spawn_dictionary)]
if not mob_def then
break --skip if something ridiculous happens (nil mob def)
end
--skip if not correct dimension
if mob_def.dimension ~= dimension then
break
end
--skip if not in correct biome
if not biome_check(mob_def.biomes, gotten_biome) then
break
end
--add this so mobs don't spawn inside nodes
spawning_position.y = spawning_position.y + 1
if spawning_position.y < mob_def.min_height or spawning_position.y > mob_def.max_height then
break
end
--only need to poll for node light if everything else worked
local gotten_light = get_node_light(spawning_position)
--don't spawn if not in light limits
if gotten_light < mob_def.min_light or gotten_light > mob_def.max_light then
break
end
local is_water = get_item_group(gotten_node, "water") ~= 0
local is_lava = get_item_group(gotten_node, "lava") ~= 0
if mob_def.type_of_spawning == "ground" and is_water then
break
end
if mob_def.type_of_spawning == "ground" and is_lava then
break
end
--finally do the heavy check (for now) of mobs in area
if count_mobs(spawning_position, mob_def.spawn_class) >= mob_def.aoc then
break
end
--adjust the position for water and lava mobs
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
spawning_position.y = spawning_position.y - 1
end
--everything is correct, spawn mob
minetest.add_entity(spawning_position, mob_def.name)
until true --this is a safety catch
end
end
end
end)
end

View File

@ -290,13 +290,13 @@ mobs_mc.spawn = {
mobs_mc.spawn_height = {
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
-- Overworld boundaries (inclusive) --I adjusted this to be more reasonable
overworld_min = -64,-- -2999,
-- Overworld boundaries (inclusive)
overworld_min = -2999,
overworld_max = 31000,
-- Nether boundaries (inclusive)
nether_min = -29067,-- -3369,
nether_max = -28939,-- -3000,
nether_min = -3369,
nether_max = -3000,
-- End boundaries (inclusive)
end_min = -6200,

View File

@ -521,7 +521,7 @@ if c("totem") then
-- Totem of Undying
minetest.register_craftitem("mobs_mc:totem", {
description = S("Totem of Undying"),
_tt_help = minetest.colorize(mcl_colors.GREEN, S("Protects you from death while wielding it")),
_tt_help = minetest.colorize("#00FF00", S("Protects you from death while wielding it")),
_doc_items_longdesc = S("A totem of undying is a rare artifact which may safe you from certain death."),
_doc_items_usagehelp = S("The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however."),
inventory_image = "mcl_totems_totem.png",

View File

@ -1,5 +1,5 @@
--###################
--################### AGENT - seemingly unused
--################### AGENT
--###################
local S = minetest.get_translator("mobs_mc")

View File

@ -64,81 +64,7 @@ else
end
-- Spawn on solid blocks at or below Sea level and the selected light level
mobs:spawn_specific(
"mobs_mc:bat",
"overworld",
"ground",
{
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"ExtremeHillsM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
},
0,
maxlight,
20,
5000,
2,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.water-1)
mobs:spawn_specific("mobs_mc:bat", mobs_mc.spawn.solid, {"air"}, 0, maxlight, 20, 5000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-1)
-- spawn eggs

View File

@ -1,6 +1,6 @@
-- daufinsyd
-- My work is under the LGPL terms
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models -hi 22i ~jordan4ibanez
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models
-- blaze.lua partial copy of mobs_mc/ghast.lua
local S = minetest.get_translator("mobs_mc")
@ -75,71 +75,9 @@ mobs:register_mob("mobs_mc:blaze", {
fear_height = 0,
glow = 14,
fire_resistant = true,
do_custom = function(self)
if self.state == "attack" and vector.distance(self.object:get_pos(), self.attack:get_pos()) < 1.2 then
mcl_burning.set_on_fire(self.attack, 5)
end
local pos = self.object:get_pos()
minetest.add_particle({
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,
vertical = false,
texture = "mcl_particles_smoke_anim.png^[colorize:#2c2c2c:255",
animation = {
type = "vertical_frames",
aspect_w = 8,
aspect_h = 8,
length = 2.05,
},
})
minetest.add_particle({
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,
vertical = false,
texture = "mcl_particles_smoke_anim.png^[colorize:#424242:255",
animation = {
type = "vertical_frames",
aspect_w = 8,
aspect_h = 8,
length = 2.05,
},
})
minetest.add_particle({
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
velocity = {x=0, y=math.random(1,1), z=0},
expirationtime = math.random(),
size = math.random(1, 4),
collisiondetection = true,
vertical = false,
texture = "mcl_particles_smoke_anim.png^[colorize:#0f0f0f:255",
animation = {
type = "vertical_frames",
aspect_w = 8,
aspect_h = 8,
length = 2.05,
},
})
end,
})
mobs:spawn_specific(
"mobs_mc:blaze",
"nether",
"ground",
{"Nether"},
0,
minetest.LIGHT_MAX+1,
30,
5000,
3,
mobs_mc.spawn_height.nether_min,
mobs_mc.spawn_height.nether_max)
mobs:spawn_specific("mobs_mc:blaze", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
-- Blaze fireball
mobs:register_arrow("mobs_mc:blaze_fireball", {

View File

@ -100,34 +100,7 @@ mobs:register_mob("mobs_mc:chicken", {
})
--spawn
mobs:spawn_specific(
"mobs_mc:chicken",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
9,
minetest.LIGHT_MAX+1,
30, 17000,
3,
mobs_mc.spawn_height.water,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:chicken", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)

View File

@ -145,53 +145,8 @@ mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
-- Spawning
mobs:spawn_specific(
"mobs_mc:cow",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
9,
minetest.LIGHT_MAX+1,
30,
17000,
10,
mobs_mc.spawn_height.water,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific(
"mobs_mc:mooshroom",
"overworld",
"ground",
{
"MushroomIslandShore",
"MushroomIsland"
},
9,
minetest.LIGHT_MAX+1,
30,
17000,
5,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:cow", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 10, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:mooshroom", mobs_mc.spawn.mushroom_island, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 5, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn egg
mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)

View File

@ -39,8 +39,6 @@ mobs:register_mob("mobs_mc:creeper", {
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
attack_type = "explode",
--hssssssssssss
explosion_strength = 3,
explosion_radius = 3.5,
explosion_damage_radius = 3.5,
@ -78,6 +76,7 @@ mobs:register_mob("mobs_mc:creeper", {
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
self.object:remove()
end
end
end,
@ -140,9 +139,6 @@ mobs:register_mob("mobs_mc:creeper_charged", {
pathfinding = 1,
visual = "mesh",
mesh = "mobs_mc_creeper.b3d",
--BOOM
textures = {
{"mobs_mc_creeper.png",
"mobs_mc_creeper_charge.png"},
@ -199,6 +195,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
if self._forced_explosion_countdown_timer <= 0 then
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
self.object:remove()
end
end
end,
@ -253,158 +250,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
glow = 3,
})
mobs:spawn_specific(
"mobs_mc:creeper",
"overworld",
"ground",
{
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"ExtremeHillsM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
"MesaPlateauFM_sandlevel",
"MesaPlateauF_sandlevel",
"MesaBryce_sandlevel",
"Mesa_sandlevel",
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean",
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
7,
20,
16500,
2,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:creeper", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 16500, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)

View File

@ -1 +0,0 @@
mcl_mobs

View File

@ -50,8 +50,8 @@ mobs:register_mob("mobs_mc:enderdragon", {
arrow = "mobs_mc:dragon_fireball",
shoot_interval = 0.5,
shoot_offset = -1.0,
xp_min = 500,
xp_max = 500,
xp_min = 12000,
xp_max = 12000,
animation = {
fly_speed = 8, stand_speed = 8,
stand_start = 0, stand_end = 20,
@ -59,47 +59,15 @@ mobs:register_mob("mobs_mc:enderdragon", {
run_start = 0, run_end = 20,
},
ignores_nametag = true,
do_custom = function(self)
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_end:crystal" then
if luaentity.beam then
if luaentity.beam == self.beam then
break
end
else
if self.beam then
self.beam:remove()
end
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
break
end
end
end
if self._portal_pos then
-- migrate old format
if type(self._portal_pos) == "string" then
self._portal_pos = minetest.string_to_pos(self._portal_pos)
end
local portal_center = vector.add(self._portal_pos, vector.new(3, 11, 3))
local pos = self.object:get_pos()
if vector.distance(pos, portal_center) > 50 then
self.object:set_pos(self._last_good_pos or portal_center)
else
self._last_good_pos = pos
end
end
end,
on_die = function(self, pos)
if self._portal_pos then
mcl_portals.spawn_gateway_portal()
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
if self._initial then
mcl_experience.throw_experience(pos, 11500) -- 500 + 11500 = 12000
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
end
on_die = function(self, own_pos)
if self._egg_spawn_pos then
local pos = minetest.string_to_pos(self._egg_spawn_pos)
--if minetest.get_node(pos).buildable_to then
minetest.set_node(pos, {name = mobs_mc.items.dragon_egg})
return
--end
end
minetest.add_item(own_pos, mobs_mc.items.dragon_egg)
end,
fire_resistant = true,
})

View File

@ -295,8 +295,7 @@ mobs:register_mob("mobs_mc:enderman", {
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
-- Check for arrows and people nearby.
local enderpos = self.object:get_pos()
enderpos.y = enderpos.y + 1.5
local objs = minetest.get_objects_inside_radius(enderpos, 2)
local objs = minetest.get_objects_inside_radius(enderpos, 4)
for n = 1, #objs do
local obj = objs[n]
if obj then
@ -308,7 +307,7 @@ mobs:register_mob("mobs_mc:enderman", {
else
local lua = obj:get_luaentity()
if lua then
if lua.name == "mcl_bows:arrow_entity" or lua.name == "mcl_throwing:snowball_entity" then
if lua.name == "mcl_bows:arrow_entity" then
self:teleport(nil)
end
end
@ -323,50 +322,41 @@ mobs:register_mob("mobs_mc:enderman", {
-- self:teleport(nil)
-- self.state = ""
--else
if self.attack ~= nil and not minetest.settings:get_bool("creative_mode") then
if self.attack ~= nil then
self.state = 'attack'
end
--end
end
-- Check to see if people are near by enough to look at us.
for _,obj in pairs(minetest.get_connected_players()) do
--check if they are within radius
local player_pos = obj:get_pos()
if player_pos then -- prevent crashing in 1 in a million scenario
local ender_distance = vector.distance(enderpos, player_pos)
if ender_distance <= 64 then
local objs = minetest.get_objects_inside_radius(enderpos, 64)
local obj
for n = 1, #objs do
obj = objs[n]
if obj then
if minetest.is_player(obj) then
-- Check if they are looking at us.
local player_pos = obj:get_pos()
local look_dir_not_normalized = obj:get_look_dir()
local look_dir = vector.normalize(look_dir_not_normalized)
local player_eye_height = obj:get_properties().eye_height
--skip player if they have no data - log it
if not player_eye_height then
minetest.log("error", "Enderman at location: ".. dump(enderpos).." has indexed a null player!")
else
--calculate very quickly the exact location the player is looking
--within the distance between the two "heads" (player and enderman)
local look_pos = vector.new(player_pos.x, player_pos.y + player_eye_height, player_pos.z)
local look_pos_base = look_pos
local ender_eye_pos = vector.new(enderpos.x, enderpos.y + 2.75, enderpos.z)
local eye_distance_from_player = vector.distance(ender_eye_pos, look_pos)
look_pos = vector.add(look_pos, vector.multiply(look_dir, eye_distance_from_player))
--if looking in general head position, turn hostile
if minetest.line_of_sight(ender_eye_pos, look_pos_base) and vector.distance(look_pos, ender_eye_pos) <= 0.4 then
local look_pos = vector.new({x = look_dir.x+player_pos.x, y = look_dir.y+player_pos.y + 1.5, z = look_dir.z+player_pos.z}) -- Arbitrary value (1.5) is head level according to player info mod.
-- Cast up to 64 to see if player is looking at enderman.
for n = 1,64,.25 do
local node = minetest.get_node(look_pos)
if node.name ~= "air" then
break
end
if look_pos.x-1<enderpos.x and look_pos.x+1>enderpos.x and look_pos.y-2.89<enderpos.y and look_pos.y-2>enderpos.y and look_pos.z-1<enderpos.z and look_pos.z+1>enderpos.z then
self.provoked = "staring"
self.attack = minetest.get_player_by_name(obj:get_player_name())
break
else -- I'm not sure what this part does, but I don't want to break anything - jordan4ibanez
else
if self.provoked == "staring" then
self.provoked = "broke_contact"
end
end
end
look_pos.x = look_pos.x + (.25 * look_dir.x)
look_pos.y = look_pos.y + (.25 * look_dir.y)
look_pos.z = look_pos.z + (.25 * look_dir.z)
end
end
end
@ -544,11 +534,9 @@ mobs:register_mob("mobs_mc:enderman", {
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
-- self:teleport(nil)
--else
if pr:next(1, 8) == 8 then --FIXME: real mc rate
self:teleport(hitter)
end
self.attack=hitter
self.state="attack"
self.attack=hitter
self.state="attack"
--end
end
end,
@ -561,189 +549,11 @@ mobs:register_mob("mobs_mc:enderman", {
-- End spawn
mobs:spawn_specific(
"mobs_mc:enderman",
"end",
"ground",
{
"End"
},
0,
minetest.LIGHT_MAX+1,
30,
3000,
12,
mobs_mc.spawn_height.end_min,
mobs_mc.spawn_height.end_max)
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 3000, 12, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
-- Overworld spawn
mobs:spawn_specific(
"mobs_mc:enderman",
"overworld",
"ground",
{
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"ExtremeHillsM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
"MesaPlateauFM_sandlevel",
"MesaPlateauF_sandlevel",
"MesaBryce_sandlevel",
"Mesa_sandlevel",
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean",
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
7,
30,
19000,
2,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 19000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- Nether spawn (rare)
mobs:spawn_specific(
"mobs_mc:enderman",
"nether",
"ground",
{
"Nether"
},
0,
7,
30,
27500,
4,
mobs_mc.spawn_height.nether_min,
mobs_mc.spawn_height.nether_max)
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 27500, 4, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
-- spawn eggs
mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)

View File

@ -63,32 +63,10 @@ mobs:register_mob("mobs_mc:ghast", {
makes_footstep_sound = false,
instant_death = true,
fire_resistant = true,
do_custom = function(self)
if self.firing == true then
self.base_texture = {"mobs_mc_ghast_firing.png"}
self.object:set_properties({textures=self.base_texture})
else
self.base_texture = {"mobs_mc_ghast.png"}
self.object:set_properties({textures=self.base_texture})
end
end,
})
mobs:spawn_specific(
"mobs_mc:ghast",
"nether",
"ground",
{
"Nether"
},
0,
minetest.LIGHT_MAX+1,
30,
18000,
2,
mobs_mc.spawn_height.nether_min,
mobs_mc.spawn_height.nether_max)
mobs:spawn_specific("mobs_mc:ghast", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 18000, 2, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
-- fireball (projectile)
mobs:register_arrow("mobs_mc:fireball", {
@ -96,7 +74,6 @@ mobs:register_arrow("mobs_mc:fireball", {
visual_size = {x = 1, y = 1},
textures = {"mcl_fire_fire_charge.png"},
velocity = 15,
collisionbox = {-.5, -.5, -.5, .5, .5, .5},
hit_player = function(self, player)
if rawget(_G, "armor") and armor.last_damage_types then

View File

@ -106,7 +106,7 @@ mobs:register_mob("mobs_mc:guardian_elder", {
view_range = 16,
})
-- Spawning disabled due to size issues <- what do you mean? -j4i
-- Spawning disabled due to size issues
-- TODO: Re-enable spawning
-- mobs:spawn_specific("mobs_mc:guardian_elder", mobs_mc.spawn.water, mobs_mc.spawn_water, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-18)

View File

@ -157,29 +157,8 @@ local horse = {
self._regentimer = 0
end
-- Some weird human is riding. Buck them off?
if self.driver and not self.tamed and self.buck_off_time <= 0 then
if math.random() < 0.2 then
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
-- TODO bucking animation
else
-- Nah, can't be bothered. Think about it again in one second
self.buck_off_time = 20
end
end
-- Tick the timer for trying to buck the player off
if self.buck_off_time then
if self.driver then
self.buck_off_time = self.buck_off_time - 1
else
-- Player isn't riding anymore so no need to count
self.buck_off_time = nil
end
end
-- if driver present and horse has a saddle allow control of horse
if self.driver and self._saddle then
-- if driver present allow control of horse
if self.driver then
mobs.drive(self, "walk", "stand", false, dtime)
@ -212,49 +191,6 @@ local horse = {
local item = clicker:get_wielded_item()
local iname = item:get_name()
local heal = 0
-- Taming
self.temper = self.temper or (math.random(1,100))
if not self.tamed then
local temper_increase = 0
-- Feeding, intentionally not using mobs:feed_tame because horse taming is
-- different and more complicated
if (iname == mobs_mc.items.sugar) then
temper_increase = 3
elseif (iname == mobs_mc.items.wheat) then
temper_increase = 3
elseif (iname == mobs_mc.items.apple) then
temper_increase = 3
elseif (iname == mobs_mc.items.golden_carrot) then
temper_increase = 5
elseif (iname == mobs_mc.items.golden_apple) then
temper_increase = 10
-- Trying to ride
elseif not self.driver then
self.object:set_properties({stepheight = 1.1})
mobs.attach(self, clicker)
self.buck_off_time = 40 -- TODO how long does it take in minecraft?
if self.temper > 100 then
self.tamed = true -- NOTE taming can only be finished by riding the horse
if not self.owner or self.owner == "" then
self.owner = clicker:get_player_name()
end
end
temper_increase = 5
-- Clicking on the horse while riding ==> unmount
elseif self.driver and self.driver == clicker then
mobs.detach(clicker, {x = 1, y = 0, z = 1})
end
-- If nothing happened temper_increase = 0 and addition does nothing
self.temper = self.temper + temper_increase
return
end
if can_breed(self.name) then
-- Breed horse with golden apple or golden carrot
if (iname == mobs_mc.items.golden_apple) then
@ -266,8 +202,7 @@ local horse = {
return
end
end
-- Feed with anything else
-- TODO heal amounts don't work
-- Feed/tame with anything else
if (iname == mobs_mc.items.sugar) then
heal = 1
elseif (iname == mobs_mc.items.wheat) then
@ -277,7 +212,7 @@ local horse = {
elseif (iname == mobs_mc.items.hay_bale) then
heal = 20
end
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, false) then
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, true) then
return
end
@ -510,60 +445,12 @@ mobs:register_mob("mobs_mc:mule", mule)
--===========================
--Spawn Function
mobs:spawn_specific(
"mobs_mc:horse",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
4,
mobs_mc.spawn_height.water+3,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific(
"mobs_mc:donkey",
"overworld",
"ground",
{
"Mesa",
"MesaPlateauFM_grasstop",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
4,
mobs_mc.spawn_height.water+3,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:horse", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:donkey", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
--mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)

View File

@ -217,25 +217,7 @@ mobs:register_mob("mobs_mc:llama", {
})
--spawn
mobs:spawn_specific(
"mobs_mc:llama",
"overworld",
"ground",
{
"Mesa",
"MesaPlateauFM_grasstop",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
5,
mobs_mc.spawn_height.water+15,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:llama", mobs_mc.spawn.savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 5, mobs_mc.spawn_height.water+15, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)

View File

@ -1,6 +1,6 @@
name = mobs_mc
author = maikerumine
description = Adds Minecraft-like monsters and animals.
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_colors
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items

View File

@ -152,25 +152,6 @@ mobs:register_mob("mobs_mc:cat", cat)
local base_spawn_chance = 5000
-- Spawn ocelot
--they get the same as the llama because I'm trying to rework so much of this code right now -j4i
mobs:spawn_specific(
"mobs_mc:ocelot",
"overworld",
"ground",
{
"Jungle",
"JungleEdgeM",
"JungleM",
"JungleEdge",
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
5,
mobs_mc.spawn_height.water+15,
mobs_mc.spawn_height.overworld_max)
--[[
mobs:spawn({
name = "mobs_mc:ocelot",
nodes = mobs_mc.spawn.jungle,
@ -182,8 +163,8 @@ mobs:spawn({
min_height = mobs_mc.spawn_height.water+1, -- Right above ocean level
max_height = mobs_mc.spawn_height.overworld_max,
on_spawn = function(self, pos)
Note: Minecraft has a 1/3 spawn failure rate.
In this mod it is emulated by reducing the spawn rate accordingly (see above).
--[[ Note: Minecraft has a 1/3 spawn failure rate.
In this mod it is emulated by reducing the spawn rate accordingly (see above). ]]
-- 1/7 chance to spawn 2 ocelot kittens
if pr:next(1,7) == 1 then
@ -226,7 +207,6 @@ mobs:spawn({
end
end,
})
]]--
-- spawn eggs
-- FIXME: The spawn icon shows a cat texture, not an ocelot texture

View File

@ -90,24 +90,8 @@ mobs:register_mob("mobs_mc:parrot", {
})
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome* <- I'll get to this eventually -j4i
mobs:spawn_specific(
"mobs_mc:parrot",
"overworld",
"ground",
{
"Jungle",
"JungleEdgeM",
"JungleM",
"JungleEdge",
},
0,
minetest.LIGHT_MAX+1,
7,
30000,
1,
mobs_mc.spawn_height.water+7,
mobs_mc.spawn_height.overworld_max)
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome*
mobs:spawn_specific("mobs_mc:parrot", {"mcl_core:jungletree", "mcl_core:jungleleaves"}, {"air"}, 0, minetest.LIGHT_MAX+1, 7, 30000, 1, mobs_mc.spawn_height.water+7, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:parrot", S("Parrot"), "mobs_mc_spawn_icon_parrot.png", 0)

View File

@ -182,35 +182,7 @@ mobs:register_mob("mobs_mc:pig", {
end,
})
mobs:spawn_specific(
"mobs_mc:pig",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
9,
minetest.LIGHT_MAX+1,
30,
15000,
8,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:pig", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 15000, 8, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:pig", S("Pig"), "mobs_mc_spawn_icon_pig.png", 0)

View File

@ -67,23 +67,7 @@ mobs:register_mob("mobs_mc:polar_bear", {
})
mobs:spawn_specific(
"mobs_mc:polar_bear",
"overworld",
"ground",
{
"ColdTaiga",
"IcePlainsSpikes",
"IcePlains",
"ExtremeHills+_snowtop",
},
0,
minetest.LIGHT_MAX+1,
30,
7000,
3,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:polar_bear", mobs_mc.spawn.snow, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 7000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn egg
mobs:register_egg("mobs_mc:polar_bear", S("Polar Bear"), "mobs_mc_spawn_icon_polarbear.png", 0)

View File

@ -107,39 +107,8 @@ end
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
-- Mob spawning rules.
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
-- Different skins depending on spawn location
mobs:spawn_specific(
"mobs_mc:rabbit",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
9,
minetest.LIGHT_MAX+1,
30,
15000,
8,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
--[[
local spawn = {
name = "mobs_mc:rabbit",
neighbors = {"air"},
@ -196,7 +165,6 @@ spawn_grass.on_spawn = function(self, pos)
self.object:set_properties({textures = self.base_texture})
end
mobs:spawn(spawn_grass)
]]--
-- Spawn egg
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)

View File

@ -25,19 +25,6 @@ local colors = {
unicolor_black = { mobs_mc.items.wool_black, "#000000D0" },
}
local rainbow_colors = {
"unicolor_light_red",
"unicolor_red",
"unicolor_orange",
"unicolor_yellow",
"unicolor_green",
"unicolor_dark_green",
"unicolor_light_blue",
"unicolor_blue",
"unicolor_violet",
"unicolor_red_violet"
}
if minetest.get_modpath("mcl_wool") ~= nil then
colors["unicolor_light_blue"] = { mobs_mc.items.wool_light_blue, "#5050FFD0" }
end
@ -125,7 +112,7 @@ mobs:register_mob("mobs_mc:sheep", {
end,
-- Set random color on spawn
do_custom = function(self, dtime)
do_custom = function(self)
if not self.initial_color_set then
local r = math.random(0,100000)
local textures
@ -162,35 +149,8 @@ mobs:register_mob("mobs_mc:sheep", {
}
self.initial_color_set = true
end
local is_kay27 = self.nametag == "kay27"
if self.color_change_timer then
local old_color = self.color
if is_kay27 then
self.color_change_timer = self.color_change_timer - dtime
if self.color_change_timer < 0 then
self.color_change_timer = 0.5
self.color_index = (self.color_index + 1) % #rainbow_colors
self.color = rainbow_colors[self.color_index + 1]
end
else
self.color_change_timer = nil
self.color_index = nil
self.color = self.initial_color
end
if old_color ~= self.color then
self.base_texture = sheep_texture(self.color)
self.object:set_properties({textures = self.base_texture})
end
elseif is_kay27 then
self.initial_color = self.color
self.color_change_timer = 0
self.color_index = -1
end
end,
on_rightclick = function(self, clicker)
local item = clicker:get_wielded_item()
@ -303,35 +263,7 @@ mobs:register_mob("mobs_mc:sheep", {
end
end,
})
mobs:spawn_specific(
"mobs_mc:sheep",
"overworld",
"ground",
{
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"ColdTaiga",
"SunflowerPlains",
"RoofedForest",
"MesaPlateauFM_grasstop",
"ExtremeHillsM",
"BirchForestM",
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
3,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:sheep", mobs_mc.spawn.grassland, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- spawn eggs
mobs:register_egg("mobs_mc:sheep", S("Sheep"), "mobs_mc_spawn_icon_sheep.png", 0)

View File

@ -81,17 +81,4 @@ mobs:register_arrow("mobs_mc:shulkerbullet", {
mobs:register_egg("mobs_mc:shulker", S("Shulker"), "mobs_mc_spawn_icon_shulker.png", 0)
mobs:spawn_specific(
"mobs_mc:shulker",
"end",
"ground",
{
"End"
},
0,
minetest.LIGHT_MAX+1,
30,
5000,
2,
mobs_mc.spawn_height.end_min,
mobs_mc.spawn_height.end_max)
mobs:spawn_specific("mobs_mc:shulker", mobs_mc.spawn.end_city, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 2, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)

View File

@ -30,14 +30,13 @@ local skeleton = {
"mcl_bows_bow_0.png", -- bow
"mobs_mc_skeleton.png", -- skeleton
} },
visual_size = {x=1, y=1},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
textures = {
{
"mobs_mc_empty.png", -- armor
"mobs_mc_skeleton.png", -- texture
"mcl_bows_bow_0.png", -- wielded_item
}
sounds = {
random = "mobs_mc_skeleton_random",
death = "mobs_mc_skeleton_death",
damage = "mobs_mc_skeleton_hurt",
distance = 16,
},
walk_velocity = 1.2,
run_velocity = 2.4,
@ -112,9 +111,9 @@ local stray = table.copy(skeleton)
stray.mesh = "mobs_mc_stray.b3d"
stray.textures = {
{
"mobs_mc_stray_overlay.png",
"mobs_mc_stray.png",
"mcl_bows_bow_0.png",
"mobs_mc_stray.png",
"mobs_mc_stray_overlay.png",
},
}
-- TODO: different sound (w/ echo)
@ -140,195 +139,13 @@ table.insert(stray.drops, {
mobs:register_mob("mobs_mc:stray", stray)
-- Overworld spawn
mobs:spawn_specific(
"mobs_mc:skeleton",
"overworld",
"ground",
{
"Mesa",
"FlowerForest",
"Swampland",
"Taiga",
"ExtremeHills",
"Jungle",
"Savanna",
"BirchForest",
"MegaSpruceTaiga",
"MegaTaiga",
"ExtremeHills+",
"Forest",
"Plains",
"Desert",
"ColdTaiga",
"MushroomIsland",
"IcePlainsSpikes",
"SunflowerPlains",
"IcePlains",
"RoofedForest",
"ExtremeHills+_snowtop",
"MesaPlateauFM_grasstop",
"JungleEdgeM",
"ExtremeHillsM",
"JungleM",
"BirchForestM",
"MesaPlateauF",
"MesaPlateauFM",
"MesaPlateauF_grasstop",
"MesaBryce",
"JungleEdge",
"SavannaM",
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
"MesaPlateauFM_sandlevel",
"MesaPlateauF_sandlevel",
"MesaBryce_sandlevel",
"Mesa_sandlevel",
"RoofedForest_ocean",
"JungleEdgeM_ocean",
"BirchForestM_ocean",
"BirchForest_ocean",
"IcePlains_deep_ocean",
"Jungle_deep_ocean",
"Savanna_ocean",
"MesaPlateauF_ocean",
"ExtremeHillsM_deep_ocean",
"Savanna_deep_ocean",
"SunflowerPlains_ocean",
"Swampland_deep_ocean",
"Swampland_ocean",
"MegaSpruceTaiga_deep_ocean",
"ExtremeHillsM_ocean",
"JungleEdgeM_deep_ocean",
"SunflowerPlains_deep_ocean",
"BirchForest_deep_ocean",
"IcePlainsSpikes_ocean",
"Mesa_ocean",
"StoneBeach_ocean",
"Plains_deep_ocean",
"JungleEdge_deep_ocean",
"SavannaM_deep_ocean",
"Desert_deep_ocean",
"Mesa_deep_ocean",
"ColdTaiga_deep_ocean",
"Plains_ocean",
"MesaPlateauFM_ocean",
"Forest_deep_ocean",
"JungleM_deep_ocean",
"FlowerForest_deep_ocean",
"MushroomIsland_ocean",
"MegaTaiga_ocean",
"StoneBeach_deep_ocean",
"IcePlainsSpikes_deep_ocean",
"ColdTaiga_ocean",
"SavannaM_ocean",
"MesaPlateauF_deep_ocean",
"MesaBryce_deep_ocean",
"ExtremeHills+_deep_ocean",
"ExtremeHills_ocean",
"MushroomIsland_deep_ocean",
"Forest_ocean",
"MegaTaiga_deep_ocean",
"JungleEdge_ocean",
"MesaBryce_ocean",
"MegaSpruceTaiga_ocean",
"ExtremeHills+_ocean",
"Jungle_ocean",
"RoofedForest_deep_ocean",
"IcePlains_ocean",
"FlowerForest_ocean",
"ExtremeHills_deep_ocean",
"MesaPlateauFM_deep_ocean",
"Desert_ocean",
"Taiga_ocean",
"BirchForestM_deep_ocean",
"Taiga_deep_ocean",
"JungleM_ocean",
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
7,
20,
17000,
2,
mobs_mc.spawn_height.overworld_min,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 17000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
-- Nether spawn
mobs:spawn_specific(
"mobs_mc:skeleton",
"nether",
"ground",
{
"Nether"
},
0,
7,
30,
10000,
3,
mobs_mc.spawn_height.nether_min,
mobs_mc.spawn_height.nether_max)
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 10000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
-- Stray spawn
-- TODO: Spawn directly under the sky
mobs:spawn_specific(
"mobs_mc:stray",
"overworld",
"ground",
{
"ColdTaiga",
"IcePlainsSpikes",
"IcePlains",
"ExtremeHills+_snowtop",
},
0,
7,
20,
19000,
2,
mobs_mc.spawn_height.water,
mobs_mc.spawn_height.overworld_max)
mobs:spawn_specific("mobs_mc:stray", mobs_mc.spawn.snow, {"air"}, 0, 7, 20, 19000, 2, mobs_mc.spawn_height.water, mobs_mc.spawn_height.overworld_max)
-- spawn eggs

View File

@ -25,12 +25,11 @@ mobs:register_mob("mobs_mc:witherskeleton", {
mesh = "mobs_mc_witherskeleton.b3d",
textures = {
{
"mobs_mc_empty.png", -- armor
"mobs_mc_wither_skeleton.png", -- wither skeleton
"default_tool_stonesword.png", -- sword
"mobs_mc_wither_skeleton.png", -- wither skeleton
}
},
visual_size = {x=1.2, y=1.2},
visual_size = {x=3.6, y=3.6},
makes_footstep_sound = true,
sounds = {
random = "mobs_mc_skeleton_random",
@ -95,20 +94,7 @@ mobs:register_mob("mobs_mc:witherskeleton", {
})
--spawn
mobs:spawn_specific(
"mobs_mc:witherskeleton",
"nether",
"ground",
{
"Nether"
},
0,
7,
30,
5000,
5,
mobs_mc.spawn_height.nether_min,
mobs_mc.spawn_height.nether_max)
mobs:spawn_specific("mobs_mc:witherskeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 5000, 5, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
-- spawn eggs
mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "mobs_mc_spawn_icon_witherskeleton.png", 0)

View File

@ -51,6 +51,7 @@ local spawn_children_on_die = function(child_mob, children_count, spawn_distance
end
end, children, self.attack)
end
return true
end
end
@ -108,6 +109,7 @@ local slime_big = {
fear_height = 0,
spawn_small_alternative = "mobs_mc:slime_small",
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
fire_resistant = true,
use_texture_alpha = true,
}
mobs:register_mob("mobs_mc:slime_big", slime_big)
@ -157,137 +159,9 @@ mobs:register_mob("mobs_mc:slime_tiny", slime_tiny)
local smin = mobs_mc.spawn_height.overworld_min
local smax = mobs_mc.spawn_height.water - 23
mobs:spawn_specific(
"mobs_mc:slime_tiny",
"overworld",
"ground",
{
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
minetest.LIGHT_MAX+1,
30,
12000,
4,
smin,
smax)
mobs:spawn_specific(
"mobs_mc:slime_small",
"overworld",
"ground",
{
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
minetest.LIGHT_MAX+1,
30,
8500,
4,
smin,
smax)
mobs:spawn_specific(
"mobs_mc:slime_big",
"overworld",
"ground",
{
"FlowerForest_underground",
"JungleEdge_underground",
"StoneBeach_underground",
"MesaBryce_underground",
"Mesa_underground",
"RoofedForest_underground",
"Jungle_underground",
"Swampland_underground",
"MushroomIsland_underground",
"BirchForest_underground",
"Plains_underground",
"MesaPlateauF_underground",
"ExtremeHills_underground",
"MegaSpruceTaiga_underground",
"BirchForestM_underground",
"SavannaM_underground",
"MesaPlateauFM_underground",
"Desert_underground",
"Savanna_underground",
"Forest_underground",
"SunflowerPlains_underground",
"ColdTaiga_underground",
"IcePlains_underground",
"IcePlainsSpikes_underground",
"MegaTaiga_underground",
"Taiga_underground",
"ExtremeHills+_underground",
"JungleM_underground",
"ExtremeHillsM_underground",
"JungleEdgeM_underground",
},
0,
minetest.LIGHT_MAX+1,
30,
10000,
4,
smin,
smax)
mobs:spawn_specific("mobs_mc:slime_tiny", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 12000, 4, smin, smax)
mobs:spawn_specific("mobs_mc:slime_small", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8500, 4, smin, smax)
mobs:spawn_specific("mobs_mc:slime_big", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 10000, 4, smin, smax)
-- Magma cube
local magma_cube_big = {
@ -400,55 +274,13 @@ mobs:register_mob("mobs_mc:magma_cube_tiny", magma_cube_tiny)
local mmin = mobs_mc.spawn_height.nether_min
local mmax = mobs_mc.spawn_height.nether_max
mobs:spawn_specific(
"mobs_mc:magma_cube_tiny",
"nether",
"ground",
{
"Nether"
},
0,
minetest.LIGHT_MAX+1,
30,
15000,
4,
mmin,
mmax)
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mmin, mmax)
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15500, 4, mmin, mmax)
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 16000, 4, mmin, mmax)
mobs:spawn_specific(
"mobs_mc:magma_cube_small",
"nether",
"ground",
{
"Nether"
},
0,
minetest.LIGHT_MAX+1,
30,
15500,
4,
mmin,
mmax)
mobs:spawn_specific(
"mobs_mc:magma_cube_big",
"nether",
"ground",
{
"Nether"
},
0,
minetest.LIGHT_MAX+1,
30,
16000,
4,
mmin,
mmax)
--mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
--mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
--mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
-- spawn eggs

Some files were not shown because too many files have changed in this diff Show More