Compare commits
705 Commits
Author | SHA1 | Date |
---|---|---|
SakuraRiu | 1b980849db | |
Riu Sakura | 8f80c6e6a6 | |
Riu Sakura | 3faa930bdc | |
ancientmarinerdev | a6e0da0e07 | |
ancientmarinerdev | 1c24adb714 | |
ancientmarinerdev | 658a08181c | |
Riu Sakura | b1b2e06dc7 | |
Michieal | 1a56a32fa8 | |
Michieal | 0a4243a0f3 | |
Michieal | 66a7c5757e | |
FossFanatic | 083a57a4a1 | |
FossFanatic | c512d28f02 | |
FossFanatic | 1056b6ca14 | |
FossFanatic | c8ec57bdbd | |
FossFanatic | 2dffe11cdd | |
FossFanatic | f29622dd59 | |
FossFanatic | d9bd3e2601 | |
Michieal | e53d7576c3 | |
Riu Sakura | 2b61231f82 | |
Michieal | 64c8afc8f0 | |
Michieal | f737df79e5 | |
Michieal | 7a3f373425 | |
Michieal | 63ea851e63 | |
Michieal | fe26f117da | |
Michieal | 5191326fc7 | |
ancientmarinerdev | 45c85841e8 | |
PrairieWind | 0a2f57476e | |
PrairieWind | 256655df83 | |
ancientmarinerdev | 20ca34233c | |
ancientmarinerdev | 93814880d0 | |
Michieal | 1861d7d30d | |
Michieal | c09ed02801 | |
Michieal | da4e359df0 | |
Michieal | 4da20f5e73 | |
ancientmarinerdev | 4ba3af5c73 | |
AFCMS | 3a619ba8e4 | |
ancientmarinerdev | 1859f44bd6 | |
kabou | 4e282df905 | |
kabou | 1b0b2b8b04 | |
kabou | c33d7d0106 | |
kabou | cec5b1668a | |
kabou | adcf03e0c6 | |
kabou | a4ef367708 | |
ancientmarinerdev | 20e78099d7 | |
ancientmarinerdev | 94a37790aa | |
ancientmarinerdev | 89f5d41366 | |
ancientmarinerdev | b33b87b9dd | |
ancientmarinerdev | 295970fbac | |
ancientmarinerdev | 639825a8da | |
FossFanatic | 0f8cfbf106 | |
FossFanatic | 39e9158c01 | |
ancientmarinerdev | 254caaff37 | |
ancientmarinerdev | 1ac6c45c7f | |
ancientmarinerdev | 5b68007902 | |
FossFanatic | 35967caf71 | |
Michieal | 29bc74dde9 | |
Michieal | ca1a46efa7 | |
Michieal | 997b6875a2 | |
ancientmarinerdev | 38e49a4b80 | |
ancientmarinerdev | 9b94a752bd | |
ancientmarinerdev | ab3530c356 | |
ancientmarinerdev | fbed60bd2d | |
ancientmarinerdev | bf34ca5f50 | |
ancientmarinerdev | 055432c1f7 | |
AFCMS | 7a371dc846 | |
AFCMS | 7deec7ae03 | |
AFCMS | 47eda5b69e | |
AFCMS | 8d921c0671 | |
AFCMS | 804aa388b5 | |
AFCMS | fcf9c35324 | |
AFCMS | 9cc63ed716 | |
AFCMS | c49f000894 | |
CyberMango | f7f2b97b13 | |
ancientmarinerdev | f7f0a2be8b | |
ancientmarinerdev | e408dd45e5 | |
FossFanatic | adead97905 | |
FossFanatic | ef33891b3f | |
FossFanatic | 1c5170d36f | |
FossFanatic | cf3e65e34e | |
FossFanatic | 0593a63626 | |
AFCMS | 2b46dd60c5 | |
ancientmarinerdev | 9d87dcf611 | |
AFCMS | 0f99373779 | |
AFCMS | bbf21762a6 | |
AFCMS | 34a7cf3e9f | |
AFCMS | 3153c41f3a | |
AFCMS | 489d3b7da6 | |
ancientmarinerdev | 2d05f9bc89 | |
Michieal | 8256fe6f04 | |
Michieal | bceb9cd855 | |
Michieal | 1c1ae53cec | |
Michieal | 0bc88b55e5 | |
Michieal | d80dd41cb4 | |
Michieal | 43a69c445f | |
Michieal | dbe0437201 | |
Michieal | 51372da2b9 | |
Michieal | 283b7c7410 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | bfa75dbcd2 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 7c87ed2d6c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | d8c16b1f31 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | ba292aeb5d | |
ancientmarinerdev | 055a93843f | |
ancientmarinerdev | a3cded0133 | |
ancientmarinerdev | 2b52eaa6ef | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 71a03bff6f | |
ancientmarinerdev | a0a0c69213 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | e57551f764 | |
AFCMS | 18cfb88ae5 | |
chmodsayshello | 826b9fcc45 | |
chmodsayshello | 7dacfe2cba | |
chmodsayshello | 7d57e73ae4 | |
chmodsayshello | 7f59611914 | |
chmodsayshello | 927fd60786 | |
chmodsayshello | ab031daa5a | |
cora | 588425df73 | |
cora | 25d0f2b0c4 | |
cora | 85e7de6c14 | |
cora | d395c82183 | |
cora | 34a558ced9 | |
cora | 17f789550d | |
cora | 9508cd4c25 | |
cora | c4808beef4 | |
cora | 69c64a2c24 | |
cora | 318da7e3c9 | |
cora | 5e022ef083 | |
Michieal | c45e4d292a | |
Michieal | a542503b72 | |
Michieal | 4b570b1aa9 | |
Michieal | cc190eeb62 | |
Michieal | 7e31ece325 | |
Michieal | b783e616df | |
Michieal | f1ae86ede5 | |
Michieal | c63533c753 | |
Michieal | 81fa7cf74c | |
Michieal | 5378aae09e | |
Michieal | aec16ee62e | |
cora | 0cdf84ab47 | |
cora | 31395a8830 | |
cora | 8e74a5a56a | |
Michieal | 2149ca1147 | |
Michieal | de7696ce69 | |
Michieal | 4b777b1d9c | |
Michieal | dadb8f9287 | |
Michieal | 83b40f8cff | |
cora | ce95f034b4 | |
Michieal | 2b3ec8d944 | |
Michieal | 972e9ea0f8 | |
Michieal | df62a3aac1 | |
cora | 7cc25c4670 | |
cora | ebf214c043 | |
cora | 7bd0b5a9b1 | |
cora | bc496a8682 | |
cora | a3415647d4 | |
cora | 4909ef6ff7 | |
cora | 3c6d79ecb3 | |
cora | 6948b77226 | |
cora | 90321ee578 | |
cora | 2b691dea07 | |
cora | c25287e493 | |
epCode | 762f64a4d1 | |
cora | db63f1c453 | |
cora | faa38ffab8 | |
cora | 19d6d43b39 | |
cora | 28b6fcf289 | |
cora | 80490b4c11 | |
cora | 5e177c61f6 | |
cora | c9c2b874be | |
cora | 18c415f6a0 | |
cora | 49670d1d28 | |
cora | a25cd921f2 | |
cora | 91b5b6266c | |
cora | 148b30694f | |
cora | f4374d9efd | |
cora | b41d6a1cae | |
cora | 6548b7fe47 | |
cora | c7681af53e | |
cora | cc77e109f5 | |
cora | e7d9ded9ab | |
cora | 96d697c5cd | |
cora | 01abfea16d | |
cora | bbba7cee41 | |
cora | e82c318f0c | |
cora | 70834d0f5d | |
cora | d62dbcb852 | |
cora | 67fd8c8b40 | |
cora | 90639ef99d | |
cora | 31ef791cfd | |
cora | 4d5104ff8f | |
cora | b7962b067f | |
cora | 19dc86c951 | |
cora | e3ee224532 | |
cora | c209bff157 | |
cora | 59a7608dfc | |
cora | 8b08707e3f | |
cora | dd8f64e039 | |
cora | 56519c881e | |
cora | 944d53111a | |
PrairieWind | 33c3007709 | |
PrairieWind | 8d5aea9743 | |
cora | fa2a2bb8ac | |
cora | ee808e8f84 | |
cora | 53d11ec71b | |
cora | f1aca79f9c | |
cora | fb8b3d5f55 | |
Michieal | 8184d4938c | |
Michieal | da64a10403 | |
cora | 2a54ea6e74 | |
PrairieWind | 8b7b2f02e9 | |
cora | 927eff691c | |
cora | efdd22d69f | |
cora | f14bfdc0cd | |
cora | 66322a873b | |
cora | e134cc5e65 | |
cora | 1fcedd1375 | |
cora | bb73e5f1cd | |
cora | d4c2501d62 | |
cora | 708fcfb368 | |
cora | ebf4678e54 | |
cora | b4971b298d | |
cora | d3349396b4 | |
cora | 665e8a06a2 | |
cora | 67e8f13a96 | |
cora | 99db678a44 | |
cora | dd58bed5d1 | |
cora | 9cf910c47f | |
cora | 69f5bad0bb | |
cora | ac7038e7a5 | |
cora | 3b368fdd5a | |
cora | 624092ddd3 | |
cora | dabc09adb2 | |
cora | d0ea7c4059 | |
cora | 87d5cdc439 | |
cora | c6fc388597 | |
cora | 93e5034317 | |
cora | 8564a12398 | |
cora | e5515ed119 | |
cora | 94057aa7e5 | |
cora | db4f9ec5a3 | |
cora | 057a78ed30 | |
cora | 22fa5096c9 | |
cora | 2147790b44 | |
PrairieWind | a1e21b44c8 | |
cora | 358250012f | |
cora | 608da950e4 | |
cora | c3455de604 | |
cora | 6251c623dd | |
cora | 62fe08de59 | |
cora | af0c718538 | |
cora | 3600bc8066 | |
cora | d1788b5643 | |
cora | c7cc9ccb3e | |
cora | 097ee8b4d4 | |
cora | 59d384020e | |
cora | 1d18482103 | |
cora | b82912f30d | |
PrairieWind | 48a8511052 | |
PrairieWind | 160d7ca6fa | |
cora | ae90cbe3ef | |
cora | 9893dee1aa | |
cora | 232f57c247 | |
cora | dc017864fd | |
cora | 2159edba05 | |
cora | 37144f8787 | |
cora | 7ab2c5fecd | |
cora | 99fce095ef | |
cora | 390aec5d7a | |
cora | 052e76bfcc | |
cora | f17a9220bb | |
cora | 627ce0dc3b | |
cora | f06d5a1ee3 | |
PrairieWind | 5dc1a3d456 | |
PrairieWind | 1b6d778dd1 | |
PrairieWind | 96ac31bec3 | |
PrairieAstronomer | 8b6409b7f1 | |
cora | 6e6f163b71 | |
cora | ba7da6a885 | |
cora | 3066d966a1 | |
cora | 626f61c2c1 | |
FossFanatic | f71759f184 | |
cora | 0942949c5d | |
cora | b255c8c23e | |
PrairieWind | e1ba319616 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 88897566d9 | |
PrairieWind | 4cc0a5b0f1 | |
cora | ab0f88ad37 | |
cora | 53c1ed9fbf | |
cora | a3fbb2e0c9 | |
cora | 22063a64db | |
Michieal | 9fc27da873 | |
PrairieWind | 556316ae34 | |
Michieal | 9ffdd13fa5 | |
PrairieWind | 9cfbbef41d | |
cora | adfbf4bc64 | |
FossFanatic | 1a5aed704c | |
cora | d738eb0554 | |
Ranko_Saotome | 0fb6d20bc7 | |
cora | 94e7520b08 | |
AFCMS | 029769f48f | |
cora | 0fb040476b | |
Daniel Cassidy | 79192d51ed | |
cora | 6051731645 | |
FossFanatic | dec2c2bade | |
FossFanatic | e25415e047 | |
FossFanatic | f93af56bf6 | |
FossFanatic | f8b98d784b | |
FossFanatic | e898b72f3d | |
cora | 90bd0ee315 | |
cora | 7d63b07e09 | |
Michieal | 197e807684 | |
Michieal | 467700591b | |
Michieal | a7e643818f | |
cora | cf282a5154 | |
PrairieWind | 1877f7261e | |
cora | adeb6c42e5 | |
anarquimico | e658e29179 | |
PrairieWind | 7e3cb59863 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | a6db44b71f | |
PrairieWind | eaccd71558 | |
cora | 142f3d6d5a | |
cora | 328a377940 | |
cora | c5ec3c770c | |
cora | 5a9d428151 | |
cora | 883ca05c3a | |
cora | dea647129e | |
cora | 35e30df552 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | d764c3eac3 | |
cora | f397ff83ff | |
PrairieWind | 89a342a34f | |
PrairieWind | b17dcf26da | |
PrairieWind | c767c617f8 | |
PrairieWind | fb651a4922 | |
PrairieWind | 16f878d3dc | |
PrairieWind | bbb908239b | |
PrairieWind | 0aaaa05164 | |
PrairieWind | 8a3c1eaa6e | |
PrairieWind | a79692a68b | |
PrairieWind | cb60338f10 | |
PrairieWind | 89b02c126a | |
PrairieWind | db0b44326b | |
PrairieWind | 4bb824cd38 | |
PrairieWind | daf5bf90a4 | |
cora | d5b5d80a1a | |
Michieal | 9dc27e535b | |
Michieal | 439ac600fd | |
Michieal | bee40c55ec | |
Michieal | 2577a70a65 | |
Michieal | e2e86ae9a8 | |
Michieal | 577f9f7785 | |
Michieal | 67296c7a37 | |
Michieal | 4a1e37d9c6 | |
Michieal | e67c6e1ada | |
Michieal | 10a3a022b6 | |
Michieal | b2660e8c63 | |
Michieal | c93f66295b | |
cora | 4dba6a62cf | |
cora | a28f667b20 | |
ancientmarinerdev | 1a670bc41b | |
cora | cdf28ec684 | |
Michieal | 99a7df7ce6 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 7630b1ca00 | |
cora | eeecc52ff8 | |
cora | 661424ad23 | |
FossFanatic | 9b392fcefc | |
FossFanatic | d091d634bb | |
FossFanatic | 16746f962f | |
FossFanatic | 00c596c126 | |
FossFanatic | 993d7b6873 | |
FossFanatic | ceff91e2c3 | |
FossFanatic | 6bfcd9b1ea | |
FossFanatic | 6b826b45e1 | |
FossFanatic | 3de1f9e4d6 | |
FossFanatic | 348434adf1 | |
FossFanatic | 6946573e65 | |
FossFanatic | 862d6a9e3e | |
FossFanatic | 78d45d23c2 | |
FossFanatic | b11d65cc82 | |
FossFanatic | f4f57a9fe6 | |
TheOnlyJoeEnderman | 9e73c2cd46 | |
TheOnlyJoeEnderman | 50e6dd94b2 | |
epCode | 7ed8370eb0 | |
epCode | 3f8f6f8e44 | |
epCode | a5eb1d240e | |
epCode | ce724ee926 | |
PrairieWind | e0419f3568 | |
cora | f66a241188 | |
cora | 80fb648447 | |
PrairieWind | 4255143087 | |
RankoSaotome | 51d4f075b2 | |
RankoSaotome | a607444e60 | |
PrairieWind | ebd38fd44e | |
cora | dbef60c558 | |
PrairieWind | 4e9190bbcf | |
cora | 033b051689 | |
cora | 589bf75e6a | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 94960b64d4 | |
anarquimico | 507f14c317 | |
cora | 12a31ee5ba | |
cora | 63225c20e5 | |
PrairieWind | 54d4a97a27 | |
cora | 61b5783e73 | |
cora | 04cb919f2b | |
cora | 4ceecffc8b | |
cora | 3c287d84d2 | |
cora | 00541cbfb8 | |
cora | 36a6a353a3 | |
cora | 8fa0371c63 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 3d6ff573e7 | |
cora | b7f766e7cc | |
cora | 489ef43458 | |
ancientmarinerdev | e6d778b214 | |
ancientmarinerdev | 70a7fb9387 | |
ancientmarinerdev | a2732b655f | |
ancientmarinerdev | 9065170b1c | |
ancientmarinerdev | ce457eb351 | |
cora | c21527d207 | |
epCode | c288b6ab93 | |
epCode | 28f952309e | |
cora | 55375a7476 | |
epCode | 080182ab15 | |
epCode | 6c6e21b63e | |
cora | b36b2058ab | |
3raven | d01aa36d09 | |
cora | daa455c41b | |
cora | b625fba4f2 | |
anarquimico | 3ef8838a11 | |
cora | 99b26f5e3f | |
ancientmarinerdev | 59808c5190 | |
ancientmarinerdev | 3000a378f3 | |
ancientmarinerdev | 3e1a475442 | |
ancientmarinerdev | e5c639c779 | |
ancientmarinerdev | ff0fea8978 | |
ancientmarinerdev | 814ad39c09 | |
cora | 1dfc71c76c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 8ff4a94889 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | c66e184373 | |
cora | 8ad79fbeed | |
cora | 41055d5abe | |
cora | 468b46a920 | |
AFCMS | 7dbe6489be | |
AFCMS | 5f626cf1b0 | |
AFCMS | 47e46efa6f | |
AFCMS | a11948c5f3 | |
AFCMS | cb7d5e3790 | |
AFCMS | 2c5d3861f6 | |
AFCMS | e56fa0d809 | |
TheOnlyJoeEnderman | 6a9387b752 | |
TheOnlyJoeEnderman | cbb7a8edb0 | |
anarquimico | cb721f37ce | |
cora | 7e1c9a86e4 | |
cora | c44b29925a | |
cora | 470d7dbe39 | |
MysticTempest | 6a0bcf9881 | |
anarquimico | 3072d44b66 | |
anarquimico | f61b072d39 | |
cora | bbd2ac904d | |
TheOnlyJoeEnderman | 012fe4b37e | |
TheOnlyJoeEnderman | 3110f97868 | |
TheOnlyJoeEnderman | dff028595c | |
Michieal | dd869afea0 | |
Michieal | c277229c1b | |
anarquimico | 91ef21c55b | |
MysticTempest | 2be75c9628 | |
TheOnlyJoeEnderman | 6e328153d4 | |
TheOnlyJoeEnderman | 4695707188 | |
TheOnlyJoeEnderman | 432e196794 | |
TheOnlyJoeEnderman | a592d69990 | |
TheOnlyJoeEnderman | 310237be33 | |
TheOnlyJoeEnderman | cf6d04c716 | |
TheOnlyJoeEnderman | c22997db09 | |
cora | 4c94bee22f | |
anarquimico | f977b1a253 | |
anarquimico | ff3438f729 | |
cora | f7ac679c0f | |
PrairieWind | 6f8d854f8a | |
PrairieWind | 92723cab9f | |
cora | 8db3071c2d | |
Nicu | 8162a4af2d | |
Nicu | 30d2022b4a | |
cora | df216cb847 | |
cora | 4dec3e1382 | |
cora | 43425279a3 | |
cora | faf75a3ce5 | |
Michieal | b62f61db10 | |
Michieal | c4981d894c | |
Michieal | 93913ed780 | |
Michieal | 3409c5a220 | |
Michieal | 76991fcba9 | |
Michieal | 61df48e46e | |
Michieal | 18e83e5763 | |
Michieal | 8a21b9e99c | |
Michieal | 6d1b1b3c74 | |
Michieal | 1f5b92f7bf | |
Michieal | 4d74e4d9c1 | |
Michieal | ef504e90c1 | |
Michieal | 09d452667b | |
Michieal | 9b37ca460e | |
cora | ffc5fe06ed | |
cora | e146b173b3 | |
cora | 51d34c9f3d | |
cora | 0d19a66bdd | |
MysticTempest | 15542d4878 | |
cora | fcba67b7e7 | |
cora | 2c4a768685 | |
cora | 95db6104e9 | |
cora | 5cf2f488a8 | |
cora | 97468cde0c | |
cora | 220b542cbf | |
cora | 21f3782439 | |
cora | d18f55aa48 | |
cora | 32e4a122a1 | |
TheOnlyJoeEnderman | 8e74209c9b | |
TheOnlyJoeEnderman | faa7ca1f68 | |
TheOnlyJoeEnderman | 7e3d5f3553 | |
ancientmarinerdev | 8b0c4c17ad | |
ancientmarinerdev | 2d0ea5e205 | |
ancientmarinerdev | ad137304bc | |
ancientmarinerdev | ac45dec544 | |
ancientmarinerdev | ea9c9e74b0 | |
ancientmarinerdev | 5866983bf5 | |
ancientmarinerdev | 17e3e83f43 | |
cora | 06d4845955 | |
cora | da9f253117 | |
cora | b144a93fd8 | |
cora | 3a37b1c8cc | |
cora | f1cdb24545 | |
cora | 48d6a2fd72 | |
cora | e9e76d2c1e | |
cora | cdd8ea1dbc | |
cora | 5355234af1 | |
Michieal | fb10320550 | |
Michieal | 5a68c876fb | |
Michieal | dd1e4ca927 | |
Michieal | 162bd36101 | |
Michieal | d48100c427 | |
TheOnlyJoeEnderman | 126de74904 | |
TheOnlyJoeEnderman | fbc57192a0 | |
cora | ab00297077 | |
ancientmarinerdev | 2f4622909e | |
ancientmarinerdev | 5ff4ce7116 | |
ancientmarinerdev | c606092066 | |
ancientmarinerdev | 5d5babdb20 | |
ancientmarinerdev | 7f33287e18 | |
ancientmarinerdev | 1d667c26b5 | |
ancientmarinerdev | a01c3d1342 | |
ancientmarinerdev | f9ec402b29 | |
ancientmarinerdev | cabe299456 | |
ancientmarinerdev | bf491c35a1 | |
ancientmarinerdev | 04cac7156e | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 963547a0ca | |
cora | 6851a53c6c | |
TheOnlyJoeEnderman | d5ca4fca9b | |
TheOnlyJoeEnderman | 3bdd1ea160 | |
TheOnlyJoeEnderman | 5cc835df93 | |
cora | 646a341f52 | |
cora | ae9173f7ec | |
cora | b4cb06b5ed | |
cora | 009ca7535c | |
cora | 3629f88dc0 | |
cora | 21f5586669 | |
cora | d393e6a181 | |
cora | f324e150ea | |
cora | aa2661341b | |
cora | 72224a392a | |
cora | 376aeda495 | |
cora | fb67bc74ba | |
cora | 6485545269 | |
cora | fb1c1a483e | |
cora | fe68e1eaac | |
cora | 0f01a28c93 | |
cora | 27a434641d | |
cora | 77bdce5cea | |
cora | 1f58cfb240 | |
cora | d1f0ade2e8 | |
cora | dec207368c | |
cora | eef4b52ce4 | |
cora | 6b8236e8f3 | |
cora | 5d26595f4a | |
cora | 1c6d1c958a | |
Michieal | c3433bd003 | |
cora | 8aedd6a761 | |
cora | 6bc12ec5ec | |
cora | 62f353d54f | |
cora | 4ad1abde14 | |
cora | 768345a91c | |
cora | 98dad0b191 | |
cora | 302b18833a | |
cora | f76e0628e4 | |
cora | 24476caf7e | |
cora | 68039e0fb6 | |
cora | d866b61d1c | |
cora | a91586b2b5 | |
cora | a03973a80b | |
cora | 9ea286428c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 8faa6aa9eb | |
cora | 56cbe50a02 | |
epCode | 4575fd0d81 | |
epCode | 8a2962cd46 | |
epCode | 9099f3ac7a | |
epCode | 1d3f52004f | |
ancientmarinerdev | 532fc22f62 | |
cora | fb29abcd50 | |
chmodsayshello | e9703c409e | |
chmodsayshello | c572c27326 | |
TheOnlyJoeEnderman | 88d02da047 | |
chmodsayshello | c1af4935a1 | |
chmodsayshello | 910b3d57ac | |
TheOnlyJoeEnderman | 7aafb67923 | |
TheOnlyJoeEnderman | e7bcc89e65 | |
TheOnlyJoeEnderman | 1e003a2571 | |
TheOnlyJoeEnderman | 3001832b8f | |
cora | a1637486bb | |
cora | dc84b28d54 | |
cora | 138c0e8668 | |
epCode | 2b2c2cf9cc | |
epCode | 5378d8eba7 | |
epCode | 4fa1c445cc | |
epCode | 83d92e08ca | |
epCode | eed5ec6f63 | |
epCode | 702ba25283 | |
epCode | de73129c2b | |
epCode | d1d9f76c5d | |
cora | e34e2ba03f | |
cora | 672e44bbb1 | |
TheOnlyJoeEnderman | e2107601f4 | |
TheOnlyJoeEnderman | 0624d9d169 | |
TheOnlyJoeEnderman | 2d7d577f31 | |
TheOnlyJoeEnderman | 0fc09c6e89 | |
TheOnlyJoeEnderman | 35ac5c75f5 | |
cora | b3553fe3b8 | |
cora | c850c0dfbb | |
cora | 9aaa13408a | |
cora | c30e2c33b9 | |
cora | 146e2de1a5 | |
TheOnlyJoeEnderman | b1355513dd | |
TheOnlyJoeEnderman | 77bf8e0fb0 | |
cora | c1174360d1 | |
cora | 11cc8745a2 | |
TheOnlyJoeEnderman | 92d4e4f1e1 | |
TheOnlyJoeEnderman | ca0786f549 | |
TheOnlyJoeEnderman | 7f291aa109 | |
TheOnlyJoeEnderman | 4683384da1 | |
cora | 4f191754b8 | |
epCode | ef923deff1 | |
epCode | ee9301467c | |
epCode | 239eca8988 | |
epCode | 86eef8827a | |
cora | e6b3458018 | |
cora | 9e3163ec42 | |
TheOnlyJoeEnderman | d34710be85 | |
TheOnlyJoeEnderman | d324197609 | |
cora | ba9d10055e | |
TheOnlyJoeEnderman | e01f103723 | |
TheOnlyJoeEnderman | 109d162bca | |
TheOnlyJoeEnderman | d89c1c0e5e | |
TheOnlyJoeEnderman | 1efacd5a1b | |
TheOnlyJoeEnderman | 5abf7d187c | |
TheOnlyJoeEnderman | 2f975f2155 | |
TheOnlyJoeEnderman | fbb1aaf29f | |
TheOnlyJoeEnderman | 4bee5268cd | |
TheOnlyJoeEnderman | d575e8394d | |
TheOnlyJoeEnderman | a8250e2544 | |
TheOnlyJoeEnderman | 7e54597a25 | |
TheOnlyJoeEnderman | a47df3dcdc | |
TheOnlyJoeEnderman | 0ab82b5317 | |
TheOnlyJoeEnderman | eaa397a7da | |
TheOnlyJoeEnderman | e4995524de | |
epCode | 6c0e29891d | |
PrairieWind | e8b983bcc5 | |
cora | ae91640234 | |
cora | d6c0561d5a | |
cora | 2664fb871b | |
cora | 2b08f9ac53 | |
cora | 8d456d8ff9 | |
cora | b00f2784ff | |
cora | 280bcddcb2 | |
balazsszalab | 495378b85e | |
cora | 5f4d7774ae | |
kay27 | a7c3878cae | |
kay27 | eb313f9482 | |
cora | 63a360674d | |
epCode | 306db6b609 | |
ancientmarinerdev | f8ee473383 | |
ancientmarinerdev | eebea4a7a9 | |
ancientmarinerdev | 37e4dd5556 | |
epCode | 65289de86e | |
epCode | 6fa85a3a0c | |
epCode | de4c40f60a | |
epCode | 636e5545a7 | |
epCode | 70beaf6b3d | |
epCode | b89a9375da | |
cora | 6a512139c9 | |
AFCMS | 4d6e79b07f | |
AFCMS | 3432734c37 | |
cora | 8b566cddf3 | |
epCode | 970011e02b | |
cora | 8778ee6e9e | |
epCode | 97c062a1ef | |
TheOnlyJoeEnderman | 0589816c75 | |
TheOnlyJoeEnderman | 162fe3bc1c | |
TheOnlyJoeEnderman | f352e99eb6 | |
TheOnlyJoeEnderman | 05329c908f | |
TheOnlyJoeEnderman | f8fe943fa6 | |
TheOnlyJoeEnderman | 6ee8e98ae3 | |
TheOnlyJoeEnderman | 65c7b741bb | |
TheOnlyJoeEnderman | 8001a1ea07 |
|
@ -0,0 +1,12 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
[*.lua]
|
||||||
|
charset = utf8
|
||||||
|
indent_style = tab
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
keep_one_space_between_table_and_bracket = false
|
||||||
|
spaces_around_operators = true
|
|
@ -3,3 +3,4 @@
|
||||||
*.blend1
|
*.blend1
|
||||||
*.blend2
|
*.blend2
|
||||||
*.blend3
|
*.blend3
|
||||||
|
/.idea/
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
|
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
* AncientMariner
|
||||||
* Nicu
|
* Nicu
|
||||||
* cora
|
|
||||||
|
|
||||||
## Previous Maintainers
|
## Previous Maintainers
|
||||||
* Fleckenstein
|
* Fleckenstein
|
||||||
* jordan4ibanez
|
* jordan4ibanez
|
||||||
|
* cora
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
* bzoss
|
* bzoss
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
* SumianVoice
|
* SumianVoice
|
||||||
* MrRar
|
* MrRar
|
||||||
* talamh
|
* talamh
|
||||||
|
* Faerraven
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
* Laurent Rocher
|
* Laurent Rocher
|
||||||
|
@ -89,6 +91,10 @@
|
||||||
* FossFanatic
|
* FossFanatic
|
||||||
* Herbert West
|
* Herbert West
|
||||||
* GuyLiner
|
* GuyLiner
|
||||||
|
* 3raven
|
||||||
|
* anarquimico
|
||||||
|
* TheOnlyJoeEnderman
|
||||||
|
* Ranko Saotome
|
||||||
|
|
||||||
## MineClone5
|
## MineClone5
|
||||||
* kay27
|
* kay27
|
||||||
|
@ -158,6 +164,7 @@
|
||||||
* MysticTempest
|
* MysticTempest
|
||||||
* RandomLegoBrick
|
* RandomLegoBrick
|
||||||
* cora
|
* cora
|
||||||
|
* Faerraven / Michieal
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
* Wuzzy
|
* Wuzzy
|
||||||
|
|
|
@ -74,6 +74,8 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
||||||
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
||||||
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
||||||
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
|
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
|
||||||
|
* `leaves=X`: Node will spotaneously decay if no tree trunk nodes remain within 6 blocks distance.
|
||||||
|
* `leaves_orphan`: See above, these nodes are in the process of decayed.
|
||||||
|
|
||||||
#### Footnotes
|
#### Footnotes
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
||||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
Version: 0.81 (in development)
|
Version: 0.82 (in development)
|
||||||
|
|
||||||
### Gameplay
|
### Gameplay
|
||||||
You start in a randomly-generated world made entirely of cubes. You can explore
|
You start in a randomly-generated world made entirely of cubes. You can explore
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#File to document release steps with a view to evolving into a script
|
||||||
|
|
||||||
|
#Update CREDITS.md
|
||||||
|
#Update version in README.md (soon to be game.conf from of 0.82.0)
|
||||||
|
|
||||||
|
lua tools/generate_ingame_credits.lua
|
||||||
|
|
||||||
|
git add CREDITS.md
|
||||||
|
git add mods/HUD/mcl_credits/people.lua
|
||||||
|
|
||||||
|
git add README.md
|
||||||
|
# To uncomment when applicable
|
||||||
|
#git add game.conf
|
||||||
|
|
||||||
|
git commit -m "Pre-release update credits and set version 0.81.1"
|
||||||
|
|
||||||
|
git tag 0.81.1
|
||||||
|
|
||||||
|
git push origin 0.81.1
|
|
@ -1,2 +1,4 @@
|
||||||
title = MineClone 2
|
title = MineClone 2
|
||||||
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
|
||||||
|
disallowed_mapgens = v6
|
||||||
|
version=MCL2-0.82-indev
|
|
@ -12,6 +12,7 @@ mcl_damage = {
|
||||||
drown = {bypasses_armor = true},
|
drown = {bypasses_armor = true},
|
||||||
starve = {bypasses_armor = true, bypasses_magic = true},
|
starve = {bypasses_armor = true, bypasses_magic = true},
|
||||||
cactus = {},
|
cactus = {},
|
||||||
|
sweet_berry = {},
|
||||||
fall = {bypasses_armor = true},
|
fall = {bypasses_armor = true},
|
||||||
fly_into_wall = {bypasses_armor = true}, -- unused
|
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
## mcl_events
|
||||||
|
### Registering Events
|
||||||
|
`mlc_events.register_event("name",def)`
|
||||||
|
|
||||||
|
#### Event Definition
|
||||||
|
{
|
||||||
|
stage = 0,
|
||||||
|
max_stage = 1,
|
||||||
|
percent = 100,
|
||||||
|
bars = {},
|
||||||
|
completed = false,
|
||||||
|
cond_start = function() end,
|
||||||
|
--return table of paramtables e.g. { { player = playername, pos = position, ... } }, custom parameters will be passed to the event object/table
|
||||||
|
on_step = function(event) end,
|
||||||
|
--this function is run every game step when the event is active
|
||||||
|
on_start = function(event) end,
|
||||||
|
-- this function is run when the event starts
|
||||||
|
on_stage_begin = function(event) end,
|
||||||
|
-- this function runs when a new stage of the event starts
|
||||||
|
cond_progress = function(event) end, --return false or next stage id
|
||||||
|
--this function checks if the event should progress to the next (or any other) stage
|
||||||
|
cond_complete = function(event) end,
|
||||||
|
--return true if event finished successfully
|
||||||
|
}
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
* /event_start <event> -- starts the given event at the current player coordinates
|
|
@ -1,15 +1,16 @@
|
||||||
mcl_events = {}
|
mcl_events = {}
|
||||||
mcl_events.registered_events = {}
|
mcl_events.registered_events = {}
|
||||||
|
local disabled_events = minetest.settings:get("mcl_disabled_events")
|
||||||
|
if disabled_events then disabled_events = disabled_events:split(",")
|
||||||
|
else disabled_events = {} end
|
||||||
local DBG = minetest.settings:get_bool("mcl_logging_event_api",false)
|
local DBG = minetest.settings:get_bool("mcl_logging_event_api",false)
|
||||||
local active_events = {}
|
local active_events = {}
|
||||||
|
|
||||||
local tpl_eventdef = {
|
local event_tpl = {
|
||||||
stage = 0,
|
stage = 0,
|
||||||
max_stage = 1,
|
max_stage = 1,
|
||||||
percent = 100,
|
percent = 100,
|
||||||
bars = {},
|
bars = {},
|
||||||
--pos = vector.zero(),
|
|
||||||
--time_start = 0,
|
|
||||||
completed = false,
|
completed = false,
|
||||||
cond_start = function(event) end, --return table of positions
|
cond_start = function(event) end, --return table of positions
|
||||||
on_step = function(event) end,
|
on_step = function(event) end,
|
||||||
|
@ -27,43 +28,39 @@ local function mcl_log(m,l)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_events.register_event(name,def)
|
function mcl_events.register_event(name,def)
|
||||||
mcl_events.registered_events[name] = {}
|
if table.indexof(disabled_events,name) ~= -1 then return end
|
||||||
--setmetatable(mcl_events.registered_events[name],tpl_eventdef)
|
|
||||||
mcl_events.registered_events[name] = def
|
mcl_events.registered_events[name] = def
|
||||||
mcl_events.registered_events[name].name = name
|
mcl_events.registered_events[name].name = name
|
||||||
end
|
end
|
||||||
|
|
||||||
local function addbars(self)
|
local function addbars(self)
|
||||||
|
if not self.enable_bossbar then return end
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
if vector.distance(self.pos,player:get_pos()) < 75 then
|
if vector.distance(self.pos,player:get_pos()) < 64 then
|
||||||
local bar = mcl_bossbars.add_bar(player, {color = "red", text = self.name .. " stage "..self.stage.." / "..self.max_stage, percentage = self.percent }, true,1)
|
local bar = mcl_bossbars.add_bar(player, {color = "red", text = self.readable_name .. ": Wave "..self.stage.." / "..self.max_stage, percentage = self.percent }, true,1)
|
||||||
table.insert(self.bars,bar)
|
table.insert(self.bars,bar)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_bars(self)
|
|
||||||
for _,b in pairs(self.bars) do
|
|
||||||
mcl_bossbars.update_bar(b,{text = self.name .. " stage "..self.stage,percentage=self.percent})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function start_event(p,e)
|
local function start_event(p,e)
|
||||||
mcl_log("event started: "..e.name.." at "..minetest.pos_to_string(vector.round(p.pos)))
|
mcl_log("[mcl_events] Event started: "..e.readable_name.." at "..minetest.pos_to_string(vector.round(p.pos)))
|
||||||
local idx = #active_events + 1
|
local idx = #active_events + 1
|
||||||
active_events[idx] = table.copy(e)
|
active_events[idx] = table.copy(e)
|
||||||
setmetatable(active_events[idx],e)
|
setmetatable(active_events[idx],{__index = event_tpl})
|
||||||
for k,v in pairs(p) do active_events[idx][k] = v end
|
for k,v in pairs(p) do active_events[idx][k] = v end
|
||||||
active_events[idx].stage = 0
|
active_events[idx].stage = 0
|
||||||
active_events[idx].percent = 100
|
active_events[idx].percent = 100
|
||||||
active_events[idx].bars = {}
|
active_events[idx].bars = {}
|
||||||
active_events[idx].time_start = os.time()
|
active_events[idx].time_start = os.time()
|
||||||
active_events[idx]:on_start(p.pos)
|
if active_events[idx].on_start then
|
||||||
|
active_events[idx]:on_start(p.pos)
|
||||||
|
end
|
||||||
addbars(active_events[idx])
|
addbars(active_events[idx])
|
||||||
end
|
end
|
||||||
|
|
||||||
local function finish_event(self,idx)
|
local function finish_event(self,idx)
|
||||||
mcl_log("Finished: "..self.name.." at "..minetest.pos_to_string(vector.round(self.pos)))
|
mcl_log("[mcl_events] Finished: "..self.readable_name.." at "..minetest.pos_to_string(vector.round(self.pos)))
|
||||||
if self.on_complete then self:on_complete() end
|
if self.on_complete then self:on_complete() end
|
||||||
for _,b in pairs(self.bars) do
|
for _,b in pairs(self.bars) do
|
||||||
mcl_bossbars.remove_bar(b)
|
mcl_bossbars.remove_bar(b)
|
||||||
|
@ -73,6 +70,7 @@ end
|
||||||
|
|
||||||
local etime = 0
|
local etime = 0
|
||||||
function check_events(dtime)
|
function check_events(dtime)
|
||||||
|
--process active events
|
||||||
for idx,ae in pairs(active_events) do
|
for idx,ae in pairs(active_events) do
|
||||||
if ae.cond_complete and ae:cond_complete() then
|
if ae.cond_complete and ae:cond_complete() then
|
||||||
ae.finished = true
|
ae.finished = true
|
||||||
|
@ -84,23 +82,27 @@ function check_events(dtime)
|
||||||
local p = ae:cond_progress()
|
local p = ae:cond_progress()
|
||||||
if p == true then
|
if p == true then
|
||||||
ae.stage = ae.stage + 1
|
ae.stage = ae.stage + 1
|
||||||
ae:on_stage_begin()
|
if ae:on_stage_begin() == true then
|
||||||
|
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." failed at stage_begin of stage "..ae.stage )
|
||||||
|
active_events[idx] = nil
|
||||||
|
end
|
||||||
elseif tonumber(p) then
|
elseif tonumber(p) then
|
||||||
ae.stage = tonumber(p) or ae.stage + 1
|
ae.stage = tonumber(p) or ae.stage + 1
|
||||||
ae:on_stage_begin()
|
ae:on_stage_begin()
|
||||||
end
|
end
|
||||||
elseif not ae.finished and ae.on_step then
|
elseif not ae.finished and ae.on_step then
|
||||||
ae:on_step()
|
ae:on_step(dtime)
|
||||||
end
|
end
|
||||||
addbars(ae)
|
addbars(ae)
|
||||||
--update_bars(ae)
|
|
||||||
end
|
end
|
||||||
|
-- check if a new event should be started
|
||||||
etime = etime - dtime
|
etime = etime - dtime
|
||||||
if etime > 0 then return end
|
if etime > 0 then return end
|
||||||
etime = 10
|
etime = 10
|
||||||
for _,e in pairs(mcl_events.registered_events) do
|
for _,e in pairs(mcl_events.registered_events) do
|
||||||
local pp = e.cond_start()
|
local pp = e.cond_start()
|
||||||
if pp then
|
if pp then
|
||||||
|
--minetest.log("It's gonna start the raid maybe")
|
||||||
for _,p in pairs(pp) do
|
for _,p in pairs(pp) do
|
||||||
local start = true
|
local start = true
|
||||||
if e.exclusive_to_area then
|
if e.exclusive_to_area then
|
||||||
|
@ -109,82 +111,45 @@ function check_events(dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if start then
|
if start then
|
||||||
|
--minetest.log("It's gonna start the raid definitely")
|
||||||
start_event(p,e)
|
start_event(p,e)
|
||||||
elseif DBG then
|
elseif DBG then
|
||||||
mcl_log("event "..e.name.." already active at "..minetest.pos_to_string(vector.round(p.pos)))
|
mcl_log("[mcl_events] Event "..e.readable_name.." already active at "..minetest.pos_to_string(vector.round(p.pos)))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
--minetest.log("Do not start this raid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for idx,ae in pairs(active_events) do
|
||||||
|
local player_near = false
|
||||||
|
for _,pl in pairs(minetest.get_connected_players()) do
|
||||||
|
if ae.pos and vector.distance(pl:get_pos(),ae.pos) < 64 then player_near = true end
|
||||||
|
end
|
||||||
|
if ae.pos and not player_near then
|
||||||
|
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." aborted - no players near." )
|
||||||
|
active_events[idx] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_globalstep(check_events)
|
minetest.register_globalstep(check_events)
|
||||||
|
|
||||||
mcl_events.register_event("infestation",{
|
mcl_info.register_debug_field("Active Events",{
|
||||||
max_stage = 5,
|
level = 4,
|
||||||
health = 1,
|
func = function(pl,pos)
|
||||||
health_max = 1,
|
return tostring(#active_events)
|
||||||
cond_start = function(self)
|
end
|
||||||
local r = {}
|
|
||||||
for _,p in pairs(minetest.get_connected_players()) do
|
|
||||||
if p:get_meta():get_string("infestation-omen") == "yes" then
|
|
||||||
p:get_meta():set_string("infestation-omen","")
|
|
||||||
table.insert(r,p:get_pos())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #r > 0 then return r end
|
|
||||||
end,
|
|
||||||
on_start = function(self)
|
|
||||||
self.mobs = {}
|
|
||||||
self.health_max = 1
|
|
||||||
self.health = 0
|
|
||||||
end,
|
|
||||||
cond_progress = function(self)
|
|
||||||
local m = {}
|
|
||||||
local h = 0
|
|
||||||
for k,o in pairs(self.mobs) do
|
|
||||||
if o and o:get_pos() then
|
|
||||||
local l = o:get_luaentity()
|
|
||||||
h = h + l.health
|
|
||||||
table.insert(m,o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.mobs = m
|
|
||||||
self.health = h
|
|
||||||
self.percent = math.max(0,(self.health / self.health_max ) * 100)
|
|
||||||
if #m < 1 then
|
|
||||||
return true end
|
|
||||||
end,
|
|
||||||
on_stage_begin = function(self)
|
|
||||||
self.health_max = 0
|
|
||||||
for i=1,15 * self.stage do
|
|
||||||
local m = mcl_mobs.spawn(vector.add(self.pos,vector.new(math.random(20)-10,0,math.random(20)-10)),"mobs_mc:silverfish")
|
|
||||||
local l = m:get_luaentity()
|
|
||||||
if l then
|
|
||||||
self.health_max = self.health_max + l.health
|
|
||||||
table.insert(self.mobs,m)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
cond_complete = function(self)
|
|
||||||
local m = {}
|
|
||||||
for k,o in pairs(self.mobs) do
|
|
||||||
if o and o:get_pos() then
|
|
||||||
local l = o:get_luaentity()
|
|
||||||
table.insert(m,o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self.stage >= self.max_stage and #m < 1
|
|
||||||
end,
|
|
||||||
on_complete = function(self)
|
|
||||||
mcl_log("INFESTATION complete")
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_chatcommand("infest",{
|
minetest.register_chatcommand("event_start",{
|
||||||
privs = {debug = true},
|
privs = {debug = true},
|
||||||
func = function(n,param)
|
description = "Debug command to start events",
|
||||||
local p = minetest.get_player_by_name(n)
|
func = function(pname,param)
|
||||||
p:get_meta():set_string("infestation-omen","yes")
|
local p = minetest.get_player_by_name(pname)
|
||||||
|
local evdef = mcl_events.registered_events[param]
|
||||||
|
if not evdef then return false,"Event "..param.." doesn't exist.'" end
|
||||||
|
start_event({pos=p:get_pos(),player=pname,factor=1},evdef)
|
||||||
|
return true,"Started event "..param
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name = mcl_events
|
name = mcl_events
|
||||||
author = cora
|
author = cora
|
||||||
depends = mcl_mobs,mcl_bossbars
|
depends = mcl_mobs,mcl_bossbars, mcl_info
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1は爆発に巻き込まれた。
|
|
@ -138,6 +138,7 @@ mcl_vars.mg_nether_min = -29067 -- Carefully chosen to be at a mapchunk border
|
||||||
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
|
mcl_vars.mg_nether_max = mcl_vars.mg_nether_min + 128
|
||||||
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
|
mcl_vars.mg_bedrock_nether_bottom_min = mcl_vars.mg_nether_min
|
||||||
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
|
mcl_vars.mg_bedrock_nether_top_max = mcl_vars.mg_nether_max
|
||||||
|
mcl_vars.mg_nether_deco_max = mcl_vars.mg_nether_max -11 -- this is so ceiling decorations don't spill into other biomes as bedrock generation calls minetest.generate_decorations to put netherrack under the bedrock
|
||||||
if not superflat then
|
if not superflat then
|
||||||
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
|
mcl_vars.mg_bedrock_nether_bottom_max = mcl_vars.mg_bedrock_nether_bottom_min + 4
|
||||||
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
|
mcl_vars.mg_bedrock_nether_top_min = mcl_vars.mg_bedrock_nether_top_max - 4
|
||||||
|
|
|
@ -22,7 +22,21 @@ function table.update_nil(t, ...)
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default",false)
|
||||||
|
local LOG_MODULE = "[MCL2]"
|
||||||
|
function mcl_util.mcl_log (message, module, bypass_default_logger)
|
||||||
|
local selected_module = LOG_MODULE
|
||||||
|
if module then
|
||||||
|
selected_module = module
|
||||||
|
end
|
||||||
|
if (bypass_default_logger or LOGGING_ON) and message then
|
||||||
|
minetest.log(selected_module .. " " .. message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function mcl_util.file_exists(name)
|
function mcl_util.file_exists(name)
|
||||||
|
if type(name) ~= "string" then return end
|
||||||
local f = io.open(name)
|
local f = io.open(name)
|
||||||
if not f then
|
if not f then
|
||||||
return false
|
return false
|
||||||
|
@ -599,10 +613,12 @@ function mcl_util.get_object_name(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_util.replace_mob(obj, mob)
|
function mcl_util.replace_mob(obj, mob)
|
||||||
|
if not obj then return end
|
||||||
local rot = obj:get_yaw()
|
local rot = obj:get_yaw()
|
||||||
local pos = obj:get_pos()
|
local pos = obj:get_pos()
|
||||||
obj:remove()
|
obj:remove()
|
||||||
obj = minetest.add_entity(pos, mob)
|
obj = minetest.add_entity(pos, mob)
|
||||||
|
if not obj then return end
|
||||||
obj:set_yaw(rot)
|
obj:set_yaw(rot)
|
||||||
return obj
|
return obj
|
||||||
end
|
end
|
||||||
|
@ -704,10 +720,11 @@ function mcl_util.set_properties(obj,props)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_util.set_bone_position(obj,b,p,r) --bone,position,rotation
|
function mcl_util.set_bone_position(obj, bone, pos, rot)
|
||||||
local oldp,oldr=obj:get_bone_position(b)
|
local current_pos, current_rot = obj:get_bone_position(bone)
|
||||||
if vector.equals(vector.round(oldp),vector.round(p)) and vector.equals(vector.round(oldr),vector.round(r)) then
|
local pos_equal = not pos or vector.equals(vector.round(current_pos), vector.round(pos))
|
||||||
return
|
local rot_equal = not rot or vector.equals(vector.round(current_rot), vector.round(rot))
|
||||||
|
if not pos_equal or not rot_equal then
|
||||||
|
obj:set_bone_position(bone, pos or current_pos, rot or current_rot)
|
||||||
end
|
end
|
||||||
obj:set_bone_position(b,p,r)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,6 +109,7 @@ local function attach_object(self, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function detach_object(obj, change_pos)
|
local function detach_object(obj, change_pos)
|
||||||
|
if not obj or not obj:get_pos() then return end
|
||||||
obj:set_detach()
|
obj:set_detach()
|
||||||
obj:set_properties({visual_size = get_visual_size(obj)})
|
obj:set_properties({visual_size = get_visual_size(obj)})
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# textdomain: mcl_boats
|
||||||
|
Acacia Boat=アカシアのボート
|
||||||
|
Birch Boat=シラカバのボート
|
||||||
|
Boat=ボート
|
||||||
|
Boats are used to travel on the surface of water.=ボートは、水面を移動するために使われます。
|
||||||
|
Dark Oak Boat=ダークオークのボート
|
||||||
|
Jungle Boat=ジャングルのボート
|
||||||
|
Oak Boat=オークのボート
|
||||||
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=水源を右クリックすると、ボートが配置されます。ボートを右クリックすると、乗り込みます。[左][右]で舵取り、[前]で加速、[後]で減速または後退します。[スニーク]でボートから離れ、ボートをパンチするとアイテムとしてドロップします。
|
||||||
|
Spruce Boat=トウヒのボート
|
||||||
|
Water vehicle=水上用の乗物
|
||||||
|
Sneak to dismount=スニークで降りる
|
||||||
|
Obsidian Boat=黒曜石のボート
|
|
@ -0,0 +1,36 @@
|
||||||
|
# mcl_dripping
|
||||||
|
|
||||||
|
Dripping Mod by kddekadenz, modified for MineClone 2 by Wuzzy, NO11 and AFCM
|
||||||
|
|
||||||
|
## Manual
|
||||||
|
|
||||||
|
- drops are generated rarely under solid nodes
|
||||||
|
- they will stay some time at the generated block and than they fall down
|
||||||
|
- when they collide with the ground, a sound is played and they are destroyed
|
||||||
|
|
||||||
|
Water and Lava have builtin drops registered.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
code & sounds: CC0
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```lua
|
||||||
|
mcl_dripping.register_drop({
|
||||||
|
-- The group the liquid's nodes belong to
|
||||||
|
liquid = "water",
|
||||||
|
-- The texture used (particles will take a random 2x2 area of it)
|
||||||
|
texture = "default_water_source_animated.png",
|
||||||
|
-- Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
||||||
|
light = 1,
|
||||||
|
-- The nodes (or node group) the particles will spawn under
|
||||||
|
nodes = { "group:opaque", "group:leaves" },
|
||||||
|
-- The sound that will be played then the particle detaches from the roof, see SimpleSoundSpec in lua_api.txt
|
||||||
|
sound = "drippingwater_drip",
|
||||||
|
-- The interval for the ABM to run
|
||||||
|
interval = 60,
|
||||||
|
-- The chance of the ABM
|
||||||
|
chance = 10,
|
||||||
|
})
|
||||||
|
```
|
|
@ -3,53 +3,99 @@
|
||||||
-- License of code, textures & sounds: CC0
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
local math = math
|
local math = math
|
||||||
local function make_drop(pos,liquid,sound,interval)
|
|
||||||
|
mcl_dripping = {}
|
||||||
|
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
|
---@param liquid string
|
||||||
|
---@param sound SimpleSoundSpec
|
||||||
|
---@param interval integer
|
||||||
|
---@param texture string
|
||||||
|
local function make_drop(pos, liquid, sound, interval, texture)
|
||||||
local pt = {
|
local pt = {
|
||||||
velocity = vector.new(0,0,0),
|
velocity = vector.zero(),
|
||||||
collision_removal = false,
|
collision_removal = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
local t = math.random() + math.random(1, interval)
|
local t = math.random() + math.random(1, interval)
|
||||||
minetest.after(t,function()
|
|
||||||
|
minetest.after(t, function()
|
||||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||||
pt.pos = vector.offset(pos,x,-0.52,z)
|
|
||||||
pt.acceleration = vector.new(0,0,0)
|
pt.pos = vector.offset(pos, x, -0.52, z)
|
||||||
|
pt.acceleration = vector.zero()
|
||||||
pt.collisiondetection = false
|
pt.collisiondetection = false
|
||||||
pt.expirationtime = t
|
pt.expirationtime = t
|
||||||
|
|
||||||
pt.texture="[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"
|
pt.texture = "[combine:2x2:" ..
|
||||||
|
-math.random(1, 16) .. "," .. -math.random(1, 16) .. "=" .. texture
|
||||||
|
|
||||||
minetest.add_particle(pt)
|
minetest.add_particle(pt)
|
||||||
minetest.after(t,function()
|
|
||||||
pt.acceleration = vector.new(0,-5,0)
|
minetest.after(t, function()
|
||||||
|
pt.acceleration = vector.new(0, -5, 0)
|
||||||
pt.collisiondetection = true
|
pt.collisiondetection = true
|
||||||
pt.expirationtime = math.random() + math.random(1, interval/2)
|
pt.expirationtime = math.random() + math.random(1, interval / 2)
|
||||||
|
|
||||||
minetest.add_particle(pt)
|
minetest.add_particle(pt)
|
||||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = pos, gain = 0.5, max_hear_distance = 8}, true)
|
|
||||||
|
minetest.sound_play(sound, { pos = pos, gain = 0.5, max_hear_distance = 8 },
|
||||||
|
true)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function register_drop(liquid, glow, sound, nodes, interval, chance)
|
---@class mcl_dripping_drop_definition
|
||||||
|
---@field liquid string The group the liquid's nodes belong to
|
||||||
|
---@field texture string The texture used (particles will take a random 2x2 area of it)
|
||||||
|
---@field light integer Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
||||||
|
---@field nodes string[] The nodes (or node group) the particles will spawn under
|
||||||
|
---@field interval integer The interval for the ABM to run
|
||||||
|
---@field chance integer The chance of the ABM
|
||||||
|
---@field sound SimpleSoundSpec The sound that will be played then the particle detaches from the roof
|
||||||
|
|
||||||
|
---@param def mcl_dripping_drop_definition
|
||||||
|
function mcl_dripping.register_drop(def)
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Create drops",
|
label = "Create drops",
|
||||||
nodenames = nodes,
|
nodenames = def.nodes,
|
||||||
neighbors = {"group:" .. liquid},
|
neighbors = { "group:" .. def.liquid },
|
||||||
interval = interval,
|
interval = def.interval,
|
||||||
chance = chance,
|
chance = def.chance,
|
||||||
action = function(pos)
|
action = function(pos)
|
||||||
local r = math.ceil(interval / 20)
|
local below = minetest.get_node(vector.offset(pos,0,-1,0)).name
|
||||||
local nn=minetest.find_nodes_in_area(vector.offset(pos,-r,0,-r),vector.offset(pos,r,0,r),nodes)
|
if below ~= "air" then return end
|
||||||
|
local r = math.ceil(def.interval / 20)
|
||||||
|
local nn = minetest.find_nodes_in_area(vector.offset(pos, -r, 0, -r), vector.offset(pos, r, 0, r), def.nodes)
|
||||||
--start a bunch of particle cycles to be able to get away
|
--start a bunch of particle cycles to be able to get away
|
||||||
--with longer abm cycles
|
--with longer abm cycles
|
||||||
table.shuffle(nn)
|
table.shuffle(nn)
|
||||||
for i=1,math.random(#nn) do
|
for i = 1, math.random(#nn) do
|
||||||
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, liquid) ~= 0
|
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, def.liquid) ~= 0 then
|
||||||
and minetest.get_node(vector.offset(nn[i], 0, -1, 0)).name == "air" then
|
make_drop(nn[i], def.liquid, def.sound, def.interval, def.texture)
|
||||||
make_drop(nn[i],liquid,sound,interval)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
register_drop("water", 1, "", {"group:opaque", "group:leaves"},60,10)
|
mcl_dripping.register_drop({
|
||||||
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"},60,10)
|
liquid = "water",
|
||||||
|
texture = "default_water_source_animated.png",
|
||||||
|
light = 1,
|
||||||
|
nodes = { "group:opaque", "group:leaves" },
|
||||||
|
sound = "drippingwater_drip",
|
||||||
|
interval = 60.3,
|
||||||
|
chance = 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_dripping.register_drop({
|
||||||
|
liquid = "lava",
|
||||||
|
texture = "default_lava_source_animated.png",
|
||||||
|
light = math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3),
|
||||||
|
nodes = { "group:opaque" },
|
||||||
|
sound = "drippingwater_lavadrip",
|
||||||
|
interval = 110.1,
|
||||||
|
chance = 10,
|
||||||
|
})
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
Dripping Mod
|
|
||||||
by kddekadenz
|
|
||||||
|
|
||||||
modified for MineClone 2 by Wuzzy and NO11
|
|
||||||
|
|
||||||
|
|
||||||
Installing instructions:
|
|
||||||
|
|
||||||
1. Copy the mcl_dripping mod folder into games/gamemode/mods
|
|
||||||
|
|
||||||
2. Start game and enjoy :)
|
|
||||||
|
|
||||||
|
|
||||||
Manual:
|
|
||||||
|
|
||||||
-> drops are generated rarely under solid nodes
|
|
||||||
-> they will stay some time at the generated block and than they fall down
|
|
||||||
-> when they collide with the ground, a sound is played and they are destroyed
|
|
||||||
|
|
||||||
|
|
||||||
License:
|
|
||||||
|
|
||||||
code & sounds: CC0
|
|
||||||
|
|
||||||
|
|
||||||
Changelog:
|
|
||||||
|
|
||||||
16.04.2012 - first release
|
|
||||||
28.04.2012 - drops are now 3D; added lava drops; fixed generating of drops (not at edges now)
|
|
|
@ -2,6 +2,10 @@ mcl_entity_invs = {}
|
||||||
|
|
||||||
local open_invs = {}
|
local open_invs = {}
|
||||||
|
|
||||||
|
local function mcl_log (message)
|
||||||
|
mcl_util.mcl_log (message, "[Entity Invs]")
|
||||||
|
end
|
||||||
|
|
||||||
local function check_distance(inv,player,count)
|
local function check_distance(inv,player,count)
|
||||||
for _,o in pairs(minetest.get_objects_inside_radius(player:get_pos(),5)) do
|
for _,o in pairs(minetest.get_objects_inside_radius(player:get_pos(),5)) do
|
||||||
local l = o:get_luaentity()
|
local l = o:get_luaentity()
|
||||||
|
@ -22,20 +26,25 @@ local inv_callbacks = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function load_inv(ent,size)
|
function mcl_entity_invs.load_inv(ent,size)
|
||||||
|
mcl_log("load_inv")
|
||||||
if not ent._inv_id then return end
|
if not ent._inv_id then return end
|
||||||
|
mcl_log("load_inv 2")
|
||||||
local inv = minetest.get_inventory({type="detached", name=ent._inv_id})
|
local inv = minetest.get_inventory({type="detached", name=ent._inv_id})
|
||||||
if not inv then
|
if not inv then
|
||||||
|
mcl_log("load_inv 3")
|
||||||
inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks)
|
inv = minetest.create_detached_inventory(ent._inv_id, inv_callbacks)
|
||||||
inv:set_size("main", size)
|
inv:set_size("main", size)
|
||||||
if ent._items then
|
if ent._items then
|
||||||
inv:set_list("main",ent._items)
|
inv:set_list("main",ent._items)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
mcl_log("load_inv 4")
|
||||||
end
|
end
|
||||||
return inv
|
return inv
|
||||||
end
|
end
|
||||||
|
|
||||||
local function save_inv(ent)
|
function mcl_entity_invs.save_inv(ent)
|
||||||
if ent._inv then
|
if ent._inv then
|
||||||
ent._items = {}
|
ent._items = {}
|
||||||
for i,it in ipairs(ent._inv:get_list("main")) do
|
for i,it in ipairs(ent._inv:get_list("main")) do
|
||||||
|
@ -46,32 +55,60 @@ local function save_inv(ent)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function load_default_formspec (ent, text)
|
||||||
|
text = text or ""
|
||||||
|
|
||||||
|
local invent_size = ent._inv_size
|
||||||
|
local div_by_two = invent_size % 2 == 0
|
||||||
|
local div_by_three = invent_size % 3 == 0
|
||||||
|
|
||||||
|
--mcl_log("Div by 3: ".. tostring(div_by_three))
|
||||||
|
--mcl_log("Div by 2: ".. tostring(div_by_two))
|
||||||
|
--mcl_log("invent_size: ".. tostring(invent_size))
|
||||||
|
local rows = 3
|
||||||
|
if invent_size > 18 or (div_by_three == true and invent_size > 8) then
|
||||||
|
--mcl_log("Div by 3")
|
||||||
|
rows = 3
|
||||||
|
elseif (div_by_two == true and invent_size > 3) or invent_size > 9 then
|
||||||
|
--mcl_log("Div by 2")
|
||||||
|
rows = 2
|
||||||
|
else
|
||||||
|
--mcl_log("Not div by 2 or 3")
|
||||||
|
rows = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--local rows = 3
|
||||||
|
local cols = (math.ceil(ent._inv_size/rows))
|
||||||
|
local spacing = (9 - cols) / 2
|
||||||
|
|
||||||
|
local formspec = "size[9,8.75]"
|
||||||
|
.. "label[0,0;" .. minetest.formspec_escape(
|
||||||
|
minetest.colorize("#313131", ent._inv_title .. " ".. text)) .. "]"
|
||||||
|
.. "list[detached:"..ent._inv_id..";main;"..spacing..",0.5;"..cols..","..rows..";]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(spacing,0.5,cols,rows)
|
||||||
|
.. "label[0,4.0;" .. minetest.formspec_escape(
|
||||||
|
minetest.colorize("#313131", "Inventory")) .. "]"
|
||||||
|
.. "list[current_player;main;0,4.5;9,3;9]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(0,4.5,9,3)
|
||||||
|
.. "list[current_player;main;0,7.74;9,1;]"
|
||||||
|
.. mcl_formspec.get_itemslot_bg(0,7.74,9,1)
|
||||||
|
.. "listring[detached:"..ent._inv_id..";main]"
|
||||||
|
.. "listring[current_player;main]"
|
||||||
|
return formspec
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function mcl_entity_invs.show_inv_form(ent,player,text)
|
function mcl_entity_invs.show_inv_form(ent,player,text)
|
||||||
if not ent._inv_id then return end
|
if not ent._inv_id then return end
|
||||||
if not open_invs[ent] then
|
if not open_invs[ent] then
|
||||||
open_invs[ent] = 0
|
open_invs[ent] = 0
|
||||||
end
|
end
|
||||||
text = text or ""
|
ent._inv = mcl_entity_invs.load_inv(ent,ent._inv_size)
|
||||||
ent._inv = load_inv(ent,ent._inv_size)
|
|
||||||
open_invs[ent] = open_invs[ent] + 1
|
open_invs[ent] = open_invs[ent] + 1
|
||||||
|
|
||||||
local playername = player:get_player_name()
|
local playername = player:get_player_name()
|
||||||
local rows = 3
|
|
||||||
local cols = (math.ceil(ent._inv_size/rows))
|
minetest.show_formspec(playername, ent._inv_id, load_default_formspec (ent, text))
|
||||||
local spacing = (9 - cols) / 2
|
|
||||||
local formspec = "size[9,8.75]"
|
|
||||||
.. "label[0,0;" .. minetest.formspec_escape(
|
|
||||||
minetest.colorize("#313131", ent._inv_title .. " ".. text)) .. "]"
|
|
||||||
.. "list[detached:"..ent._inv_id..";main;"..spacing..",0.5;"..cols..","..rows..";]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(spacing,0.5,cols,rows)
|
|
||||||
.. "label[0,4.0;" .. minetest.formspec_escape(
|
|
||||||
minetest.colorize("#313131", "Inventory")) .. "]"
|
|
||||||
.. "list[current_player;main;0,4.5;9,3;9]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(0,4.5,9,3)
|
|
||||||
.. "list[current_player;main;0,7.74;9,1;]"
|
|
||||||
.. mcl_formspec.get_itemslot_bg(0,7.74,9,1)
|
|
||||||
.. "listring[detached:"..ent._inv_id..";main]"
|
|
||||||
.. "listring[current_player;main]"
|
|
||||||
minetest.show_formspec(playername,ent._inv_id,formspec)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function drop_inv(ent)
|
local function drop_inv(ent)
|
||||||
|
@ -85,9 +122,9 @@ local function drop_inv(ent)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function on_remove(self,killer,oldf)
|
local function on_remove(self,killer,oldf)
|
||||||
save_inv(self)
|
mcl_entity_invs.save_inv(self)
|
||||||
drop_inv(self)
|
drop_inv(self)
|
||||||
if oldf then return oldf(self,killer) end
|
if oldf then return oldf(self,killer) end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
@ -95,7 +132,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
if formname == k._inv_id then
|
if formname == k._inv_id then
|
||||||
open_invs[k] = open_invs[k] - 1
|
open_invs[k] = open_invs[k] - 1
|
||||||
if open_invs[k] < 1 then
|
if open_invs[k] < 1 then
|
||||||
save_inv(k)
|
mcl_entity_invs.save_inv(k)
|
||||||
open_invs[k] = nil
|
open_invs[k] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -151,7 +188,7 @@ function mcl_entity_invs.register_inv(entity_name,show_name,size,no_on_righclick
|
||||||
|
|
||||||
local old_ode = minetest.registered_entities[entity_name].on_deactivate
|
local old_ode = minetest.registered_entities[entity_name].on_deactivate
|
||||||
minetest.registered_entities[entity_name].on_deactivate = function(self,removal)
|
minetest.registered_entities[entity_name].on_deactivate = function(self,removal)
|
||||||
save_inv(self)
|
mcl_entity_invs.save_inv(self)
|
||||||
if removal then
|
if removal then
|
||||||
on_remove(self)
|
on_remove(self)
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,3 +26,9 @@ http://minetest.net/
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
---------
|
||||||
|
|
||||||
|
Alterations and contributions are released under GNU GPLv3 after 11/11/2022 and for contributors:
|
||||||
|
|
||||||
|
AncientMariner/ancientmarinerdev
|
|
@ -6,6 +6,16 @@ local pool = {}
|
||||||
|
|
||||||
local tick = false
|
local tick = false
|
||||||
|
|
||||||
|
|
||||||
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities",false)
|
||||||
|
local function mcl_log (message)
|
||||||
|
if LOGGING_ON then
|
||||||
|
mcl_util.mcl_log (message, "[Item Entities]", true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
local name
|
local name
|
||||||
name = player:get_player_name()
|
name = player:get_player_name()
|
||||||
|
@ -375,6 +385,116 @@ local function cxcz(o, cw, one, zero)
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function hopper_take_item (self, pos)
|
||||||
|
--mcl_log("self.itemstring: ".. self.itemstring)
|
||||||
|
--mcl_log("self.itemstring: ".. minetest.pos_to_string(pos))
|
||||||
|
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, 2)
|
||||||
|
|
||||||
|
if objs and self.itemstring then
|
||||||
|
--mcl_log("there is an itemstring. Number of objs: ".. #objs)
|
||||||
|
|
||||||
|
for k,v in pairs(objs) do
|
||||||
|
local ent = v:get_luaentity()
|
||||||
|
|
||||||
|
-- Don't forget actual hoppers
|
||||||
|
if ent and ent.name == "mcl_minecarts:hopper_minecart" then
|
||||||
|
local taken_items = false
|
||||||
|
|
||||||
|
mcl_log("ent.name: ".. tostring(ent.name))
|
||||||
|
mcl_log("ent pos: ".. tostring(ent.object:get_pos()))
|
||||||
|
|
||||||
|
local inv = mcl_entity_invs.load_inv(ent,5)
|
||||||
|
|
||||||
|
if not inv then
|
||||||
|
mcl_log("No inv")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_itemstack = ItemStack(self.itemstring)
|
||||||
|
|
||||||
|
mcl_log("inv. size: " .. ent._inv_size)
|
||||||
|
if inv:room_for_item("main", current_itemstack) then
|
||||||
|
mcl_log("Room")
|
||||||
|
inv:add_item("main", current_itemstack)
|
||||||
|
self.object:get_luaentity().itemstring = ""
|
||||||
|
self.object:remove()
|
||||||
|
taken_items = true
|
||||||
|
else
|
||||||
|
mcl_log("no Room")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not taken_items then
|
||||||
|
local items_remaining = current_itemstack:get_count()
|
||||||
|
|
||||||
|
-- This will take part of a floating item stack if no slot can hold the full amount
|
||||||
|
for i = 1, ent._inv_size,1 do
|
||||||
|
local stack = inv:get_stack("main", i)
|
||||||
|
|
||||||
|
mcl_log("i: " .. tostring(i))
|
||||||
|
mcl_log("Items remaining: " .. items_remaining)
|
||||||
|
mcl_log("Name: " .. tostring(stack:get_name()))
|
||||||
|
|
||||||
|
if current_itemstack:get_name() == stack:get_name() then
|
||||||
|
mcl_log("We have a match. Name: " .. tostring(stack:get_name()))
|
||||||
|
|
||||||
|
local room_for = stack:get_stack_max() - stack:get_count()
|
||||||
|
mcl_log("Room for: " .. tostring(room_for))
|
||||||
|
|
||||||
|
if room_for == 0 then
|
||||||
|
-- Do nothing
|
||||||
|
mcl_log("No room")
|
||||||
|
elseif room_for < items_remaining then
|
||||||
|
mcl_log("We have more items remaining than space")
|
||||||
|
|
||||||
|
items_remaining = items_remaining - room_for
|
||||||
|
stack:set_count(stack:get_stack_max())
|
||||||
|
inv:set_stack("main", i, stack)
|
||||||
|
taken_items = true
|
||||||
|
else
|
||||||
|
local new_stack_size = stack:get_count() + items_remaining
|
||||||
|
stack:set_count(new_stack_size)
|
||||||
|
mcl_log("We have more than enough space. Now holds: " .. new_stack_size)
|
||||||
|
|
||||||
|
inv:set_stack("main", i, stack)
|
||||||
|
items_remaining = 0
|
||||||
|
|
||||||
|
self.object:get_luaentity().itemstring = ""
|
||||||
|
self.object:remove()
|
||||||
|
|
||||||
|
taken_items = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_log("Count: " .. tostring(stack:get_count()))
|
||||||
|
mcl_log("stack max: " .. tostring(stack:get_stack_max()))
|
||||||
|
--mcl_log("Is it empty: " .. stack:to_string())
|
||||||
|
end
|
||||||
|
|
||||||
|
if i == ent._inv_size and taken_items then
|
||||||
|
mcl_log("We are on last item and still have items left. Set final stack size: " .. items_remaining)
|
||||||
|
current_itemstack:set_count(items_remaining)
|
||||||
|
--mcl_log("Itemstack2: " .. current_itemstack:to_string())
|
||||||
|
self.itemstring = current_itemstack:to_string()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--Add in, and delete
|
||||||
|
if taken_items then
|
||||||
|
mcl_log("Saving")
|
||||||
|
mcl_entity_invs.save_inv(ent)
|
||||||
|
return taken_items
|
||||||
|
else
|
||||||
|
mcl_log("No need to save")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
minetest.register_entity(":__builtin:item", {
|
minetest.register_entity(":__builtin:item", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
|
@ -648,6 +768,12 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
|
|
||||||
|
-- If hopper has taken item, it has gone, and no operations should be conducted on this item
|
||||||
|
if hopper_take_item(self, p) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = minetest.get_node_or_nil(p)
|
||||||
local in_unloaded = (node == nil)
|
local in_unloaded = (node == nil)
|
||||||
|
|
||||||
|
|
|
@ -770,8 +770,9 @@ register_minecart(
|
||||||
},
|
},
|
||||||
"mcl_minecarts_minecart_hopper.png",
|
"mcl_minecarts_minecart_hopper.png",
|
||||||
{"mcl_minecarts:minecart", "mcl_hoppers:hopper"},
|
{"mcl_minecarts:minecart", "mcl_hoppers:hopper"},
|
||||||
nil, nil, false
|
nil, nil, true
|
||||||
)
|
)
|
||||||
|
mcl_entity_invs.register_inv("mcl_minecarts:hopper_minecart", "Hopper Minecart", 5, false, true)
|
||||||
|
|
||||||
-- Minecart with TNT
|
-- Minecart with TNT
|
||||||
register_minecart(
|
register_minecart(
|
||||||
|
@ -839,16 +840,14 @@ minetest.register_craft({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
minetest.register_craft({
|
||||||
|
|
||||||
--[[minetest.register_craft({
|
|
||||||
output = "mcl_minecarts:hopper_minecart",
|
output = "mcl_minecarts:hopper_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_hoppers:hopper"},
|
{"mcl_hoppers:hopper"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
--]]
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:chest_minecart",
|
output = "mcl_minecarts:chest_minecart",
|
||||||
|
@ -863,5 +862,4 @@ if has_mcl_wip then
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# textdomain: mcl_minecarts
|
||||||
|
Minecart=トロッコ
|
||||||
|
Minecarts can be used for a quick transportion on rails.=トロッコは、レールを使った高速輸送を可能にします。
|
||||||
|
Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type.=トロッコはレール上にしか乗らない為、常に線路に沿って走ります。直進できない丁字路では、取り敢えず左折します。速度はレールの種類によって異なります。
|
||||||
|
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=レールの上にトロッコを置けます。右クリックで乗り込みます。パンチすると動き出します。
|
||||||
|
To obtain the minecart, punch it while holding down the sneak key.=トロッコを入手するには、スニークキーを押しながらパンチします。
|
||||||
|
A minecart with TNT is an explosive vehicle that travels on rail.=TNT付きトロッコは、レール上を行きかう爆薬車両です。
|
||||||
|
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=レール上に配置。パンチで移動。TNTが着火するのは、火打石と打金を使った時か、稼動中のアクティベーターレール上にトロッコが乗った時です。
|
||||||
|
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=トロッコとTNTを入手するには、スニークキーを押しながらパンチしてください。TNTに火が着いていた場合は、無理です。
|
||||||
|
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=かまど付きトロッコは、レール上を走行する車両です。燃料で自走できます。
|
||||||
|
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=レールの上に置きます。石炭を与えると、かまどが長時間燃え続け、トロッコが自走可能になります。パンチすると動き出します。
|
||||||
|
To obtain the minecart and furnace, punch them while holding down the sneak key.=トロッコとかまどを入手するには、スニークキーを押しながらパンチします。
|
||||||
|
Minecart with Chest=チェスト付きトロッコ
|
||||||
|
Minecart with Furnace=かまど付きトロッコ
|
||||||
|
Minecart with Command Block=コマンドブロック付きトロッコ
|
||||||
|
Minecart with Hopper=ホッパー付きトロッコ
|
||||||
|
Minecart with TNT=TNT付きトロッコ
|
||||||
|
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=地面に置いて線路を作ると、レール同士が自動的につながり、必要に応じてカーブや丁字路、踏切、坂道などに変化します。
|
||||||
|
Rail=レール
|
||||||
|
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=レールを利用して、トロッコの輸送路が敷けます。普通のレールは、摩擦の関係でトロッコが少しずつ減速していきます。
|
||||||
|
Powered Rail=パワードレール
|
||||||
|
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=レールを利用して、トロッコの輸送路が敷けます。パワードレールは、トロッコを加速させたり、ブレーキをかけたりできます。
|
||||||
|
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=レッドストーン動力なしだと、このレールはトロッコにブレーキをかけます。このレールでトロッコを加速させるには、レッドストーン動力を供給してください。
|
||||||
|
Activator Rail=アクティベーターレール
|
||||||
|
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=レールを利用して、トロッコの輸送路が敷けます。アクティベーターレールは、特殊なトロッコを作動させるために使われます。
|
||||||
|
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=このレールでトロッコを作動させるには、レッドストーン動力を与えたレール上にトロッコを送り込みます。
|
||||||
|
Detector Rail=
|
||||||
|
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=レールを利用して、トロッコの輸送路が敷けます。ディテクターレールは、その上にあるトロッコを検知でき、その際レッドストーン機構の動力源となります。
|
||||||
|
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=トロッコを検知してレッドストーン動力を供給するには、レッドストーン導線またはレッドストーン機構に接続し、任意のトロッコをレール上に送り込みます。
|
||||||
|
Track for minecarts=トロッコ用の線路
|
||||||
|
Speed up when powered, slow down when not powered=稼動中は加速、非稼動中は減速
|
||||||
|
Activates minecarts when powered=稼動中はトロッコを作動
|
||||||
|
Emits redstone power when a minecart is detected=トロッコを検知するとレッドストーン動力を放出
|
||||||
|
Vehicle for fast travel on rails=レール上を快速移動するための車両
|
||||||
|
Can be ignited by tools or powered activator rail=道具や稼動中のアクティベーターレールにより着火が可能
|
||||||
|
Sneak to dismount=スニークで降りる
|
|
@ -0,0 +1,352 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local MAX_MOB_NAME_LENGTH = 30
|
||||||
|
local HORNY_TIME = 30
|
||||||
|
local HORNY_AGAIN_TIME = 300
|
||||||
|
local CHILD_GROW_TIME = 60*20
|
||||||
|
|
||||||
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
||||||
|
|
||||||
|
local LOG_MODULE = "[mcl_mobs]"
|
||||||
|
local function mcl_log (message)
|
||||||
|
if LOGGING_ON and message then
|
||||||
|
minetest.log(LOG_MODULE .. " " .. message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No-op in MCL2 (capturing mobs is not possible).
|
||||||
|
-- Provided for compability with Mobs Redo
|
||||||
|
function mcl_mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, force_take, replacewith)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- No-op in MCL2 (protecting mobs is not possible).
|
||||||
|
function mcl_mobs:protect(self, clicker)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- feeding, taming and breeding (thanks blert2112)
|
||||||
|
function mob_class:feed_tame(clicker, feed_count, breed, tame, notake)
|
||||||
|
if not self.follow then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
-- can eat/tame with item in hand
|
||||||
|
if self.nofollow or self:follow_holding(clicker) then
|
||||||
|
local consume_food = false
|
||||||
|
|
||||||
|
-- tame if not still a baby
|
||||||
|
|
||||||
|
if tame and not self.child then
|
||||||
|
if not self.owner or self.owner == "" then
|
||||||
|
self.tamed = true
|
||||||
|
self.owner = clicker:get_player_name()
|
||||||
|
consume_food = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- increase health
|
||||||
|
|
||||||
|
if self.health < self.hp_max and not consume_food then
|
||||||
|
consume_food = true
|
||||||
|
self.health = math.min(self.health + 4, self.hp_max)
|
||||||
|
|
||||||
|
if self.htimer < 1 then
|
||||||
|
self.htimer = 5
|
||||||
|
end
|
||||||
|
self.object:set_hp(self.health)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- make children grow quicker
|
||||||
|
|
||||||
|
if not consume_food and self.child == true then
|
||||||
|
consume_food = true
|
||||||
|
-- deduct 10% of the time to adulthood
|
||||||
|
self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- breed animals
|
||||||
|
|
||||||
|
if breed and not consume_food and self.hornytimer == 0 and not self.horny then
|
||||||
|
self.food = (self.food or 0) + 1
|
||||||
|
consume_food = true
|
||||||
|
if self.food >= feed_count then
|
||||||
|
self.food = 0
|
||||||
|
self.horny = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:update_tag()
|
||||||
|
-- play a sound if the animal used the item and take the item if not in creative
|
||||||
|
if consume_food then
|
||||||
|
-- don't consume food if clicker is in creative
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) and not notake then
|
||||||
|
local item = clicker:get_wielded_item()
|
||||||
|
item:take_item()
|
||||||
|
clicker:set_wielded_item(item)
|
||||||
|
end
|
||||||
|
-- always play the eat sound if food is used, even in creative
|
||||||
|
self:mob_sound("eat", nil, true)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- make sound when the mob doesn't want food
|
||||||
|
self:mob_sound("random", true)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Spawn a child
|
||||||
|
function mcl_mobs.spawn_child(pos, mob_type)
|
||||||
|
local child = minetest.add_entity(pos, mob_type)
|
||||||
|
if not child then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local ent = child:get_luaentity()
|
||||||
|
mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5)
|
||||||
|
|
||||||
|
ent.child = true
|
||||||
|
|
||||||
|
local textures
|
||||||
|
-- using specific child texture (if found)
|
||||||
|
if ent.child_texture then
|
||||||
|
textures = ent.child_texture[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- and resize to half height
|
||||||
|
child:set_properties({
|
||||||
|
textures = textures,
|
||||||
|
visual_size = {
|
||||||
|
x = ent.base_size.x * .5,
|
||||||
|
y = ent.base_size.y * .5,
|
||||||
|
},
|
||||||
|
collisionbox = {
|
||||||
|
ent.base_colbox[1] * .5,
|
||||||
|
ent.base_colbox[2] * .5,
|
||||||
|
ent.base_colbox[3] * .5,
|
||||||
|
ent.base_colbox[4] * .5,
|
||||||
|
ent.base_colbox[5] * .5,
|
||||||
|
ent.base_colbox[6] * .5,
|
||||||
|
},
|
||||||
|
selectionbox = {
|
||||||
|
ent.base_selbox[1] * .5,
|
||||||
|
ent.base_selbox[2] * .5,
|
||||||
|
ent.base_selbox[3] * .5,
|
||||||
|
ent.base_selbox[4] * .5,
|
||||||
|
ent.base_selbox[5] * .5,
|
||||||
|
ent.base_selbox[6] * .5,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
ent.animation = ent._child_animations
|
||||||
|
ent._current_animation = nil
|
||||||
|
ent:set_animation("stand")
|
||||||
|
|
||||||
|
return child
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find two animals of same type and breed if nearby and horny
|
||||||
|
function mob_class:check_breeding()
|
||||||
|
|
||||||
|
--mcl_log("In breed function")
|
||||||
|
-- child takes a long time before growing into adult
|
||||||
|
if self.child == true then
|
||||||
|
|
||||||
|
-- When a child, hornytimer is used to count age until adulthood
|
||||||
|
self.hornytimer = self.hornytimer + 1
|
||||||
|
|
||||||
|
if self.hornytimer >= CHILD_GROW_TIME then
|
||||||
|
|
||||||
|
self.child = false
|
||||||
|
self.hornytimer = 0
|
||||||
|
|
||||||
|
self.object:set_properties({
|
||||||
|
textures = self.base_texture,
|
||||||
|
mesh = self.base_mesh,
|
||||||
|
visual_size = self.base_size,
|
||||||
|
collisionbox = self.base_colbox,
|
||||||
|
selectionbox = self.base_selbox,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- custom function when child grows up
|
||||||
|
if self.on_grown then
|
||||||
|
self.on_grown(self)
|
||||||
|
else
|
||||||
|
-- jump when fully grown so as not to fall into ground
|
||||||
|
self.object:set_velocity({
|
||||||
|
x = 0,
|
||||||
|
y = self.jump_height*3,
|
||||||
|
z = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
self.animation = nil
|
||||||
|
local anim = self._current_animation
|
||||||
|
self._current_animation = nil -- Mobs Redo does nothing otherwise
|
||||||
|
self:set_animation(anim)
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- horny animal can mate for HORNY_TIME seconds,
|
||||||
|
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
||||||
|
if self.horny == true
|
||||||
|
and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then
|
||||||
|
|
||||||
|
self.hornytimer = self.hornytimer + 1
|
||||||
|
|
||||||
|
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
||||||
|
self.hornytimer = 0
|
||||||
|
self.horny = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find another same animal who is also horny and mate if nearby
|
||||||
|
if self.horny == true
|
||||||
|
and self.hornytimer <= HORNY_TIME then
|
||||||
|
|
||||||
|
mcl_log("In breed function. All good. Do the magic.")
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
mcl_mobs.effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1)
|
||||||
|
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, 3)
|
||||||
|
local num = 0
|
||||||
|
local ent = nil
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
ent = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
-- check for same animal with different colour
|
||||||
|
local canmate = false
|
||||||
|
|
||||||
|
if ent then
|
||||||
|
|
||||||
|
if ent.name == self.name then
|
||||||
|
canmate = true
|
||||||
|
else
|
||||||
|
local entname = string.split(ent.name,":")
|
||||||
|
local selfname = string.split(self.name,":")
|
||||||
|
|
||||||
|
if entname[1] == selfname[1] then
|
||||||
|
entname = string.split(entname[2],"_")
|
||||||
|
selfname = string.split(selfname[2],"_")
|
||||||
|
|
||||||
|
if entname[1] == selfname[1] then
|
||||||
|
canmate = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if canmate then mcl_log("In breed function. Can mate.") end
|
||||||
|
|
||||||
|
if ent
|
||||||
|
and canmate == true
|
||||||
|
and ent.horny == true
|
||||||
|
and ent.hornytimer <= HORNY_TIME then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- found your mate? then have a baby
|
||||||
|
if num > 1 then
|
||||||
|
|
||||||
|
self.hornytimer = HORNY_TIME + 1
|
||||||
|
ent.hornytimer = HORNY_TIME + 1
|
||||||
|
|
||||||
|
-- spawn baby
|
||||||
|
|
||||||
|
|
||||||
|
minetest.after(5, function(parent1, parent2, pos)
|
||||||
|
if not parent1.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not parent2.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_experience.throw_xp(pos, math.random(1, 7))
|
||||||
|
|
||||||
|
-- custom breed function
|
||||||
|
if parent1.on_breed then
|
||||||
|
-- when false, skip going any further
|
||||||
|
if parent1.on_breed(parent1, parent2) == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||||
|
|
||||||
|
local ent_c = child:get_luaentity()
|
||||||
|
|
||||||
|
|
||||||
|
-- Use texture of one of the parents
|
||||||
|
local p = math.random(1, 2)
|
||||||
|
if p == 1 then
|
||||||
|
ent_c.base_texture = parent1.base_texture
|
||||||
|
else
|
||||||
|
ent_c.base_texture = parent2.base_texture
|
||||||
|
end
|
||||||
|
child:set_properties({
|
||||||
|
textures = ent_c.base_texture
|
||||||
|
})
|
||||||
|
|
||||||
|
-- tamed and owned by parents' owner
|
||||||
|
ent_c.tamed = true
|
||||||
|
ent_c.owner = parent1.owner
|
||||||
|
end, self, ent, pos)
|
||||||
|
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:toggle_sit(clicker,p)
|
||||||
|
if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local particle
|
||||||
|
if not self.order or self.order == "" or self.order == "sit" then
|
||||||
|
particle = "mobs_mc_wolf_icon_roam.png"
|
||||||
|
self.order = "roam"
|
||||||
|
self.state = "stand"
|
||||||
|
self.walk_chance = default_walk_chance
|
||||||
|
self.jump = true
|
||||||
|
self:set_animation("stand")
|
||||||
|
-- TODO: Add sitting model
|
||||||
|
else
|
||||||
|
particle = "mobs_mc_wolf_icon_sit.png"
|
||||||
|
self.order = "sit"
|
||||||
|
self.state = "stand"
|
||||||
|
self.walk_chance = 0
|
||||||
|
self.jump = false
|
||||||
|
if self.animation.sit_start then
|
||||||
|
self:set_animation("sit")
|
||||||
|
else
|
||||||
|
self:set_animation("stand")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local pp = vector.new(0,1.4,0)
|
||||||
|
if p then pp = vector.offset(pp,0,p,0) end
|
||||||
|
-- Display icon to show current order (sit or roam)
|
||||||
|
minetest.add_particle({
|
||||||
|
pos = vector.add(pos, pp),
|
||||||
|
velocity = {x=0,y=0.2,z=0},
|
||||||
|
expirationtime = 1,
|
||||||
|
size = 4,
|
||||||
|
texture = particle,
|
||||||
|
playername = self.owner,
|
||||||
|
glow = minetest.LIGHT_MAX,
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,796 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||||
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
|
|
||||||
|
-- pathfinding settings
|
||||||
|
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||||
|
local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
||||||
|
|
||||||
|
-- check if daytime and also if mob is docile during daylight hours
|
||||||
|
function mob_class:day_docile()
|
||||||
|
if self.docile_by_day == false then
|
||||||
|
return false
|
||||||
|
elseif self.docile_by_day == true
|
||||||
|
and self.time_of_day > 0.2
|
||||||
|
and self.time_of_day < 0.8 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- attack player/mob
|
||||||
|
function mob_class:do_attack(player)
|
||||||
|
|
||||||
|
if self.state == "attack" or self.state == "die" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.attack = player
|
||||||
|
self.state = "attack"
|
||||||
|
|
||||||
|
-- TODO: Implement war_cry sound without being annoying
|
||||||
|
--if random(0, 100) < 90 then
|
||||||
|
--self:mob_sound("war_cry", true)
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- blast damage to entities nearby
|
||||||
|
local function entity_physics(pos,radius)
|
||||||
|
|
||||||
|
radius = radius * 2
|
||||||
|
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||||
|
local obj_pos, dist
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
obj_pos = objs[n]:get_pos()
|
||||||
|
|
||||||
|
dist = vector.distance(pos, obj_pos)
|
||||||
|
if dist < 1 then dist = 1 end
|
||||||
|
|
||||||
|
local damage = math.floor((4 / dist) * radius)
|
||||||
|
local ent = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
-- punches work on entities AND players
|
||||||
|
objs[n]:punch(objs[n], 1.0, {
|
||||||
|
full_punch_interval = 1.0,
|
||||||
|
damage_groups = {fleshy = damage},
|
||||||
|
}, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:entity_physics(pos,radius) return entity_physics(pos,radius) end
|
||||||
|
|
||||||
|
local los_switcher = false
|
||||||
|
local height_switcher = false
|
||||||
|
|
||||||
|
-- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3
|
||||||
|
function mob_class:smart_mobs(s, p, dist, dtime)
|
||||||
|
|
||||||
|
local s1 = self.path.lastpos
|
||||||
|
|
||||||
|
local target_pos = self.attack:get_pos()
|
||||||
|
|
||||||
|
-- is it becoming stuck?
|
||||||
|
if math.abs(s1.x - s.x) + math.abs(s1.z - s.z) < .5 then
|
||||||
|
self.path.stuck_timer = self.path.stuck_timer + dtime
|
||||||
|
else
|
||||||
|
self.path.stuck_timer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
||||||
|
|
||||||
|
local use_pathfind = false
|
||||||
|
local has_lineofsight = minetest.line_of_sight(
|
||||||
|
{x = s.x, y = (s.y) + .5, z = s.z},
|
||||||
|
{x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2)
|
||||||
|
|
||||||
|
-- im stuck, search for path
|
||||||
|
if not has_lineofsight then
|
||||||
|
|
||||||
|
if los_switcher == true then
|
||||||
|
use_pathfind = true
|
||||||
|
los_switcher = false
|
||||||
|
end -- cannot see target!
|
||||||
|
else
|
||||||
|
if los_switcher == false then
|
||||||
|
|
||||||
|
los_switcher = true
|
||||||
|
use_pathfind = false
|
||||||
|
|
||||||
|
minetest.after(1, function(self)
|
||||||
|
if not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if has_lineofsight then self.path.following = false end
|
||||||
|
end, self)
|
||||||
|
end -- can see target!
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
||||||
|
|
||||||
|
use_pathfind = true
|
||||||
|
self.path.stuck_timer = 0
|
||||||
|
|
||||||
|
minetest.after(1, function(self)
|
||||||
|
if not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if has_lineofsight then self.path.following = false end
|
||||||
|
end, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
||||||
|
|
||||||
|
use_pathfind = true
|
||||||
|
self.path.stuck_timer = 0
|
||||||
|
|
||||||
|
minetest.after(1, function(self)
|
||||||
|
if not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if has_lineofsight then self.path.following = false end
|
||||||
|
end, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then
|
||||||
|
|
||||||
|
if height_switcher then
|
||||||
|
use_pathfind = true
|
||||||
|
height_switcher = false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not height_switcher then
|
||||||
|
use_pathfind = false
|
||||||
|
height_switcher = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if use_pathfind then
|
||||||
|
-- lets try find a path, first take care of positions
|
||||||
|
-- since pathfinder is very sensitive
|
||||||
|
local sheight = self.collisionbox[5] - self.collisionbox[2]
|
||||||
|
|
||||||
|
-- round position to center of node to avoid stuck in walls
|
||||||
|
-- also adjust height for player models!
|
||||||
|
s.x = math.floor(s.x + 0.5)
|
||||||
|
s.z = math.floor(s.z + 0.5)
|
||||||
|
|
||||||
|
local ssight, sground = minetest.line_of_sight(s, {
|
||||||
|
x = s.x, y = s.y - 4, z = s.z}, 1)
|
||||||
|
|
||||||
|
-- determine node above ground
|
||||||
|
if not ssight then
|
||||||
|
s.y = sground.y + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local p1 = self.attack:get_pos()
|
||||||
|
|
||||||
|
p1.x = math.floor(p1.x + 0.5)
|
||||||
|
p1.y = math.floor(p1.y + 0.5)
|
||||||
|
p1.z = math.floor(p1.z + 0.5)
|
||||||
|
|
||||||
|
local dropheight = 12
|
||||||
|
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
||||||
|
local jumpheight = 0
|
||||||
|
if self.jump and self.jump_height >= 4 then
|
||||||
|
jumpheight = math.min(math.ceil(self.jump_height / 4), 4)
|
||||||
|
elseif self.stepheight > 0.5 then
|
||||||
|
jumpheight = 1
|
||||||
|
end
|
||||||
|
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
||||||
|
|
||||||
|
self.state = ""
|
||||||
|
self:do_attack(self.attack)
|
||||||
|
|
||||||
|
-- no path found, try something else
|
||||||
|
if not self.path.way then
|
||||||
|
|
||||||
|
self.path.following = false
|
||||||
|
|
||||||
|
-- lets make way by digging/building if not accessible
|
||||||
|
if self.pathfinding == 2 and mobs_griefing then
|
||||||
|
|
||||||
|
-- is player higher than mob?
|
||||||
|
if s.y < p1.y then
|
||||||
|
|
||||||
|
-- build upwards
|
||||||
|
if not minetest.is_protected(s, "") then
|
||||||
|
|
||||||
|
local ndef1 = minetest.registered_nodes[self.standing_in]
|
||||||
|
|
||||||
|
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
|
||||||
|
|
||||||
|
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local sheight = math.ceil(self.collisionbox[5]) + 1
|
||||||
|
|
||||||
|
-- assume mob is 2 blocks high so it digs above its head
|
||||||
|
s.y = s.y + sheight
|
||||||
|
|
||||||
|
-- remove one block above to make room to jump
|
||||||
|
if not minetest.is_protected(s, "") then
|
||||||
|
|
||||||
|
local node1 = node_ok(s, "air").name
|
||||||
|
local ndef1 = minetest.registered_nodes[node1]
|
||||||
|
|
||||||
|
if node1 ~= "air"
|
||||||
|
and node1 ~= "ignore"
|
||||||
|
and ndef1
|
||||||
|
and not ndef1.groups.level
|
||||||
|
and not ndef1.groups.unbreakable
|
||||||
|
and not ndef1.groups.liquid then
|
||||||
|
|
||||||
|
minetest.set_node(s, {name = "air"})
|
||||||
|
minetest.add_item(s, ItemStack(node1))
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s.y = s.y - sheight
|
||||||
|
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z})
|
||||||
|
|
||||||
|
else -- dig 2 blocks to make door toward player direction
|
||||||
|
|
||||||
|
local yaw1 = self.object:get_yaw() + math.pi / 2
|
||||||
|
local p1 = {
|
||||||
|
x = s.x + math.cos(yaw1),
|
||||||
|
y = s.y,
|
||||||
|
z = s.z + math.sin(yaw1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if not minetest.is_protected(p1, "") then
|
||||||
|
|
||||||
|
local node1 = node_ok(p1, "air").name
|
||||||
|
local ndef1 = minetest.registered_nodes[node1]
|
||||||
|
|
||||||
|
if node1 ~= "air"
|
||||||
|
and node1 ~= "ignore"
|
||||||
|
and ndef1
|
||||||
|
and not ndef1.groups.level
|
||||||
|
and not ndef1.groups.unbreakable
|
||||||
|
and not ndef1.groups.liquid then
|
||||||
|
|
||||||
|
minetest.add_item(p1, ItemStack(node1))
|
||||||
|
minetest.set_node(p1, {name = "air"})
|
||||||
|
end
|
||||||
|
|
||||||
|
p1.y = p1.y + 1
|
||||||
|
node1 = node_ok(p1, "air").name
|
||||||
|
ndef1 = minetest.registered_nodes[node1]
|
||||||
|
|
||||||
|
if node1 ~= "air"
|
||||||
|
and node1 ~= "ignore"
|
||||||
|
and ndef1
|
||||||
|
and not ndef1.groups.level
|
||||||
|
and not ndef1.groups.unbreakable
|
||||||
|
and not ndef1.groups.liquid then
|
||||||
|
|
||||||
|
minetest.add_item(p1, ItemStack(node1))
|
||||||
|
minetest.set_node(p1, {name = "air"})
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- will try again in 2 seconds
|
||||||
|
self.path.stuck_timer = stuck_timeout - 2
|
||||||
|
elseif s.y < p1.y and (not self.fly) then
|
||||||
|
self:do_jump() --add jump to pathfinding
|
||||||
|
self.path.following = true
|
||||||
|
-- Yay, I found path!
|
||||||
|
-- TODO: Implement war_cry sound without being annoying
|
||||||
|
--self:mob_sound("war_cry", true)
|
||||||
|
else
|
||||||
|
self:set_velocity(self.walk_velocity)
|
||||||
|
|
||||||
|
-- follow path now that it has it
|
||||||
|
self.path.following = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- specific attacks
|
||||||
|
local specific_attack = function(list, what)
|
||||||
|
|
||||||
|
-- no list so attack default (player, animals etc.)
|
||||||
|
if list == nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- found entity on list to attack?
|
||||||
|
for no = 1, #list do
|
||||||
|
|
||||||
|
if list[no] == what then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find someone to attack
|
||||||
|
function mob_class:monster_attack()
|
||||||
|
if not damage_enabled
|
||||||
|
or self.passive ~= false
|
||||||
|
or self.state == "attack"
|
||||||
|
or self:day_docile() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
local p, sp, dist
|
||||||
|
local player, obj, min_player
|
||||||
|
local type, name = "", ""
|
||||||
|
local min_dist = self.view_range + 1
|
||||||
|
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
||||||
|
local blacklist_attack = {}
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
if not objs[n]:is_player() then
|
||||||
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj then
|
||||||
|
player = obj.object
|
||||||
|
name = obj.name or ""
|
||||||
|
end
|
||||||
|
if obj and obj.type == self.type and obj.passive == false and obj.state == "attack" and obj.attack then
|
||||||
|
table.insert(blacklist_attack, obj.attack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
|
||||||
|
if objs[n]:is_player() then
|
||||||
|
if mcl_mobs.invis[ objs[n]:get_player_name() ] or (not self:object_in_range(objs[n])) then
|
||||||
|
type = ""
|
||||||
|
elseif (self.type == "monster" or self._aggro) then
|
||||||
|
player = objs[n]
|
||||||
|
type = "player"
|
||||||
|
name = "player"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj then
|
||||||
|
player = obj.object
|
||||||
|
type = obj.type
|
||||||
|
name = obj.name or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find specific mob to attack, failing that attack player/npc/animal
|
||||||
|
if specific_attack(self.specific_attack, name)
|
||||||
|
and (type == "player" or ( type == "npc" and self.attack_npcs )
|
||||||
|
or (type == "animal" and self.attack_animals == true)) then
|
||||||
|
|
||||||
|
p = player:get_pos()
|
||||||
|
sp = s
|
||||||
|
|
||||||
|
dist = vector.distance(p, s)
|
||||||
|
|
||||||
|
-- aim higher to make looking up hills more realistic
|
||||||
|
p.y = p.y + 1
|
||||||
|
sp.y = sp.y + 1
|
||||||
|
|
||||||
|
local attacked_p = false
|
||||||
|
for c=1, #blacklist_attack do
|
||||||
|
if blacklist_attack[c] == player then
|
||||||
|
attacked_p = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- choose closest player to attack
|
||||||
|
if dist < min_dist
|
||||||
|
and not attacked_p
|
||||||
|
and self:line_of_sight( sp, p, 2) == true then
|
||||||
|
min_dist = dist
|
||||||
|
min_player = player
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not min_player and #blacklist_attack > 0 then
|
||||||
|
min_player=blacklist_attack[math.random(#blacklist_attack)]
|
||||||
|
end
|
||||||
|
-- attack player
|
||||||
|
if min_player then
|
||||||
|
self:do_attack(min_player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- npc, find closest monster to attack
|
||||||
|
function mob_class:npc_attack()
|
||||||
|
|
||||||
|
if self.type ~= "npc"
|
||||||
|
or not self.attacks_monsters
|
||||||
|
or self.state == "attack" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local p, sp, obj, min_player
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
local min_dist = self.view_range + 1
|
||||||
|
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj and obj.type == "monster" then
|
||||||
|
|
||||||
|
p = obj.object:get_pos()
|
||||||
|
sp = s
|
||||||
|
|
||||||
|
local dist = vector.distance(p, s)
|
||||||
|
|
||||||
|
-- aim higher to make looking up hills more realistic
|
||||||
|
p.y = p.y + 1
|
||||||
|
sp.y = sp.y + 1
|
||||||
|
|
||||||
|
if dist < min_dist
|
||||||
|
and self:line_of_sight( sp, p, 2) == true then
|
||||||
|
min_dist = dist
|
||||||
|
min_player = obj.object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if min_player then
|
||||||
|
self:do_attack(min_player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- dogshoot attack switch and counter function
|
||||||
|
function mob_class:dogswitch(dtime)
|
||||||
|
|
||||||
|
-- switch mode not activated
|
||||||
|
if not self.dogshoot_switch
|
||||||
|
or not dtime then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self.dogshoot_count = self.dogshoot_count + dtime
|
||||||
|
|
||||||
|
if (self.dogshoot_switch == 1
|
||||||
|
and self.dogshoot_count > self.dogshoot_count_max)
|
||||||
|
or (self.dogshoot_switch == 2
|
||||||
|
and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||||
|
|
||||||
|
self.dogshoot_count = 0
|
||||||
|
|
||||||
|
if self.dogshoot_switch == 1 then
|
||||||
|
self.dogshoot_switch = 2
|
||||||
|
else
|
||||||
|
self.dogshoot_switch = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.dogshoot_switch
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no damage to nodes explosion
|
||||||
|
function mob_class:safe_boom(pos, strength)
|
||||||
|
minetest.sound_play(self.sounds and self.sounds.explode or "tnt_explode", {
|
||||||
|
pos = pos,
|
||||||
|
gain = 1.0,
|
||||||
|
max_hear_distance = self.sounds and self.sounds.distance or 32
|
||||||
|
}, true)
|
||||||
|
local radius = strength
|
||||||
|
entity_physics(pos, radius)
|
||||||
|
mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", radius * 3, radius * 5, radius, 1, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- make explosion with protection and tnt mod check
|
||||||
|
function mob_class:boom(pos, strength, fire)
|
||||||
|
if mobs_griefing and not minetest.is_protected(pos, "") then
|
||||||
|
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
|
||||||
|
else
|
||||||
|
mcl_mobs.mob_class.safe_boom(self, pos, strength) --need to call it this way bc self is the "arrow" object here
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delete the object after it punched the player to avoid nil entities in e.g. mcl_shields!!
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- deal damage and effects when mob punched
|
||||||
|
function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
||||||
|
|
||||||
|
-- custom punch function
|
||||||
|
if self.do_punch then
|
||||||
|
|
||||||
|
-- when false skip going any further
|
||||||
|
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- error checking when mod profiling is enabled
|
||||||
|
if not tool_capabilities then
|
||||||
|
minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_player = hitter:is_player()
|
||||||
|
|
||||||
|
if is_player then
|
||||||
|
-- is mob protected?
|
||||||
|
if self.protected and minetest.is_protected(self.object:get_pos(), hitter:get_player_name()) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.is_creative_enabled(hitter:get_player_name()) then
|
||||||
|
self.health = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set/update 'drop xp' timestamp if hitted by player
|
||||||
|
self.xp_timestamp = minetest.get_us_time()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- punch interval
|
||||||
|
local weapon = hitter:get_wielded_item()
|
||||||
|
local punch_interval = 1.4
|
||||||
|
|
||||||
|
-- exhaust attacker
|
||||||
|
if is_player then
|
||||||
|
mcl_hunger.exhaust(hitter:get_player_name(), mcl_hunger.EXHAUST_ATTACK)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- calculate mob damage
|
||||||
|
local damage = 0
|
||||||
|
local armor = self.object:get_armor_groups() or {}
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
-- quick error check incase it ends up 0 (serialize.h check test)
|
||||||
|
if tflp == 0 then
|
||||||
|
tflp = 0.2
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
||||||
|
|
||||||
|
tmp = tflp / (tool_capabilities.full_punch_interval or 1.4)
|
||||||
|
|
||||||
|
if tmp < 0 then
|
||||||
|
tmp = 0.0
|
||||||
|
elseif tmp > 1 then
|
||||||
|
tmp = 1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
damage = damage + (tool_capabilities.damage_groups[group] or 0)
|
||||||
|
* tmp * ((armor[group] or 0) / 100.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if weapon then
|
||||||
|
local fire_aspect_level = mcl_enchanting.get_enchantment(weapon, "fire_aspect")
|
||||||
|
if fire_aspect_level > 0 then
|
||||||
|
mcl_burning.set_on_fire(self.object, fire_aspect_level * 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for tool immunity or special damage
|
||||||
|
for n = 1, #self.immune_to do
|
||||||
|
|
||||||
|
if self.immune_to[n][1] == weapon:get_name() then
|
||||||
|
|
||||||
|
damage = self.immune_to[n][2] or 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- healing
|
||||||
|
if damage <= -1 then
|
||||||
|
self.health = self.health - math.floor(damage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if tool_capabilities then
|
||||||
|
punch_interval = tool_capabilities.full_punch_interval or 1.4
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add weapon wear manually
|
||||||
|
-- Required because we have custom health handling ("health" property)
|
||||||
|
if minetest.is_creative_enabled("") ~= true
|
||||||
|
and tool_capabilities then
|
||||||
|
if tool_capabilities.punch_attack_uses then
|
||||||
|
-- Without this delay, the wear does not work. Quite hacky ...
|
||||||
|
minetest.after(0, function(name)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
if not player then return end
|
||||||
|
local weapon = hitter:get_wielded_item(player)
|
||||||
|
local def = weapon:get_definition()
|
||||||
|
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
||||||
|
local wear = math.floor(65535/tool_capabilities.punch_attack_uses)
|
||||||
|
weapon:add_wear(wear)
|
||||||
|
hitter:set_wielded_item(weapon)
|
||||||
|
end
|
||||||
|
end, hitter:get_player_name())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local die = false
|
||||||
|
|
||||||
|
|
||||||
|
if damage >= 0 then
|
||||||
|
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
||||||
|
if damage >= 0.1 then
|
||||||
|
-- weapon sounds
|
||||||
|
if weapon:get_definition().sounds ~= nil then
|
||||||
|
|
||||||
|
local s = math.random(0, #weapon:get_definition().sounds)
|
||||||
|
|
||||||
|
minetest.sound_play(weapon:get_definition().sounds[s], {
|
||||||
|
object = self.object, --hitter,
|
||||||
|
max_hear_distance = 8
|
||||||
|
}, true)
|
||||||
|
else
|
||||||
|
minetest.sound_play("default_punch", {
|
||||||
|
object = self.object,
|
||||||
|
max_hear_distance = 5
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:damage_effect(damage)
|
||||||
|
|
||||||
|
-- do damage
|
||||||
|
self.health = self.health - damage
|
||||||
|
|
||||||
|
-- skip future functions if dead, except alerting others
|
||||||
|
if self:check_for_death( "hit", {type = "punch", puncher = hitter}) then
|
||||||
|
die = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- knock back effect (only on full punch)
|
||||||
|
if self.knock_back
|
||||||
|
and tflp >= punch_interval then
|
||||||
|
-- direction error check
|
||||||
|
dir = dir or {x = 0, y = 0, z = 0}
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
if not v then return end
|
||||||
|
local r = 1.4 - math.min(punch_interval, 1.4)
|
||||||
|
local kb = r * (math.abs(v.x)+math.abs(v.z))
|
||||||
|
local up = 2
|
||||||
|
|
||||||
|
if die==true then
|
||||||
|
kb=kb*2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if already in air then dont go up anymore when hit
|
||||||
|
if math.abs(v.y) > 0.1
|
||||||
|
or self.fly then
|
||||||
|
up = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- check if tool already has specific knockback value
|
||||||
|
if tool_capabilities.damage_groups["knockback"] then
|
||||||
|
kb = tool_capabilities.damage_groups["knockback"]
|
||||||
|
else
|
||||||
|
kb = kb * 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local luaentity
|
||||||
|
if hitter then
|
||||||
|
luaentity = hitter:get_luaentity()
|
||||||
|
end
|
||||||
|
if hitter and is_player then
|
||||||
|
local wielditem = hitter:get_wielded_item()
|
||||||
|
kb = kb + 3 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
||||||
|
elseif luaentity and luaentity._knockback then
|
||||||
|
kb = kb + luaentity._knockback
|
||||||
|
end
|
||||||
|
self._kb_turn = true
|
||||||
|
self._turn_to=self.object:get_yaw()-1.57
|
||||||
|
self.frame_speed_multiplier=2.3
|
||||||
|
if self.animation.run_end then
|
||||||
|
self:set_animation( "run")
|
||||||
|
elseif self.animation.walk_end then
|
||||||
|
self:set_animation( "walk")
|
||||||
|
end
|
||||||
|
minetest.after(0.2, function()
|
||||||
|
if self and self.object then
|
||||||
|
self.frame_speed_multiplier=1
|
||||||
|
self._kb_turn = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
self.object:add_velocity({
|
||||||
|
x = dir.x * kb,
|
||||||
|
y = up*2,
|
||||||
|
z = dir.z * kb
|
||||||
|
})
|
||||||
|
|
||||||
|
self.pause_timer = 0.25
|
||||||
|
end
|
||||||
|
end -- END if damage
|
||||||
|
|
||||||
|
-- if skittish then run away
|
||||||
|
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
||||||
|
|
||||||
|
local yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||||
|
minetest.after(0.2,function()
|
||||||
|
if self and self.object and self.object:get_pos() and hitter and is_player and hitter:get_pos() then
|
||||||
|
yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||||
|
self:set_velocity( self.run_velocity)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
self.state = "runaway"
|
||||||
|
self.runaway_timer = 0
|
||||||
|
self.following = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = hitter:get_player_name() or ""
|
||||||
|
|
||||||
|
-- attack puncher and call other mobs for help
|
||||||
|
if self.passive == false
|
||||||
|
and self.state ~= "flop"
|
||||||
|
and (self.child == false or self.type == "monster")
|
||||||
|
and hitter:get_player_name() ~= self.owner
|
||||||
|
and not mcl_mobs.invis[ name ] then
|
||||||
|
if not die then
|
||||||
|
-- attack whoever punched mob
|
||||||
|
self.state = ""
|
||||||
|
self:do_attack(hitter)
|
||||||
|
self._aggro= true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- alert others to the attack
|
||||||
|
local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range)
|
||||||
|
local obj = nil
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj then
|
||||||
|
-- only alert members of same mob or friends
|
||||||
|
if obj.group_attack
|
||||||
|
and obj.state ~= "attack"
|
||||||
|
and obj.owner ~= name then
|
||||||
|
if obj.name == self.name then
|
||||||
|
obj:do_attack(hitter)
|
||||||
|
elseif type(obj.group_attack) == "table" then
|
||||||
|
for i=1, #obj.group_attack do
|
||||||
|
if obj.name == obj.group_attack[i] then
|
||||||
|
obj._aggro = true
|
||||||
|
obj:do_attack(hitter)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- have owned mobs attack player threat
|
||||||
|
if obj.owner == name and obj.owner_loyal then
|
||||||
|
obj:do_attack(self.object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_aggro(dtime)
|
||||||
|
if not self._aggro or not self.attack then return end
|
||||||
|
if not self._check_aggro_timer or self._check_aggro_timer > 5 then
|
||||||
|
self._check_aggro_timer = 0
|
||||||
|
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||||
|
self._aggro = nil
|
||||||
|
self.attack = nil
|
||||||
|
self.state = "stand"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self._check_aggro_timer = self._check_aggro_timer + dtime
|
||||||
|
end
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
-- this is to make the register_mob and register egg functions commonly used by mods not break
|
||||||
|
-- when they use the weird old : notation AND self as first argument
|
||||||
|
local oldregmob = mcl_mobs.register_mob
|
||||||
|
function mcl_mobs.register_mob(self,name,def)
|
||||||
|
if type(self) == "string" then
|
||||||
|
def = name
|
||||||
|
name = self
|
||||||
|
end
|
||||||
|
return oldregmob(name,def)
|
||||||
|
end
|
||||||
|
local oldregegg = mcl_mobs.register_egg
|
||||||
|
function mcl_mobs.register_egg(self, mob, desc, background_color, overlay_color, addegg, no_creative)
|
||||||
|
if type(self) == "string" then
|
||||||
|
no_creative = addegg
|
||||||
|
addegg = overlay_color
|
||||||
|
overlay_color = background_color
|
||||||
|
background_color = desc
|
||||||
|
desc = mob
|
||||||
|
mob = self
|
||||||
|
end
|
||||||
|
return oldregegg(mob, desc, background_color, overlay_color, addegg, no_creative)
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldregarrow = mcl_mobs.register_mob
|
||||||
|
function mcl_mobs.register_mob(self,name,def)
|
||||||
|
if type(self) == "string" then
|
||||||
|
def = name
|
||||||
|
name = self
|
||||||
|
end
|
||||||
|
return oldregarrow(name,def)
|
||||||
|
end
|
|
@ -0,0 +1,384 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
local active_particlespawners = {}
|
||||||
|
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||||
|
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
|
|
||||||
|
local player_transfer_distance = tonumber(minetest.settings:get("player_transfer_distance")) or 128
|
||||||
|
if player_transfer_distance == 0 then player_transfer_distance = math.huge end
|
||||||
|
|
||||||
|
|
||||||
|
-- custom particle effects
|
||||||
|
function mcl_mobs.effect(pos, amount, texture, min_size, max_size, radius, gravity, glow, go_down)
|
||||||
|
|
||||||
|
radius = radius or 2
|
||||||
|
min_size = min_size or 0.5
|
||||||
|
max_size = max_size or 1
|
||||||
|
gravity = gravity or DEFAULT_FALL_SPEED
|
||||||
|
glow = glow or 0
|
||||||
|
go_down = go_down or false
|
||||||
|
|
||||||
|
local ym
|
||||||
|
if go_down then
|
||||||
|
ym = 0
|
||||||
|
else
|
||||||
|
ym = -radius
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = amount,
|
||||||
|
time = 0.25,
|
||||||
|
minpos = pos,
|
||||||
|
maxpos = pos,
|
||||||
|
minvel = {x = -radius, y = ym, z = -radius},
|
||||||
|
maxvel = {x = radius, y = radius, z = radius},
|
||||||
|
minacc = {x = 0, y = gravity, z = 0},
|
||||||
|
maxacc = {x = 0, y = gravity, z = 0},
|
||||||
|
minexptime = 0.1,
|
||||||
|
maxexptime = 1,
|
||||||
|
minsize = min_size,
|
||||||
|
maxsize = max_size,
|
||||||
|
texture = texture,
|
||||||
|
glow = glow,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_mobs.death_effect(pos, yaw, collisionbox, rotate)
|
||||||
|
local min, max
|
||||||
|
if collisionbox then
|
||||||
|
min = {x=collisionbox[1], y=collisionbox[2], z=collisionbox[3]}
|
||||||
|
max = {x=collisionbox[4], y=collisionbox[5], z=collisionbox[6]}
|
||||||
|
else
|
||||||
|
min = { x = -0.5, y = 0, z = -0.5 }
|
||||||
|
max = { x = 0.5, y = 0.5, z = 0.5 }
|
||||||
|
end
|
||||||
|
if rotate then
|
||||||
|
min = vector.rotate(min, {x=0, y=yaw, z=math.pi/2})
|
||||||
|
max = vector.rotate(max, {x=0, y=yaw, z=math.pi/2})
|
||||||
|
min, max = vector.sort(min, max)
|
||||||
|
min = vector.multiply(min, 0.5)
|
||||||
|
max = vector.multiply(max, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 50,
|
||||||
|
time = 0.001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-5,-5,-5),
|
||||||
|
maxvel = vector.new(5,5,5),
|
||||||
|
minexptime = 1.1,
|
||||||
|
maxexptime = 1.5,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_mob_death.png^[colorize:#000000:255",
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.sound_play("mcl_mobs_mob_poof", {
|
||||||
|
pos = pos,
|
||||||
|
gain = 1.0,
|
||||||
|
max_hear_distance = 8,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- play sound
|
||||||
|
function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
|
||||||
|
|
||||||
|
local soundinfo
|
||||||
|
if self.sounds_child and self.child then
|
||||||
|
soundinfo = self.sounds_child
|
||||||
|
elseif self.sounds then
|
||||||
|
soundinfo = self.sounds
|
||||||
|
end
|
||||||
|
if not soundinfo then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local sound = soundinfo[soundname]
|
||||||
|
if sound then
|
||||||
|
if is_opinion and self.opinion_sound_cooloff > 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pitch
|
||||||
|
if not fixed_pitch then
|
||||||
|
local base_pitch = soundinfo.base_pitch
|
||||||
|
if not base_pitch then
|
||||||
|
base_pitch = 1
|
||||||
|
end
|
||||||
|
if self.child and (not self.sounds_child) then
|
||||||
|
-- Children have higher pitch
|
||||||
|
pitch = base_pitch * 1.5
|
||||||
|
else
|
||||||
|
pitch = base_pitch
|
||||||
|
end
|
||||||
|
-- randomize the pitch a bit
|
||||||
|
pitch = pitch + math.random(-10, 10) * 0.005
|
||||||
|
end
|
||||||
|
minetest.sound_play(sound, {
|
||||||
|
object = self.object,
|
||||||
|
gain = 1.0,
|
||||||
|
max_hear_distance = self.sounds.distance,
|
||||||
|
pitch = pitch,
|
||||||
|
}, true)
|
||||||
|
self.opinion_sound_cooloff = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:add_texture_mod(mod)
|
||||||
|
local full_mod = ""
|
||||||
|
local already_added = false
|
||||||
|
for i=1, #self.texture_mods do
|
||||||
|
if mod == self.texture_mods[i] then
|
||||||
|
already_added = true
|
||||||
|
end
|
||||||
|
full_mod = full_mod .. self.texture_mods[i]
|
||||||
|
end
|
||||||
|
if not already_added then
|
||||||
|
full_mod = full_mod .. mod
|
||||||
|
table.insert(self.texture_mods, mod)
|
||||||
|
end
|
||||||
|
self.object:set_texture_mod(full_mod)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:remove_texture_mod(mod)
|
||||||
|
local full_mod = ""
|
||||||
|
local remove = {}
|
||||||
|
for i=1, #self.texture_mods do
|
||||||
|
if self.texture_mods[i] ~= mod then
|
||||||
|
full_mod = full_mod .. self.texture_mods[i]
|
||||||
|
else
|
||||||
|
table.insert(remove, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i=#remove, 1 do
|
||||||
|
table.remove(self.texture_mods, remove[i])
|
||||||
|
end
|
||||||
|
self.object:set_texture_mod(full_mod)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:damage_effect(damage)
|
||||||
|
-- damage particles
|
||||||
|
if (not disable_blood) and damage > 0 then
|
||||||
|
|
||||||
|
local amount_large = math.floor(damage / 2)
|
||||||
|
local amount_small = damage % 2
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
pos.y = pos.y + (self.collisionbox[5] - self.collisionbox[2]) * .5
|
||||||
|
|
||||||
|
local texture = "mobs_blood.png"
|
||||||
|
-- full heart damage (one particle for each 2 HP damage)
|
||||||
|
if amount_large > 0 then
|
||||||
|
mcl_mobs.effect(pos, amount_large, texture, 2, 2, 1.75, 0, nil, true)
|
||||||
|
end
|
||||||
|
-- half heart damage (one additional particle if damage is an odd number)
|
||||||
|
if amount_small > 0 then
|
||||||
|
-- TODO: Use "half heart"
|
||||||
|
mcl_mobs.effect(pos, amount_small, texture, 1, 1, 1.75, 0, nil, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:remove_particlespawners(pn)
|
||||||
|
if not active_particlespawners[pn] then return end
|
||||||
|
if not active_particlespawners[pn][self.object] then return end
|
||||||
|
for k,v in pairs(active_particlespawners[pn][self.object]) do
|
||||||
|
minetest.delete_particlespawner(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:add_particlespawners(pn)
|
||||||
|
if not active_particlespawners[pn] then active_particlespawners[pn] = {} end
|
||||||
|
if not active_particlespawners[pn][self.object] then active_particlespawners[pn][self.object] = {} end
|
||||||
|
for _,ps in pairs(self.particlespawners) do
|
||||||
|
ps.attached = self.object
|
||||||
|
ps.playername = pn
|
||||||
|
table.insert(active_particlespawners[pn][self.object],minetest.add_particlespawner(ps))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_particlespawners(dtime)
|
||||||
|
if not self.particlespawners then return end
|
||||||
|
--minetest.log(dump(active_particlespawners))
|
||||||
|
if self._particle_timer and self._particle_timer >= 1 then
|
||||||
|
self._particle_timer = 0
|
||||||
|
local players = {}
|
||||||
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
|
local pn = player:get_player_name()
|
||||||
|
table.insert(players,pn)
|
||||||
|
if not active_particlespawners[pn] then
|
||||||
|
active_particlespawners[pn] = {} end
|
||||||
|
|
||||||
|
local dst = vector.distance(player:get_pos(),self.object:get_pos())
|
||||||
|
if dst < player_transfer_distance and not active_particlespawners[pn][self.object] then
|
||||||
|
self:add_particlespawners(pn)
|
||||||
|
elseif dst >= player_transfer_distance and active_particlespawners[pn][self.object] then
|
||||||
|
self:remove_particlespawners(pn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif not self._particle_timer then
|
||||||
|
self._particle_timer = 0
|
||||||
|
end
|
||||||
|
self._particle_timer = self._particle_timer + dtime
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- set defined animation
|
||||||
|
function mob_class:set_animation(anim, fixed_frame)
|
||||||
|
if not self.animation or not anim then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.jockey then
|
||||||
|
anim = "jockey"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if self:flight_check() and self.fly and anim == "walk" then anim = "fly" end
|
||||||
|
|
||||||
|
self._current_animation = self._current_animation or ""
|
||||||
|
|
||||||
|
if (anim == self._current_animation
|
||||||
|
or not self.animation[anim .. "_start"]
|
||||||
|
or not self.animation[anim .. "_end"]) and self.state ~= "die" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self._current_animation = anim
|
||||||
|
|
||||||
|
local a_start = self.animation[anim .. "_start"]
|
||||||
|
local a_end
|
||||||
|
if fixed_frame then
|
||||||
|
a_end = a_start
|
||||||
|
else
|
||||||
|
a_end = self.animation[anim .. "_end"]
|
||||||
|
end
|
||||||
|
if a_start and a_end then
|
||||||
|
self.object:set_animation({
|
||||||
|
x = a_start,
|
||||||
|
y = a_end},
|
||||||
|
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
||||||
|
0, self.animation[anim .. "_loop"] ~= false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- above function exported for mount.lua
|
||||||
|
function mcl_mobs:set_animation(self, anim)
|
||||||
|
self:set_animation(anim)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dir_to_pitch(dir)
|
||||||
|
--local dir2 = vector.normalize(dir)
|
||||||
|
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||||
|
return -math.atan2(-dir.y, xz)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_head_swivel(dtime)
|
||||||
|
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
|
||||||
|
local final_rotation = vector.new(0,0,0)
|
||||||
|
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 10)) do
|
||||||
|
if obj:is_player() and not self.attack or obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity() then
|
||||||
|
if not self._locked_object then
|
||||||
|
if math.random(5000/self.curiosity) == 1 or vector.distance(pos,obj:get_pos())<4 and obj:is_player() then
|
||||||
|
self._locked_object = obj
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if math.random(10000/self.curiosity) == 1 then
|
||||||
|
self._locked_object = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.attack or self.following then
|
||||||
|
self._locked_object = self.attack or self.following
|
||||||
|
end
|
||||||
|
|
||||||
|
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
|
||||||
|
local _locked_object_eye_height = 1.5
|
||||||
|
if self._locked_object:get_luaentity() then
|
||||||
|
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height
|
||||||
|
end
|
||||||
|
if self._locked_object:is_player() then
|
||||||
|
_locked_object_eye_height = self._locked_object:get_properties().eye_height
|
||||||
|
end
|
||||||
|
if _locked_object_eye_height then
|
||||||
|
local self_rot = self.object:get_rotation()
|
||||||
|
if self.object:get_attach() then
|
||||||
|
self_rot = self.object:get_attach():get_rotation()
|
||||||
|
end
|
||||||
|
if self.rot then
|
||||||
|
local player_pos = self._locked_object:get_pos()
|
||||||
|
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
|
||||||
|
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
|
||||||
|
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
|
||||||
|
|
||||||
|
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
|
||||||
|
final_rotation = vector.multiply(oldr, 0.9)
|
||||||
|
elseif self.attack and self.state == "attack" and not self.runaway then
|
||||||
|
if self.head_yaw == "y" then
|
||||||
|
final_rotation = vector.new(mob_pitch, mob_yaw, 0)
|
||||||
|
elseif self.head_yaw == "z" then
|
||||||
|
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if self.head_yaw == "y" then
|
||||||
|
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0)
|
||||||
|
elseif self.head_yaw == "z" then
|
||||||
|
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
|
||||||
|
final_rotation = vector.multiply(oldr, 0.9)
|
||||||
|
else
|
||||||
|
final_rotation = vector.new(0,0,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), final_rotation)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:set_animation_speed()
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
if v then
|
||||||
|
if self.frame_speed_multiplier then
|
||||||
|
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
||||||
|
if not self.animation.walk_speed then
|
||||||
|
self.animation.walk_speed = 25
|
||||||
|
end
|
||||||
|
if math.abs(v.x)+math.abs(v.z) > 0.5 then
|
||||||
|
self.object:set_animation_frame_speed((v2/math.max(1,self.run_velocity))*self.animation.walk_speed*self.frame_speed_multiplier)
|
||||||
|
else
|
||||||
|
self.object:set_animation_frame_speed(25)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--set_speed
|
||||||
|
if self.acc then
|
||||||
|
self.object:add_velocity(self.acc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local pn = player:get_player_name()
|
||||||
|
if not active_particlespawners[pn] then return end
|
||||||
|
for _,m in pairs(active_particlespawners[pn]) do
|
||||||
|
for k,v in pairs(m) do
|
||||||
|
minetest.delete_particlespawner(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
active_particlespawners[pn] = nil
|
||||||
|
end)
|
|
@ -1,14 +1,566 @@
|
||||||
|
mcl_mobs = {}
|
||||||
local path = minetest.get_modpath(minetest.get_current_modname())
|
mcl_mobs.mob_class = {}
|
||||||
|
mcl_mobs.mob_class_meta = {__index = mcl_mobs.mob_class}
|
||||||
-- Mob API
|
mcl_mobs.registered_mobs = {}
|
||||||
|
local modname = minetest.get_current_modname()
|
||||||
|
local path = minetest.get_modpath(modname)
|
||||||
|
local S = minetest.get_translator(modname)
|
||||||
|
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
||||||
|
--api and helpers
|
||||||
|
-- effects: sounds and particles mostly
|
||||||
|
dofile(path .. "/effects.lua")
|
||||||
|
-- physics: involuntary mob movement - particularly falling and death
|
||||||
|
dofile(path .. "/physics.lua")
|
||||||
|
-- movement: general voluntary mob movement, walking avoiding cliffs etc.
|
||||||
|
dofile(path .. "/movement.lua")
|
||||||
|
-- items: item management for mobs
|
||||||
|
dofile(path .. "/items.lua")
|
||||||
|
-- pathfinding: pathfinding to target positions
|
||||||
|
dofile(path .. "/pathfinding.lua")
|
||||||
|
-- combat: attack logic
|
||||||
|
dofile(path .. "/combat.lua")
|
||||||
|
-- the enity functions themselves
|
||||||
dofile(path .. "/api.lua")
|
dofile(path .. "/api.lua")
|
||||||
|
|
||||||
-- Spawning Algorithm
|
|
||||||
|
--utility functions
|
||||||
|
dofile(path .. "/breeding.lua")
|
||||||
dofile(path .. "/spawning.lua")
|
dofile(path .. "/spawning.lua")
|
||||||
|
|
||||||
-- Rideable Mobs
|
|
||||||
dofile(path .. "/mount.lua")
|
dofile(path .. "/mount.lua")
|
||||||
|
|
||||||
-- Mob Items
|
|
||||||
dofile(path .. "/crafts.lua")
|
dofile(path .. "/crafts.lua")
|
||||||
|
dofile(path .. "/compat.lua")
|
||||||
|
|
||||||
|
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
|
local MAX_MOB_NAME_LENGTH = 30
|
||||||
|
|
||||||
|
local old_spawn_icons = minetest.settings:get_bool("mcl_old_spawn_icons",false)
|
||||||
|
local extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true)
|
||||||
|
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
||||||
|
|
||||||
|
-- get node but use fallback for nil or unknown
|
||||||
|
local node_ok = function(pos, fallback)
|
||||||
|
fallback = fallback or mcl_mobs.fallback_node
|
||||||
|
local node = minetest.get_node_or_nil(pos)
|
||||||
|
if node and minetest.registered_nodes[node.name] then
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
return minetest.registered_nodes[fallback]
|
||||||
|
end
|
||||||
|
|
||||||
|
--#### REGISTER FUNCS
|
||||||
|
|
||||||
|
-- Code to execute before custom on_rightclick handling
|
||||||
|
local on_rightclick_prefix = function(self, clicker)
|
||||||
|
if not clicker:is_player() then return end
|
||||||
|
local item = clicker:get_wielded_item()
|
||||||
|
if extended_pet_control and self.tamed and self.owner == clicker:get_player_name() then
|
||||||
|
self:toggle_sit(clicker)
|
||||||
|
end
|
||||||
|
-- Name mob with nametag
|
||||||
|
if not self.ignores_nametag and item:get_name() == "mcl_mobs:nametag" then
|
||||||
|
|
||||||
|
local tag = item:get_meta():get_string("name")
|
||||||
|
if tag ~= "" then
|
||||||
|
if string.len(tag) > MAX_MOB_NAME_LENGTH then
|
||||||
|
tag = string.sub(tag, 1, MAX_MOB_NAME_LENGTH)
|
||||||
|
end
|
||||||
|
self.nametag = tag
|
||||||
|
|
||||||
|
self:update_tag()
|
||||||
|
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
item:take_item()
|
||||||
|
clicker:set_wielded_item(item)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local create_mob_on_rightclick = function(on_rightclick)
|
||||||
|
return function(self, clicker)
|
||||||
|
local stop = on_rightclick_prefix(self, clicker)
|
||||||
|
if (not stop) and (on_rightclick) then
|
||||||
|
on_rightclick(self, clicker)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if within physical map limits (-30911 to 30927)
|
||||||
|
local function within_limits(pos, radius)
|
||||||
|
local wmin, wmax = -30912, 30928
|
||||||
|
if mcl_vars then
|
||||||
|
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
||||||
|
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if radius then
|
||||||
|
wmin = wmin - radius
|
||||||
|
wmax = wmax + radius
|
||||||
|
end
|
||||||
|
for _,v in pairs(pos) do
|
||||||
|
if v < wmin or v > wmax then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_mobs.spawning_mobs = {}
|
||||||
|
-- register mob entity
|
||||||
|
function mcl_mobs.register_mob(name, def)
|
||||||
|
|
||||||
|
mcl_mobs.spawning_mobs[name] = true
|
||||||
|
mcl_mobs.registered_mobs[name] = def
|
||||||
|
|
||||||
|
local can_despawn
|
||||||
|
if def.can_despawn ~= nil then
|
||||||
|
can_despawn = def.can_despawn
|
||||||
|
elseif def.spawn_class == "passive" then
|
||||||
|
can_despawn = false
|
||||||
|
else
|
||||||
|
can_despawn = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scale_difficulty(value, default, min, special)
|
||||||
|
if (not value) or (value == default) or (value == special) then
|
||||||
|
return default
|
||||||
|
else
|
||||||
|
return math.max(min, value * difficulty)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
|
||||||
|
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
||||||
|
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
||||||
|
-- FIXME: Remove workaround if it's no longer needed.
|
||||||
|
if collisionbox[5] < 0.79 then
|
||||||
|
collisionbox[5] = 0.79
|
||||||
|
end
|
||||||
|
local final_def = {
|
||||||
|
use_texture_alpha = def.use_texture_alpha,
|
||||||
|
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
||||||
|
head_yaw_offset = def.head_yaw_offset or 0, -- for wonkey model bones
|
||||||
|
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
||||||
|
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
|
||||||
|
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
|
||||||
|
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
||||||
|
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||||
|
horrizonatal_head_height = def.horrizonatal_head_height or 0,
|
||||||
|
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
||||||
|
stepheight = def.stepheight or 0.6,
|
||||||
|
name = name,
|
||||||
|
description = def.description,
|
||||||
|
type = def.type,
|
||||||
|
attack_type = def.attack_type,
|
||||||
|
fly = def.fly or false,
|
||||||
|
fly_in = def.fly_in or {"air", "__airlike"},
|
||||||
|
owner = def.owner or "",
|
||||||
|
order = def.order or "",
|
||||||
|
on_die = def.on_die,
|
||||||
|
spawn_small_alternative = def.spawn_small_alternative,
|
||||||
|
do_custom = def.do_custom,
|
||||||
|
detach_child = def.detach_child,
|
||||||
|
jump_height = def.jump_height or 4, -- was 6
|
||||||
|
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
||||||
|
lifetimer = def.lifetimer or 57.73,
|
||||||
|
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
||||||
|
hp_max = scale_difficulty(def.hp_max, 10, 1),
|
||||||
|
xp_min = def.xp_min or 0,
|
||||||
|
xp_max = def.xp_max or 0,
|
||||||
|
xp_timestamp = 0,
|
||||||
|
breath_max = def.breath_max or 15,
|
||||||
|
breathes_in_water = def.breathes_in_water or false,
|
||||||
|
physical = true,
|
||||||
|
collisionbox = collisionbox,
|
||||||
|
selectionbox = def.selectionbox or def.collisionbox,
|
||||||
|
visual = def.visual,
|
||||||
|
visual_size = def.visual_size or {x = 1, y = 1},
|
||||||
|
mesh = def.mesh,
|
||||||
|
makes_footstep_sound = def.makes_footstep_sound or false,
|
||||||
|
view_range = def.view_range or 16,
|
||||||
|
walk_velocity = def.walk_velocity or 1,
|
||||||
|
run_velocity = def.run_velocity or 2,
|
||||||
|
damage = scale_difficulty(def.damage, 0, 0),
|
||||||
|
light_damage = def.light_damage or 0,
|
||||||
|
sunlight_damage = def.sunlight_damage or 0,
|
||||||
|
water_damage = def.water_damage or 0,
|
||||||
|
lava_damage = def.lava_damage or 8,
|
||||||
|
fire_damage = def.fire_damage or 1,
|
||||||
|
suffocation = def.suffocation or true,
|
||||||
|
fall_damage = def.fall_damage or 1,
|
||||||
|
fall_speed = def.fall_speed or DEFAULT_FALL_SPEED, -- must be lower than -2
|
||||||
|
drops = def.drops or {},
|
||||||
|
armor = def.armor or 100,
|
||||||
|
on_rightclick = create_mob_on_rightclick(def.on_rightclick),
|
||||||
|
arrow = def.arrow,
|
||||||
|
shoot_interval = def.shoot_interval,
|
||||||
|
sounds = def.sounds or {},
|
||||||
|
animation = def.animation or {},
|
||||||
|
follow = def.follow,
|
||||||
|
nofollow = def.nofollow,
|
||||||
|
can_open_doors = def.can_open_doors,
|
||||||
|
jump = def.jump ~= false,
|
||||||
|
automatic_face_movement_max_rotation_per_sec = 300,
|
||||||
|
walk_chance = def.walk_chance or 50,
|
||||||
|
attacks_monsters = def.attacks_monsters or false,
|
||||||
|
group_attack = def.group_attack or false,
|
||||||
|
passive = def.passive or false,
|
||||||
|
knock_back = def.knock_back ~= false,
|
||||||
|
shoot_offset = def.shoot_offset or 0,
|
||||||
|
floats = def.floats or 1, -- floats in water by default
|
||||||
|
floats_on_lava = def.floats_on_lava or 0,
|
||||||
|
replace_rate = def.replace_rate,
|
||||||
|
replace_what = def.replace_what,
|
||||||
|
replace_with = def.replace_with,
|
||||||
|
replace_offset = def.replace_offset or 0,
|
||||||
|
on_replace = def.on_replace,
|
||||||
|
timer = 0,
|
||||||
|
env_damage_timer = 0,
|
||||||
|
tamed = false,
|
||||||
|
pause_timer = 0,
|
||||||
|
horny = false,
|
||||||
|
hornytimer = 0,
|
||||||
|
gotten = false,
|
||||||
|
health = 0,
|
||||||
|
frame_speed_multiplier = 1,
|
||||||
|
reach = def.reach or 3,
|
||||||
|
htimer = 0,
|
||||||
|
texture_list = def.textures,
|
||||||
|
child_texture = def.child_texture,
|
||||||
|
docile_by_day = def.docile_by_day or false,
|
||||||
|
time_of_day = 0.5,
|
||||||
|
fear_height = def.fear_height or 0,
|
||||||
|
runaway = def.runaway,
|
||||||
|
runaway_timer = 0,
|
||||||
|
pathfinding = def.pathfinding,
|
||||||
|
immune_to = def.immune_to or {},
|
||||||
|
explosion_radius = def.explosion_radius, -- LEGACY
|
||||||
|
explosion_damage_radius = def.explosion_damage_radius, -- LEGACY
|
||||||
|
explosiontimer_reset_radius = def.explosiontimer_reset_radius,
|
||||||
|
explosion_timer = def.explosion_timer or 3,
|
||||||
|
allow_fuse_reset = def.allow_fuse_reset ~= false,
|
||||||
|
stop_to_explode = def.stop_to_explode ~= false,
|
||||||
|
custom_attack = def.custom_attack,
|
||||||
|
double_melee_attack = def.double_melee_attack,
|
||||||
|
dogshoot_switch = def.dogshoot_switch,
|
||||||
|
dogshoot_count = 0,
|
||||||
|
dogshoot_count_max = def.dogshoot_count_max or 5,
|
||||||
|
dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5),
|
||||||
|
attack_animals = def.attack_animals or false,
|
||||||
|
attack_npcs = def.attack_npcs or false,
|
||||||
|
specific_attack = def.specific_attack,
|
||||||
|
runaway_from = def.runaway_from,
|
||||||
|
owner_loyal = def.owner_loyal,
|
||||||
|
facing_fence = false,
|
||||||
|
is_mob = true,
|
||||||
|
pushable = def.pushable or true,
|
||||||
|
|
||||||
|
-- MCL2 extensions
|
||||||
|
shooter_avoid_enemy = def.shooter_avoid_enemy,
|
||||||
|
strafes = def.strafes,
|
||||||
|
avoid_distance = def.avoid_distance or 9,
|
||||||
|
do_teleport = def.do_teleport,
|
||||||
|
spawn_class = def.spawn_class,
|
||||||
|
can_spawn = def.can_spawn,
|
||||||
|
ignores_nametag = def.ignores_nametag or false,
|
||||||
|
rain_damage = def.rain_damage or 0,
|
||||||
|
glow = def.glow,
|
||||||
|
can_despawn = can_despawn,
|
||||||
|
child = def.child or false,
|
||||||
|
texture_mods = {},
|
||||||
|
shoot_arrow = def.shoot_arrow,
|
||||||
|
sounds_child = def.sounds_child,
|
||||||
|
_child_animations = def.child_animations,
|
||||||
|
pick_up = def.pick_up,
|
||||||
|
explosion_strength = def.explosion_strength,
|
||||||
|
suffocation_timer = 0,
|
||||||
|
follow_velocity = def.follow_velocity or 2.4,
|
||||||
|
instant_death = def.instant_death or false,
|
||||||
|
fire_resistant = def.fire_resistant or false,
|
||||||
|
fire_damage_resistant = def.fire_damage_resistant or false,
|
||||||
|
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
||||||
|
spawn_in_group = def.spawn_in_group,
|
||||||
|
spawn_in_group_min = def.spawn_in_group_min,
|
||||||
|
noyaw = def.noyaw or false,
|
||||||
|
particlespawners = def.particlespawners,
|
||||||
|
-- End of MCL2 extensions
|
||||||
|
on_spawn = def.on_spawn,
|
||||||
|
on_blast = def.on_blast or function(self,damage)
|
||||||
|
self.object:punch(self.object, 1.0, {
|
||||||
|
full_punch_interval = 1.0,
|
||||||
|
damage_groups = {fleshy = damage},
|
||||||
|
}, nil)
|
||||||
|
return false, true, {}
|
||||||
|
end,
|
||||||
|
do_punch = def.do_punch,
|
||||||
|
on_breed = def.on_breed,
|
||||||
|
on_grown = def.on_grown,
|
||||||
|
on_pick_up = def.on_pick_up,
|
||||||
|
on_activate = function(self, staticdata, dtime)
|
||||||
|
--this is a temporary hack so mobs stop
|
||||||
|
--glitching and acting really weird with the
|
||||||
|
--default built in engine collision detection
|
||||||
|
self.is_mob = true
|
||||||
|
self.object:set_properties({
|
||||||
|
collide_with_objects = false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return self:mob_activate(staticdata, def, dtime)
|
||||||
|
end,
|
||||||
|
harmed_by_heal = def.harmed_by_heal,
|
||||||
|
on_lightning_strike = def.on_lightning_strike
|
||||||
|
}
|
||||||
|
minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta))
|
||||||
|
|
||||||
|
if minetest.get_modpath("doc_identifier") ~= nil then
|
||||||
|
doc.sub.identifier.register_object(name, "basics", "mobs")
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- END mcl_mobs.register_mob function
|
||||||
|
|
||||||
|
|
||||||
|
-- register arrow for shoot attack
|
||||||
|
function mcl_mobs.register_arrow(name, def)
|
||||||
|
|
||||||
|
if not name or not def then return end -- errorcheck
|
||||||
|
|
||||||
|
minetest.register_entity(name, {
|
||||||
|
|
||||||
|
physical = false,
|
||||||
|
visual = def.visual,
|
||||||
|
visual_size = def.visual_size,
|
||||||
|
textures = def.textures,
|
||||||
|
velocity = def.velocity,
|
||||||
|
hit_player = def.hit_player,
|
||||||
|
hit_node = def.hit_node,
|
||||||
|
hit_mob = def.hit_mob,
|
||||||
|
hit_object = def.hit_object,
|
||||||
|
drop = def.drop or false, -- drops arrow as registered item when true
|
||||||
|
collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows
|
||||||
|
timer = 0,
|
||||||
|
switch = 0,
|
||||||
|
owner_id = def.owner_id,
|
||||||
|
rotate = def.rotate,
|
||||||
|
on_punch = function(self)
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
self.object:set_velocity({x=vel.x * -1, y=vel.y * -1, z=vel.z * -1})
|
||||||
|
end,
|
||||||
|
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
||||||
|
automatic_face_movement_dir = def.rotate
|
||||||
|
and (def.rotate - (math.pi / 180)) or false,
|
||||||
|
|
||||||
|
on_activate = def.on_activate,
|
||||||
|
|
||||||
|
on_step = def.on_step or function(self, dtime)
|
||||||
|
|
||||||
|
self.timer = self.timer + 1
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
if self.switch == 0
|
||||||
|
or self.timer > 150
|
||||||
|
or not within_limits(pos, 0) then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove();
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- does arrow have a tail (fireball)
|
||||||
|
if def.tail
|
||||||
|
and def.tail == 1
|
||||||
|
and def.tail_texture then
|
||||||
|
|
||||||
|
minetest.add_particle({
|
||||||
|
pos = pos,
|
||||||
|
velocity = {x = 0, y = 0, z = 0},
|
||||||
|
acceleration = {x = 0, y = 0, z = 0},
|
||||||
|
expirationtime = def.expire or 0.25,
|
||||||
|
collisiondetection = false,
|
||||||
|
texture = def.tail_texture,
|
||||||
|
size = def.tail_size or 5,
|
||||||
|
glow = def.glow or 0,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.hit_node then
|
||||||
|
|
||||||
|
local node = node_ok(pos).name
|
||||||
|
|
||||||
|
if minetest.registered_nodes[node].walkable then
|
||||||
|
|
||||||
|
self.hit_node(self, pos, node)
|
||||||
|
|
||||||
|
if self.drop == true then
|
||||||
|
|
||||||
|
pos.y = pos.y + 1
|
||||||
|
|
||||||
|
self.lastpos = (self.lastpos or pos)
|
||||||
|
|
||||||
|
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.object:remove();
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.hit_player or self.hit_mob or self.hit_object then
|
||||||
|
|
||||||
|
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
||||||
|
|
||||||
|
if self.hit_player
|
||||||
|
and player:is_player() then
|
||||||
|
|
||||||
|
self.hit_player(self, player)
|
||||||
|
self.object:remove();
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local entity = player:get_luaentity()
|
||||||
|
|
||||||
|
if entity
|
||||||
|
and self.hit_mob
|
||||||
|
and entity.is_mob == true
|
||||||
|
and tostring(player) ~= self.owner_id
|
||||||
|
and entity.name ~= self.object:get_luaentity().name then
|
||||||
|
self.hit_mob(self, player)
|
||||||
|
self.object:remove();
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if entity
|
||||||
|
and self.hit_object
|
||||||
|
and (not entity.is_mob)
|
||||||
|
and tostring(player) ~= self.owner_id
|
||||||
|
and entity.name ~= self.object:get_luaentity().name then
|
||||||
|
self.hit_object(self, player)
|
||||||
|
self.object:remove();
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.lastpos = pos
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Register spawn eggs
|
||||||
|
|
||||||
|
-- Note: This also introduces the “spawn_egg” group:
|
||||||
|
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
||||||
|
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
||||||
|
function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addegg, no_creative)
|
||||||
|
|
||||||
|
local grp = {spawn_egg = 1}
|
||||||
|
|
||||||
|
-- do NOT add this egg to creative inventory (e.g. dungeon master)
|
||||||
|
if no_creative == true then
|
||||||
|
grp.not_in_creative_inventory = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
||||||
|
if old_spawn_icons then
|
||||||
|
local mobname = mob:gsub("mobs_mc:","")
|
||||||
|
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
|
||||||
|
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
||||||
|
invimg = fn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if addegg == 1 then
|
||||||
|
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
||||||
|
"^[mask:mobs_chicken_egg_overlay.png)"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- register old stackable mob egg
|
||||||
|
minetest.register_craftitem(mob, {
|
||||||
|
|
||||||
|
description = desc,
|
||||||
|
inventory_image = invimg,
|
||||||
|
groups = grp,
|
||||||
|
|
||||||
|
_doc_items_longdesc = S("This allows you to place a single mob."),
|
||||||
|
_doc_items_usagehelp = S("Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns."),
|
||||||
|
|
||||||
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
|
||||||
|
local pos = pointed_thing.above
|
||||||
|
|
||||||
|
-- am I clicking on something with existing on_rightclick function?
|
||||||
|
local under = minetest.get_node(pointed_thing.under)
|
||||||
|
local def = minetest.registered_nodes[under.name]
|
||||||
|
if def and def.on_rightclick then
|
||||||
|
return def.on_rightclick(pointed_thing.under, under, placer, itemstack)
|
||||||
|
end
|
||||||
|
|
||||||
|
if pos
|
||||||
|
and within_limits(pos, 0)
|
||||||
|
and not minetest.is_protected(pos, placer:get_player_name()) then
|
||||||
|
|
||||||
|
local name = placer:get_player_name()
|
||||||
|
local privs = minetest.get_player_privs(name)
|
||||||
|
if under.name == "mcl_mobspawners:spawner" then
|
||||||
|
if minetest.is_protected(pointed_thing.under, name) then
|
||||||
|
minetest.record_protection_violation(pointed_thing.under, name)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
if not privs.maphack then
|
||||||
|
minetest.chat_send_player(name, S("You need the “maphack” privilege to change the mob spawner."))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
mcl_mobspawners.setup_spawner(pointed_thing.under, itemstack:get_name())
|
||||||
|
if not minetest.is_creative_enabled(name) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
if not minetest.registered_entities[mob] then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||||
|
and minetest.registered_entities[mob].type == "monster" then
|
||||||
|
minetest.chat_send_player(name, S("Only peaceful mobs allowed!"))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
pos.y = pos.y - 0.5
|
||||||
|
|
||||||
|
local mob = minetest.add_entity(pos, mob)
|
||||||
|
local entityname = itemstack:get_name()
|
||||||
|
minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos))
|
||||||
|
local ent = mob:get_luaentity()
|
||||||
|
|
||||||
|
-- don't set owner if monster or sneak pressed
|
||||||
|
if ent.type ~= "monster"
|
||||||
|
and not placer:get_player_control().sneak then
|
||||||
|
ent.owner = placer:get_player_name()
|
||||||
|
ent.tamed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set nametag
|
||||||
|
local nametag = itemstack:get_meta():get_string("name")
|
||||||
|
if nametag ~= "" then
|
||||||
|
if string.len(nametag) > MAX_MOB_NAME_LENGTH then
|
||||||
|
nametag = string.sub(nametag, 1, MAX_MOB_NAME_LENGTH)
|
||||||
|
end
|
||||||
|
ent.nametag = nametag
|
||||||
|
update_tag(ent)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if not in creative then take item
|
||||||
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
--- Item and armor management
|
||||||
|
|
||||||
|
local function player_near(pos)
|
||||||
|
for _,o in pairs(minetest.get_objects_inside_radius(pos,2)) do
|
||||||
|
if o:is_player() then return true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_armor_texture(armor_name)
|
||||||
|
if armor_name == "" then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
if armor_name=="blank.png" then
|
||||||
|
return "blank.png"
|
||||||
|
end
|
||||||
|
local seperator = string.find(armor_name, ":")
|
||||||
|
return "mcl_armor_"..string.sub(armor_name, seperator+1, -1)..".png^"
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:set_armor_texture()
|
||||||
|
if self.armor_list then
|
||||||
|
local chestplate=minetest.registered_items[self.armor_list.chestplate] or {name=""}
|
||||||
|
local boots=minetest.registered_items[self.armor_list.boots] or {name=""}
|
||||||
|
local leggings=minetest.registered_items[self.armor_list.leggings] or {name=""}
|
||||||
|
local helmet=minetest.registered_items[self.armor_list.helmet] or {name=""}
|
||||||
|
|
||||||
|
if helmet.name=="" and chestplate.name=="" and leggings.name=="" and boots.name=="" then
|
||||||
|
helmet={name="blank.png"}
|
||||||
|
end
|
||||||
|
local texture = get_armor_texture(chestplate.name)..get_armor_texture(helmet.name)..get_armor_texture(boots.name)..get_armor_texture(leggings.name)
|
||||||
|
if string.sub(texture, -1,-1) == "^" then
|
||||||
|
texture=string.sub(texture,1,-2)
|
||||||
|
end
|
||||||
|
if self.textures[self.wears_armor] then
|
||||||
|
self.textures[self.wears_armor]=texture
|
||||||
|
end
|
||||||
|
self.object:set_properties({textures=self.textures})
|
||||||
|
|
||||||
|
local armor_
|
||||||
|
if type(self.armor) == "table" then
|
||||||
|
armor_ = table.copy(self.armor)
|
||||||
|
armor_.immortal = 1
|
||||||
|
else
|
||||||
|
armor_ = {immortal=1, fleshy = self.armor}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,item in pairs(self.armor_list) do
|
||||||
|
if not item then return end
|
||||||
|
if type(minetest.get_item_group(item, "mcl_armor_points")) == "number" then
|
||||||
|
armor_.fleshy=armor_.fleshy-(minetest.get_item_group(item, "mcl_armor_points")*3.5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.object:set_armor_groups(armor_)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_item_pickup()
|
||||||
|
if self.pick_up and #self.pick_up > 0 or self.wears_armor then
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
if not p then return end
|
||||||
|
for _,o in pairs(minetest.get_objects_inside_radius(p,2)) do
|
||||||
|
local l=o:get_luaentity()
|
||||||
|
if l and l.name == "__builtin:item" then
|
||||||
|
if not player_near(p) and l.itemstring:find("mcl_armor") and self.wears_armor then
|
||||||
|
local armor_type
|
||||||
|
if l.itemstring:find("chestplate") then
|
||||||
|
armor_type = "chestplate"
|
||||||
|
elseif l.itemstring:find("boots") then
|
||||||
|
armor_type = "boots"
|
||||||
|
elseif l.itemstring:find("leggings") then
|
||||||
|
armor_type = "leggings"
|
||||||
|
elseif l.itemstring:find("helmet") then
|
||||||
|
armor_type = "helmet"
|
||||||
|
end
|
||||||
|
if not armor_type then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not self.armor_list then
|
||||||
|
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||||
|
elseif self.armor_list[armor_type] and self.armor_list[armor_type] ~= "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.armor_list[armor_type]=ItemStack(l.itemstring):get_name()
|
||||||
|
o:remove()
|
||||||
|
end
|
||||||
|
if self.pick_up then
|
||||||
|
for k,v in pairs(self.pick_up) do
|
||||||
|
if not player_near(p) and self.on_pick_up and l.itemstring:find(v) then
|
||||||
|
local r = self.on_pick_up(self,l)
|
||||||
|
if r and r.is_empty and not r:is_empty() then
|
||||||
|
l.itemstring = r:to_string()
|
||||||
|
elseif r and r.is_empty and r:is_empty() then
|
||||||
|
o:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
# textdomain: mcl_mobs
|
||||||
|
Peaceful mode active! No monsters will spawn.=ピースフルモード有効! モンスターは出現しません。
|
||||||
|
This allows you to place a single mob.=これにより、1体のMOBを配置できます。
|
||||||
|
Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns.=MOBを出現させたい場所に、置くだけです。スニークキーを押しながら配置しない限り、動物は飼いならされた状態でスポーンします。MOBスポナーに設置すると、スポーンするMOBが変わります。
|
||||||
|
You need the “maphack” privilege to change the mob spawner.=MOBスポナーを変更するには、"maphack"権限が必要です。
|
||||||
|
Name Tag=名札
|
||||||
|
A name tag is an item to name a mob.=名札は、MOBに名前をつけるためのアイテムです。
|
||||||
|
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=名札を使用する前に、金床で名前の設定をする必要があります。その後、MOBに名前をつけるために名札が使えます。これで名札は使い切ります。
|
||||||
|
Only peaceful mobs allowed!=平和的なMOBのみ許可!
|
||||||
|
Give names to mobs=MOBに名前を付与
|
||||||
|
Set name at anvil=金床で名前の設定
|
|
@ -1,4 +1,5 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
-- lib_mount by Blert2112 (edited by TenPlus1)
|
||||||
|
|
||||||
local enable_crash = false
|
local enable_crash = false
|
||||||
|
@ -206,7 +207,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||||
-- move forwards
|
-- move forwards
|
||||||
if ctrl.up then
|
if ctrl.up then
|
||||||
|
|
||||||
entity.v = entity.v + entity.accel / 10
|
entity.v = entity.v + entity.accel / 10 * entity.run_velocity / 2.6
|
||||||
|
|
||||||
-- move backwards
|
-- move backwards
|
||||||
elseif ctrl.down then
|
elseif ctrl.down then
|
||||||
|
@ -385,7 +386,6 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||||
entity.v2 = v
|
entity.v2 = v
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||||
|
|
||||||
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||||
|
@ -446,3 +446,18 @@ function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_an
|
||||||
mcl_mobs:set_animation(entity, moving_anim)
|
mcl_mobs:set_animation(entity, moving_anim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
mcl_mobs.mob_class.drive = mcl_mobs.drive
|
||||||
|
mcl_mobs.mob_class.fly = mcl_mobs.fly
|
||||||
|
mcl_mobs.mob_class.attach = mcl_mobs.attach
|
||||||
|
|
||||||
|
function mob_class:on_detach_child(child)
|
||||||
|
if self.detach_child then
|
||||||
|
if self.detach_child(self, child) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.driver == child then
|
||||||
|
self.driver = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,382 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
||||||
|
local PATHFINDING = "gowp"
|
||||||
|
local enable_pathfinding = true
|
||||||
|
|
||||||
|
local LOG_MODULE = "[Mobs]"
|
||||||
|
local function mcl_log (message)
|
||||||
|
if LOGGING_ON and message then
|
||||||
|
minetest.log(LOG_MODULE .. " " .. message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function output_table (wp)
|
||||||
|
if not wp then return end
|
||||||
|
mcl_log("wp items: ".. tostring(#wp))
|
||||||
|
for a,b in pairs(wp) do
|
||||||
|
mcl_log(a.. ": ".. tostring(b))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function append_paths (wp1, wp2)
|
||||||
|
mcl_log("Start append")
|
||||||
|
if not wp1 or not wp2 then
|
||||||
|
mcl_log("Cannot append wp's")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
output_table(wp1)
|
||||||
|
output_table(wp2)
|
||||||
|
for _,a in pairs (wp2) do
|
||||||
|
table.insert(wp1, a)
|
||||||
|
end
|
||||||
|
mcl_log("End append")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function output_enriched (wp_out)
|
||||||
|
mcl_log("Output enriched path")
|
||||||
|
local i = 0
|
||||||
|
for _,outy in pairs (wp_out) do
|
||||||
|
i = i + 1
|
||||||
|
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
||||||
|
|
||||||
|
local action = outy["action"]
|
||||||
|
if action then
|
||||||
|
mcl_log("type: " .. action["type"])
|
||||||
|
mcl_log("action: " .. action["action"])
|
||||||
|
mcl_log("target: " .. minetest.pos_to_string(action["target"]))
|
||||||
|
end
|
||||||
|
mcl_log("failed attempts: " .. outy["failed_attempts"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This function will take a list of paths, and enrich it with:
|
||||||
|
-- a var for failed attempts
|
||||||
|
-- an action, such as to open or close a door where we know that pos requires that action
|
||||||
|
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
||||||
|
local wp_out = {}
|
||||||
|
for i, cur_pos in pairs(wp_in) do
|
||||||
|
local action = nil
|
||||||
|
|
||||||
|
local one_down = vector.new(0,-1,0)
|
||||||
|
local cur_pos_to_add = vector.add(cur_pos, one_down)
|
||||||
|
if door_open_pos and vector.equals (cur_pos, door_open_pos) then
|
||||||
|
mcl_log ("Door open match")
|
||||||
|
--action = {type = "door", action = "open"}
|
||||||
|
action = {}
|
||||||
|
action["type"] = "door"
|
||||||
|
action["action"] = "open"
|
||||||
|
action["target"] = cur_door_pos
|
||||||
|
cur_pos_to_add = vector.add(cur_pos, one_down)
|
||||||
|
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
||||||
|
mcl_log ("Door close match")
|
||||||
|
--action = {type = "door", action = "closed"}
|
||||||
|
action = {}
|
||||||
|
action["type"] = "door"
|
||||||
|
action["action"] = "close"
|
||||||
|
action["target"] = cur_door_pos
|
||||||
|
cur_pos_to_add = vector.add(cur_pos, one_down)
|
||||||
|
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
||||||
|
mcl_log("Current door pos")
|
||||||
|
cur_pos_to_add = vector.add(cur_pos, one_down)
|
||||||
|
action = {}
|
||||||
|
action["type"] = "door"
|
||||||
|
action["action"] = "open"
|
||||||
|
action["target"] = cur_door_pos
|
||||||
|
else
|
||||||
|
cur_pos_to_add = cur_pos
|
||||||
|
--mcl_log ("Pos doesn't match")
|
||||||
|
end
|
||||||
|
|
||||||
|
wp_out[i] = {}
|
||||||
|
wp_out[i]["pos"] = cur_pos_to_add
|
||||||
|
wp_out[i]["failed_attempts"] = 0
|
||||||
|
wp_out[i]["action"] = action
|
||||||
|
|
||||||
|
--wp_out[i] = {"pos" = cur_pos, "failed_attempts" = 0, "action" = action}
|
||||||
|
--output_pos(cur_pos, i)
|
||||||
|
end
|
||||||
|
output_enriched(wp_out)
|
||||||
|
return wp_out
|
||||||
|
end
|
||||||
|
|
||||||
|
local plane_adjacents = {
|
||||||
|
vector.new(1,0,0),
|
||||||
|
vector.new(-1,0,0),
|
||||||
|
vector.new(0,0,1),
|
||||||
|
vector.new(0,0,-1),
|
||||||
|
}
|
||||||
|
|
||||||
|
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
||||||
|
local function calculate_path_through_door (p, t, target)
|
||||||
|
-- target is the same as t, just 1 square difference. Maybe we don't need target
|
||||||
|
mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
|
||||||
|
|
||||||
|
local enriched_path = nil
|
||||||
|
|
||||||
|
local cur_door_pos = nil
|
||||||
|
local pos_closest_to_door = nil
|
||||||
|
local other_side_of_door = nil
|
||||||
|
|
||||||
|
--Path to door first
|
||||||
|
local wp = minetest.find_path(p,t,150,1,4)
|
||||||
|
if not wp then
|
||||||
|
mcl_log("No direct path. Path through door")
|
||||||
|
|
||||||
|
-- This could improve. There could be multiple doors. Check you can path from door to target first.
|
||||||
|
local cur_door_pos = minetest.find_node_near(target,16,{"group:door"})
|
||||||
|
if cur_door_pos then
|
||||||
|
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
|
||||||
|
for _,v in pairs(plane_adjacents) do
|
||||||
|
pos_closest_to_door = vector.add(cur_door_pos,v)
|
||||||
|
|
||||||
|
local n = minetest.get_node(pos_closest_to_door)
|
||||||
|
if n.name == "air" then
|
||||||
|
wp = minetest.find_path(p,pos_closest_to_door,150,1,4)
|
||||||
|
if wp then
|
||||||
|
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
other_side_of_door = vector.add(cur_door_pos,-v)
|
||||||
|
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||||
|
|
||||||
|
local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4)
|
||||||
|
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
||||||
|
table.insert(wp, cur_door_pos)
|
||||||
|
append_paths (wp, wp_otherside_door_to_target)
|
||||||
|
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
|
mcl_log("We have a path from outside door to target")
|
||||||
|
else
|
||||||
|
mcl_log("We cannot path from outside door to target")
|
||||||
|
end
|
||||||
|
break
|
||||||
|
else
|
||||||
|
mcl_log("This block next to door doesn't work.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("Block is not air, it is: ".. n.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("No door found")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("We have a direct route")
|
||||||
|
end
|
||||||
|
|
||||||
|
if wp and not enriched_path then
|
||||||
|
enriched_path = generate_enriched_path(wp)
|
||||||
|
end
|
||||||
|
return enriched_path
|
||||||
|
end
|
||||||
|
|
||||||
|
local gopath_last = os.time()
|
||||||
|
function mob_class:gopath(target,callback_arrived)
|
||||||
|
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
||||||
|
|
||||||
|
if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then
|
||||||
|
mcl_log("We are not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
||||||
|
return
|
||||||
|
else
|
||||||
|
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
|
||||||
|
end
|
||||||
|
|
||||||
|
--if os.time() - gopath_last < 5 then
|
||||||
|
-- mcl_log("Not ready to path yet")
|
||||||
|
-- return
|
||||||
|
--end
|
||||||
|
--gopath_last = os.time()
|
||||||
|
|
||||||
|
self.order = nil
|
||||||
|
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
local t = vector.offset(target,0,1,0)
|
||||||
|
|
||||||
|
local wp = calculate_path_through_door(p, t, target)
|
||||||
|
if not wp then
|
||||||
|
mcl_log("Could not calculate path")
|
||||||
|
self._pf_last_failed = os.time()
|
||||||
|
-- Cover for a flaw in pathfind where it chooses the wrong door and gets stuck. Take a break, allow others.
|
||||||
|
end
|
||||||
|
--output_table(wp)
|
||||||
|
|
||||||
|
if wp and #wp > 0 then
|
||||||
|
self._target = t
|
||||||
|
self.callback_arrived = callback_arrived
|
||||||
|
local current_location = table.remove(wp,1)
|
||||||
|
if current_location and current_location["pos"] then
|
||||||
|
mcl_log("Removing first co-ord? " .. tostring(current_location["pos"]))
|
||||||
|
else
|
||||||
|
mcl_log("Nil pos")
|
||||||
|
end
|
||||||
|
self.current_target = current_location
|
||||||
|
self.waypoints = wp
|
||||||
|
self.state = PATHFINDING
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
self.state = "walk"
|
||||||
|
self.waypoints = nil
|
||||||
|
self.current_target = nil
|
||||||
|
-- minetest.log("no path found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:interact_with_door(action, target)
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
--local t = minetest.get_timeofday()
|
||||||
|
--local dd = minetest.find_nodes_in_area(vector.offset(p,-1,-1,-1),vector.offset(p,1,1,1),{"group:door"})
|
||||||
|
--for _,d in pairs(dd) do
|
||||||
|
if target then
|
||||||
|
mcl_log("Door target is: ".. minetest.pos_to_string(target))
|
||||||
|
|
||||||
|
local n = minetest.get_node(target)
|
||||||
|
if n.name:find("_b_") or n.name:find("_t_") then
|
||||||
|
mcl_log("Door")
|
||||||
|
local def = minetest.registered_nodes[n.name]
|
||||||
|
local closed = n.name:find("_b_1") or n.name:find("_t_1")
|
||||||
|
--if self.state == PATHFINDING then
|
||||||
|
if closed and action == "open" and def.on_rightclick then
|
||||||
|
mcl_log("Open door")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
|
if not closed and action == "close" and def.on_rightclick then
|
||||||
|
mcl_log("Close door")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
|
--else
|
||||||
|
else
|
||||||
|
mcl_log("Not door")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("no target. cannot try and open or close door")
|
||||||
|
end
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:do_pathfind_action(action)
|
||||||
|
if action then
|
||||||
|
mcl_log("Action present")
|
||||||
|
local type = action["type"]
|
||||||
|
local action_val = action["action"]
|
||||||
|
local target = action["target"]
|
||||||
|
if target then
|
||||||
|
mcl_log("Target: ".. minetest.pos_to_string(target))
|
||||||
|
end
|
||||||
|
if type and type == "door" then
|
||||||
|
mcl_log("Type is door")
|
||||||
|
self:interact_with_door(action_val, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local gowp_etime = 0
|
||||||
|
|
||||||
|
function mob_class:check_gowp(dtime)
|
||||||
|
gowp_etime = gowp_etime + dtime
|
||||||
|
|
||||||
|
-- 0.1 is optimal.
|
||||||
|
--less frequently = villager will get sent back after passing a point.
|
||||||
|
--more frequently = villager will fail points they shouldn't they just didn't get there yet
|
||||||
|
|
||||||
|
--if gowp_etime < 0.05 then return end
|
||||||
|
--gowp_etime = 0
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
|
||||||
|
-- no destination
|
||||||
|
if not p or not self._target then
|
||||||
|
mcl_log("p: ".. tostring(p))
|
||||||
|
mcl_log("self._target: ".. tostring(self._target))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- arrived at location, finish gowp
|
||||||
|
local distance_to_targ = vector.distance(p,self._target)
|
||||||
|
--mcl_log("Distance to targ: ".. tostring(distance_to_targ))
|
||||||
|
if distance_to_targ < 2 then
|
||||||
|
mcl_log("Arrived at _target")
|
||||||
|
self.waypoints = nil
|
||||||
|
self._target = nil
|
||||||
|
self.current_target = nil
|
||||||
|
self.state = "stand"
|
||||||
|
self.order = "stand"
|
||||||
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
if self.callback_arrived then return self.callback_arrived(self) end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- More pathing to be done
|
||||||
|
local distance_to_current_target = 50
|
||||||
|
if self.current_target and self.current_target["pos"] then
|
||||||
|
distance_to_current_target = vector.distance(p,self.current_target["pos"])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
||||||
|
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
||||||
|
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
||||||
|
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then
|
||||||
|
-- We have waypoints, and no current target, or we're at it. We need a new current_target.
|
||||||
|
self:do_pathfind_action (self.current_target["action"])
|
||||||
|
|
||||||
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
|
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: " .. distance_to_current_target)
|
||||||
|
|
||||||
|
self.current_target = table.remove(self.waypoints, 1)
|
||||||
|
self:go_to_pos(self.current_target["pos"])
|
||||||
|
return
|
||||||
|
elseif self.current_target and self.current_target["pos"] then
|
||||||
|
-- No waypoints left, but have current target. Potentially last waypoint to go to.
|
||||||
|
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
||||||
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
|
if failed_attempts >= 50 then
|
||||||
|
mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts)
|
||||||
|
self.state = "stand"
|
||||||
|
self.current_target = nil
|
||||||
|
self.waypoints = nil
|
||||||
|
self._target = nil
|
||||||
|
self._pf_last_failed = os.time()
|
||||||
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: ".. distance_to_current_target)
|
||||||
|
self:go_to_pos(self.current_target["pos"])
|
||||||
|
-- Do i just delete current_target, and return so we can find final path.
|
||||||
|
else
|
||||||
|
-- Not at target, no current waypoints or current_target. Through the door and should be able to path to target.
|
||||||
|
-- Is a little sensitive and could take 1 - 7 times. A 10 fail count might be a good exit condition.
|
||||||
|
|
||||||
|
mcl_log("We don't have waypoints or a current target. Let's try to path to target")
|
||||||
|
local final_wp = minetest.find_path(p,self._target,150,1,4)
|
||||||
|
if final_wp then
|
||||||
|
mcl_log("We might be able to get to target here.")
|
||||||
|
-- self.waypoints = final_wp
|
||||||
|
self:go_to_pos(self._target)
|
||||||
|
else
|
||||||
|
-- Abandon route?
|
||||||
|
mcl_log("Cannot plot final route to target")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- I don't think we need the following anymore, but test first.
|
||||||
|
-- Maybe just need something to path to target if no waypoints left
|
||||||
|
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
||||||
|
local updated_p = self.object:get_pos()
|
||||||
|
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
||||||
|
|
||||||
|
mcl_log("Distance to current target: ".. tostring(distance_to_cur_targ))
|
||||||
|
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
||||||
|
|
||||||
|
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
||||||
|
if distance_to_cur_targ > 1.9 then
|
||||||
|
mcl_log("not close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
||||||
|
self:go_to_pos(self._current_target)
|
||||||
|
else
|
||||||
|
mcl_log("close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
||||||
|
self.current_target = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,976 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local ENTITY_CRAMMING_MAX = 24
|
||||||
|
local CRAMMING_DAMAGE = 3
|
||||||
|
local DEATH_DELAY = 0.5
|
||||||
|
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
|
local FLOP_HEIGHT = 6
|
||||||
|
local FLOP_HOR_SPEED = 1.5
|
||||||
|
local PATHFINDING = "gowp"
|
||||||
|
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
|
||||||
|
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
||||||
|
local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
||||||
|
local show_health = false
|
||||||
|
|
||||||
|
-- get node but use fallback for nil or unknown
|
||||||
|
local node_ok = function(pos, fallback)
|
||||||
|
|
||||||
|
fallback = fallback or mcl_mobs.fallback_node
|
||||||
|
|
||||||
|
local node = minetest.get_node_or_nil(pos)
|
||||||
|
|
||||||
|
if node and minetest.registered_nodes[node.name] then
|
||||||
|
return node
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest.registered_nodes[fallback]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if within physical map limits (-30911 to 30927)
|
||||||
|
local function within_limits(pos, radius)
|
||||||
|
local wmin, wmax = -30912, 30928
|
||||||
|
if mcl_vars then
|
||||||
|
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
||||||
|
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if radius then
|
||||||
|
wmin = wmin - radius
|
||||||
|
wmax = wmax + radius
|
||||||
|
end
|
||||||
|
if not pos then return true end
|
||||||
|
for _,v in pairs(pos) do
|
||||||
|
if v < wmin or v > wmax then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:player_in_active_range()
|
||||||
|
for _,p in pairs(minetest.get_connected_players()) do
|
||||||
|
if vector.distance(self.object:get_pos(),p:get_pos()) <= mob_active_range then return true end
|
||||||
|
-- slightly larger than the mc 32 since mobs spawn on that circle and easily stand still immediately right after spawning.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return true if object is in view_range
|
||||||
|
function mob_class:object_in_range(object)
|
||||||
|
if not object then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local factor
|
||||||
|
-- Apply view range reduction for special player armor
|
||||||
|
if object:is_player() then
|
||||||
|
local factors = mcl_armor.player_view_range_factors[object]
|
||||||
|
factor = factors and factors[self.name]
|
||||||
|
end
|
||||||
|
-- Distance check
|
||||||
|
local dist
|
||||||
|
if factor and factor == 0 then
|
||||||
|
return false
|
||||||
|
elseif factor then
|
||||||
|
dist = self.view_range * factor
|
||||||
|
else
|
||||||
|
dist = self.view_range
|
||||||
|
end
|
||||||
|
|
||||||
|
local p1, p2 = self.object:get_pos(), object:get_pos()
|
||||||
|
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:item_drop(cooked, looting_level)
|
||||||
|
|
||||||
|
if not mobs_drop_items then return end
|
||||||
|
|
||||||
|
looting_level = looting_level or 0
|
||||||
|
|
||||||
|
if (self.child and self.type ~= "monster") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj, item, num
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
self.drops = self.drops or {}
|
||||||
|
|
||||||
|
for n = 1, #self.drops do
|
||||||
|
local dropdef = self.drops[n]
|
||||||
|
local chance = 1 / dropdef.chance
|
||||||
|
local looting_type = dropdef.looting
|
||||||
|
|
||||||
|
if looting_level > 0 then
|
||||||
|
local chance_function = dropdef.looting_chance_function
|
||||||
|
if chance_function then
|
||||||
|
chance = chance_function(looting_level)
|
||||||
|
elseif looting_type == "rare" then
|
||||||
|
chance = chance + (dropdef.looting_factor or 0.01) * looting_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local num = 0
|
||||||
|
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
||||||
|
if math.random() < chance then
|
||||||
|
num = math.random(dropdef.min or 1, dropdef.max or 1)
|
||||||
|
elseif not dropdef.looting_ignore_chance then
|
||||||
|
do_common_looting = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if do_common_looting then
|
||||||
|
num = num + math.floor(math.random(0, looting_level) + 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
if num > 0 then
|
||||||
|
item = dropdef.name
|
||||||
|
|
||||||
|
if cooked then
|
||||||
|
|
||||||
|
local output = minetest.get_craft_result({
|
||||||
|
method = "cooking", width = 1, items = {item}})
|
||||||
|
|
||||||
|
if output and output.item and not output.item:is_empty() then
|
||||||
|
item = output.item:get_name()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = 1, num do
|
||||||
|
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj and obj:get_luaentity() then
|
||||||
|
|
||||||
|
obj:set_velocity({
|
||||||
|
x = math.random(-10, 10) / 9,
|
||||||
|
y = 6,
|
||||||
|
z = math.random(-10, 10) / 9,
|
||||||
|
})
|
||||||
|
elseif obj then
|
||||||
|
obj:remove() -- item does not exist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.drops = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- collision function borrowed amended from jordan4ibanez open_ai mod
|
||||||
|
function mob_class:collision()
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
if not pos then return {0,0} end
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
local x = 0
|
||||||
|
local z = 0
|
||||||
|
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
||||||
|
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
||||||
|
|
||||||
|
local ent = object:get_luaentity()
|
||||||
|
if object:is_player() or (ent and ent.is_mob and object ~= self.object) then
|
||||||
|
|
||||||
|
if object:is_player() and mcl_burning.is_burning(self.object) then
|
||||||
|
mcl_burning.set_on_fire(object, 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos2 = object:get_pos()
|
||||||
|
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
|
||||||
|
local force = (width + 0.5) - vector.distance(
|
||||||
|
{x = pos.x, y = 0, z = pos.z},
|
||||||
|
{x = pos2.x, y = 0, z = pos2.z})
|
||||||
|
|
||||||
|
x = x + (vec.x * force)
|
||||||
|
z = z + (vec.z * force)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return({x,z})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- move mob in facing direction
|
||||||
|
function mob_class:set_velocity(v)
|
||||||
|
local c_x, c_y = 0, 0
|
||||||
|
|
||||||
|
-- can mob be pushed, if so calculate direction
|
||||||
|
if self.pushable then
|
||||||
|
c_x, c_y = unpack(self:collision())
|
||||||
|
end
|
||||||
|
|
||||||
|
-- halt mob if it has been ordered to stay
|
||||||
|
if self.order == "stand" or self.order == "sit" then
|
||||||
|
self.acc=vector.new(0,0,0)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local yaw = (self.object:get_yaw() or 0) + self.rotate
|
||||||
|
local vv = self.object:get_velocity()
|
||||||
|
if vv then
|
||||||
|
self.acc={
|
||||||
|
x = ((math.sin(yaw) * -v) + c_x)*.27,
|
||||||
|
y = 0,
|
||||||
|
z = ((math.cos(yaw) * v) + c_y)*.27,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- calculate mob velocity
|
||||||
|
function mob_class:get_velocity()
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
if v then
|
||||||
|
return (v.x * v.x + v.z * v.z) ^ 0.5
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:update_roll()
|
||||||
|
local is_Fleckenstein = self.nametag == "Fleckenstein"
|
||||||
|
local was_Fleckenstein = false
|
||||||
|
|
||||||
|
local rot = self.object:get_rotation()
|
||||||
|
rot.z = is_Fleckenstein and pi or 0
|
||||||
|
self.object:set_rotation(rot)
|
||||||
|
|
||||||
|
local cbox = table.copy(self.collisionbox)
|
||||||
|
local acbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
if math.abs(cbox[2] - acbox[2]) > 0.1 then
|
||||||
|
was_Fleckenstein = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_Fleckenstein ~= was_Fleckenstein then
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
pos.y = pos.y + (acbox[2] + acbox[5])
|
||||||
|
self.object:set_pos(pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_Fleckenstein then
|
||||||
|
cbox[2], cbox[5] = -cbox[5], -cbox[2]
|
||||||
|
self.object:set_properties({collisionbox = cbox})
|
||||||
|
-- This leads to child mobs having the wrong collisionbox
|
||||||
|
-- and seeing as it seems to be nothing but an easter egg
|
||||||
|
-- i've put it inside the if. Which just makes it be upside
|
||||||
|
-- down lol.
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local function shortest_term_of_yaw_rotation(self, rot_origin, rot_target, nums)
|
||||||
|
|
||||||
|
if not rot_origin or not rot_target then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
rot_origin = math.deg(rot_origin)
|
||||||
|
rot_target = math.deg(rot_target)
|
||||||
|
|
||||||
|
if rot_origin < rot_target then
|
||||||
|
if math.abs(rot_origin-rot_target)<180 then
|
||||||
|
if nums then
|
||||||
|
return rot_target-rot_origin
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if nums then
|
||||||
|
return -(rot_origin-(rot_target-360))
|
||||||
|
else
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if math.abs(rot_origin-rot_target)<180 then
|
||||||
|
if nums then
|
||||||
|
return rot_target-rot_origin
|
||||||
|
else
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if nums then
|
||||||
|
return (rot_target-(rot_origin-360))
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- set and return valid yaw
|
||||||
|
function mob_class:set_yaw(yaw, delay, dtime)
|
||||||
|
if self.noyaw then return end
|
||||||
|
|
||||||
|
if self.state ~= PATHFINDING then
|
||||||
|
self._turn_to = yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
--mcl_log("Yaw is: \t\t" .. tostring(math.deg(yaw)))
|
||||||
|
--mcl_log("self.object:get_yaw() is: \t" .. tostring(math.deg(self.object:get_yaw())))
|
||||||
|
|
||||||
|
--clamp our yaw to a 360 range
|
||||||
|
if math.deg(self.object:get_yaw()) > 360 then
|
||||||
|
self.object:set_yaw(math.rad(0))
|
||||||
|
elseif math.deg(self.object:get_yaw()) < 0 then
|
||||||
|
self.object:set_yaw(math.rad(360))
|
||||||
|
end
|
||||||
|
|
||||||
|
if math.deg(yaw) > 360 then
|
||||||
|
yaw=yaw%360
|
||||||
|
elseif math.deg(yaw) < 0 then
|
||||||
|
yaw=((360*5)-yaw)%360
|
||||||
|
end
|
||||||
|
|
||||||
|
--calculate the shortest way to turn to find our target
|
||||||
|
local target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, false)
|
||||||
|
local target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, true)
|
||||||
|
|
||||||
|
--turn in the shortest path possible toward our target. if we are attacking, don't dance.
|
||||||
|
if (math.abs(target_shortest_path) > 50 and not self._kb_turn) and (self.attack and self.attack:get_pos() or self.following and self.following:get_pos()) then
|
||||||
|
if self.following then
|
||||||
|
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), true)
|
||||||
|
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), false)
|
||||||
|
else
|
||||||
|
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), true)
|
||||||
|
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ddtime = 0.05 --set_tick_rate
|
||||||
|
|
||||||
|
if dtime then
|
||||||
|
ddtime = dtime
|
||||||
|
end
|
||||||
|
|
||||||
|
if math.abs(target_shortest_path_nums) > 5 then
|
||||||
|
self.object:set_yaw(self.object:get_yaw()+(target_shortest_path*(3.6*ddtime)))
|
||||||
|
if self.acc then
|
||||||
|
self.acc=vector.rotate_around_axis(self.acc,vector.new(0,1,0), target_shortest_path*(3.6*ddtime))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
delay = delay or 0
|
||||||
|
|
||||||
|
yaw = self.object:get_yaw()
|
||||||
|
|
||||||
|
if delay == 0 then
|
||||||
|
if self.shaking and dtime then
|
||||||
|
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
||||||
|
end
|
||||||
|
--self:update_roll()
|
||||||
|
return yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
self.target_yaw = yaw
|
||||||
|
self.delay = delay
|
||||||
|
|
||||||
|
return self.target_yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
-- global function to set mob yaw
|
||||||
|
function mcl_mobs.yaw(self, yaw, delay, dtime)
|
||||||
|
return mob_class.set_yaw(self, yaw, delay, dtime)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- are we flying in what we are suppose to? (taikedz)
|
||||||
|
function mob_class:flight_check()
|
||||||
|
|
||||||
|
local nod = self.standing_in
|
||||||
|
local def = minetest.registered_nodes[nod]
|
||||||
|
|
||||||
|
if not def then return false end -- nil check
|
||||||
|
|
||||||
|
local fly_in
|
||||||
|
if type(self.fly_in) == "string" then
|
||||||
|
fly_in = { self.fly_in }
|
||||||
|
elseif type(self.fly_in) == "table" then
|
||||||
|
fly_in = self.fly_in
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,checknode in pairs(fly_in) do
|
||||||
|
if nod == checknode or nod == "ignore" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if mob is dead or only hurt
|
||||||
|
function mob_class:check_for_death(cause, cmi_cause)
|
||||||
|
|
||||||
|
if self.state == "die" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- has health actually changed?
|
||||||
|
if self.health == self.old_health and self.health > 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local damaged = self.health < self.old_health
|
||||||
|
self.old_health = self.health
|
||||||
|
|
||||||
|
-- still got some health?
|
||||||
|
if self.health > 0 then
|
||||||
|
|
||||||
|
-- make sure health isn't higher than max
|
||||||
|
if self.health > self.hp_max then
|
||||||
|
self.health = self.hp_max
|
||||||
|
end
|
||||||
|
|
||||||
|
-- play damage sound if health was reduced and make mob flash red.
|
||||||
|
if damaged then
|
||||||
|
self:add_texture_mod("^[colorize:#d42222:175")
|
||||||
|
minetest.after(1, function(self)
|
||||||
|
if self and self.object then
|
||||||
|
self:remove_texture_mod("^[colorize:#d42222:175")
|
||||||
|
end
|
||||||
|
end, self)
|
||||||
|
self:mob_sound("damage")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- backup nametag so we can show health stats
|
||||||
|
if not self.nametag2 then
|
||||||
|
self.nametag2 = self.nametag or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
if show_health
|
||||||
|
and (cmi_cause and cmi_cause.type == "punch") then
|
||||||
|
|
||||||
|
self.htimer = 2
|
||||||
|
self.nametag = "♥ " .. self.health .. " / " .. self.hp_max
|
||||||
|
|
||||||
|
self:update_tag()
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
self:mob_sound("death")
|
||||||
|
|
||||||
|
local function death_handle(self)
|
||||||
|
-- dropped cooked item if mob died in fire or lava
|
||||||
|
if cause == "lava" or cause == "fire" then
|
||||||
|
self:item_drop(true, 0)
|
||||||
|
else
|
||||||
|
local wielditem = ItemStack()
|
||||||
|
if cause == "hit" then
|
||||||
|
local puncher = cmi_cause.puncher
|
||||||
|
if puncher then
|
||||||
|
wielditem = puncher:get_wielded_item()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local cooked = mcl_burning.is_burning(self.object) or mcl_enchanting.has_enchantment(wielditem, "fire_aspect")
|
||||||
|
local looting = mcl_enchanting.get_enchantment(wielditem, "looting")
|
||||||
|
self:item_drop(cooked, looting)
|
||||||
|
|
||||||
|
if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then
|
||||||
|
mcl_experience.throw_xp(self.object:get_pos(), math.random(self.xp_min, self.xp_max))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute custom death function
|
||||||
|
if self.on_die then
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local on_die_exit = self.on_die(self, pos, cmi_cause)
|
||||||
|
if on_die_exit ~= true then
|
||||||
|
death_handle(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if on_die_exit == true then
|
||||||
|
self.state = "die"
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local collisionbox
|
||||||
|
if self.collisionbox then
|
||||||
|
collisionbox = table.copy(self.collisionbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.state = "die"
|
||||||
|
self.attack = nil
|
||||||
|
self.v_start = false
|
||||||
|
self.fall_speed = DEFAULT_FALL_SPEED
|
||||||
|
self.timer = 0
|
||||||
|
self.blinktimer = 0
|
||||||
|
self:remove_texture_mod("^[colorize:#FF000040")
|
||||||
|
self:remove_texture_mod("^[brighten")
|
||||||
|
self.passive = true
|
||||||
|
|
||||||
|
self.object:set_properties({
|
||||||
|
pointable = false,
|
||||||
|
collide_with_objects = false,
|
||||||
|
})
|
||||||
|
|
||||||
|
self:set_velocity(0)
|
||||||
|
local acc = self.object:get_acceleration()
|
||||||
|
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
|
||||||
|
self.object:set_acceleration(acc)
|
||||||
|
|
||||||
|
local length
|
||||||
|
-- default death function and die animation (if defined)
|
||||||
|
if self.instant_death then
|
||||||
|
length = 0
|
||||||
|
elseif self.animation
|
||||||
|
and self.animation.die_start
|
||||||
|
and self.animation.die_end then
|
||||||
|
|
||||||
|
local frames = self.animation.die_end - self.animation.die_start
|
||||||
|
local speed = self.animation.die_speed or 15
|
||||||
|
length = math.max(frames / speed, 0) + DEATH_DELAY
|
||||||
|
self:set_animation( "die")
|
||||||
|
else
|
||||||
|
length = 1 + DEATH_DELAY
|
||||||
|
self:set_animation( "stand", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Remove body after a few seconds and drop stuff
|
||||||
|
local kill = function(self)
|
||||||
|
if not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
death_handle(self)
|
||||||
|
local dpos = self.object:get_pos()
|
||||||
|
local cbox = self.collisionbox
|
||||||
|
local yaw = self.object:get_rotation().y
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
mcl_mobs.death_effect(dpos, yaw, cbox, not self.instant_death)
|
||||||
|
end
|
||||||
|
if length <= 0 then
|
||||||
|
kill(self)
|
||||||
|
else
|
||||||
|
minetest.after(length, kill, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deal light damage to mob, returns true if mob died
|
||||||
|
function mob_class:deal_light_damage(pos, damage)
|
||||||
|
if not ((mcl_weather.rain.raining or mcl_weather.state == "snow") and mcl_weather.is_outdoor(pos)) then
|
||||||
|
self.health = self.health - damage
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
||||||
|
|
||||||
|
if self:check_for_death("light", {type = "light"}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- environmental damage (water, lava, fire, light etc.)
|
||||||
|
function mob_class:do_env_damage()
|
||||||
|
-- feed/tame text timer (so mob 'full' messages dont spam chat)
|
||||||
|
if self.htimer > 0 then
|
||||||
|
self.htimer = self.htimer - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reset nametag after showing health stats
|
||||||
|
if self.htimer < 1 and self.nametag2 then
|
||||||
|
|
||||||
|
self.nametag = self.nametag2
|
||||||
|
self.nametag2 = nil
|
||||||
|
|
||||||
|
self:update_tag()
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
if not pos then return end
|
||||||
|
|
||||||
|
self.time_of_day = minetest.get_timeofday()
|
||||||
|
|
||||||
|
-- remove mob if beyond map limits
|
||||||
|
if not within_limits(pos, 0) then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local sunlight = minetest.get_natural_light(pos, self.time_of_day)
|
||||||
|
|
||||||
|
-- bright light harms mob
|
||||||
|
if self.light_damage ~= 0 and (sunlight or 0) > 12 then
|
||||||
|
if self:deal_light_damage(pos, self.light_damage) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||||
|
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (sunlight or 0) >= minetest.LIGHT_MAX and dim == "overworld" then
|
||||||
|
if self.armor_list and not self.armor_list.helmet or not self.armor_list or self.armor_list and self.armor_list.helmet and self.armor_list.helmet == "" then
|
||||||
|
if self.ignited_by_sunlight then
|
||||||
|
mcl_burning.set_on_fire(self.object, 10)
|
||||||
|
else
|
||||||
|
self:deal_light_damage(pos, self.sunlight_damage)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local y_level = self.collisionbox[2]
|
||||||
|
|
||||||
|
if self.child then
|
||||||
|
y_level = self.collisionbox[2] * 0.5
|
||||||
|
end
|
||||||
|
|
||||||
|
-- what is mob standing in?
|
||||||
|
pos.y = pos.y + y_level + 0.25 -- foot level
|
||||||
|
local pos2 = {x=pos.x, y=pos.y-1, z=pos.z}
|
||||||
|
self.standing_in = node_ok(pos, "air").name
|
||||||
|
self.standing_on = node_ok(pos2, "air").name
|
||||||
|
|
||||||
|
-- don't fall when on ignore, just stand still
|
||||||
|
if self.standing_in == "ignore" then
|
||||||
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
local nodef = minetest.registered_nodes[self.standing_in]
|
||||||
|
|
||||||
|
-- rain
|
||||||
|
if self.rain_damage > 0 then
|
||||||
|
if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
|
||||||
|
|
||||||
|
self.health = self.health - self.rain_damage
|
||||||
|
|
||||||
|
if self:check_for_death("rain", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
pos.y = pos.y + 1 -- for particle effect position
|
||||||
|
|
||||||
|
-- water damage
|
||||||
|
if self.water_damage > 0
|
||||||
|
and nodef.groups.water then
|
||||||
|
|
||||||
|
if self.water_damage ~= 0 then
|
||||||
|
|
||||||
|
self.health = self.health - self.water_damage
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png", nil, nil, 1, nil)
|
||||||
|
|
||||||
|
if self:check_for_death("water", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- lava damage
|
||||||
|
elseif self.lava_damage > 0
|
||||||
|
and (nodef.groups.lava) then
|
||||||
|
|
||||||
|
if self.lava_damage ~= 0 then
|
||||||
|
|
||||||
|
self.health = self.health - self.lava_damage
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
||||||
|
mcl_burning.set_on_fire(self.object, 10)
|
||||||
|
|
||||||
|
if self:check_for_death("lava", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fire damage
|
||||||
|
elseif self.fire_damage > 0
|
||||||
|
and (nodef.groups.fire) then
|
||||||
|
|
||||||
|
if self.fire_damage ~= 0 then
|
||||||
|
|
||||||
|
self.health = self.health - self.fire_damage
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
||||||
|
mcl_burning.set_on_fire(self.object, 5)
|
||||||
|
|
||||||
|
if self:check_for_death("fire", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- damage_per_second node check
|
||||||
|
elseif nodef.damage_per_second ~= 0 and not nodef.groups.lava and not nodef.groups.fire then
|
||||||
|
|
||||||
|
self.health = self.health - nodef.damage_per_second
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
||||||
|
|
||||||
|
if self:check_for_death("dps", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Drowning damage
|
||||||
|
if self.breath_max ~= -1 then
|
||||||
|
local drowning = false
|
||||||
|
if self.breathes_in_water then
|
||||||
|
if minetest.get_item_group(self.standing_in, "water") == 0 then
|
||||||
|
drowning = true
|
||||||
|
end
|
||||||
|
elseif nodef.drowning > 0 then
|
||||||
|
drowning = true
|
||||||
|
end
|
||||||
|
if drowning then
|
||||||
|
|
||||||
|
self.breath = math.max(0, self.breath - 1)
|
||||||
|
|
||||||
|
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
||||||
|
if self.breath <= 0 then
|
||||||
|
local dmg
|
||||||
|
if nodef.drowning > 0 then
|
||||||
|
dmg = nodef.drowning
|
||||||
|
else
|
||||||
|
dmg = 4
|
||||||
|
end
|
||||||
|
self:damage_effect(dmg)
|
||||||
|
self.health = self.health - dmg
|
||||||
|
end
|
||||||
|
if self:check_for_death("drowning", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.breath = math.min(self.breath_max, self.breath + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- suffocation inside solid node
|
||||||
|
-- FIXME: Redundant with mcl_playerplus
|
||||||
|
if (self.suffocation == true)
|
||||||
|
and (nodef.walkable == nil or nodef.walkable == true)
|
||||||
|
and (nodef.collision_box == nil or nodef.collision_box.type == "regular")
|
||||||
|
and (nodef.node_box == nil or nodef.node_box.type == "regular")
|
||||||
|
and (nodef.groups.disable_suffocation ~= 1)
|
||||||
|
and (nodef.groups.opaque == 1) then
|
||||||
|
|
||||||
|
-- Short grace period before starting to take suffocation damage.
|
||||||
|
-- This is different from players, who take damage instantly.
|
||||||
|
-- This has been done because mobs might briefly be inside solid nodes
|
||||||
|
-- when e.g. climbing up stairs.
|
||||||
|
-- This is a bit hacky because it assumes that do_env_damage
|
||||||
|
-- is called roughly every second only.
|
||||||
|
self.suffocation_timer = self.suffocation_timer + 1
|
||||||
|
if self.suffocation_timer >= 3 then
|
||||||
|
-- 2 damage per second
|
||||||
|
-- TODO: Deal this damage once every 1/2 second
|
||||||
|
self.health = self.health - 2
|
||||||
|
|
||||||
|
if self:check_for_death("suffocation", {type = "environment",
|
||||||
|
pos = pos, node = self.standing_in}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.suffocation_timer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return self:check_for_death("", {type = "unknown"})
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:damage_mob(reason,damage)
|
||||||
|
if not self.health then return end
|
||||||
|
damage = math.floor(damage)
|
||||||
|
if damage > 0 then
|
||||||
|
self.health = self.health - damage
|
||||||
|
|
||||||
|
mcl_mobs.effect(self.object:get_pos(), 5, "mcl_particles_smoke.png", 1, 2, 2, nil)
|
||||||
|
|
||||||
|
if self:check_for_death(reason, {type = reason}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_entity_cramming()
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
if not p then return end
|
||||||
|
local oo = minetest.get_objects_inside_radius(p,1)
|
||||||
|
local mobs = {}
|
||||||
|
for _,o in pairs(oo) do
|
||||||
|
local l = o:get_luaentity()
|
||||||
|
if l and l.is_mob and l.health > 0 then table.insert(mobs,l) end
|
||||||
|
end
|
||||||
|
local clear = #mobs < ENTITY_CRAMMING_MAX
|
||||||
|
local ncram = {}
|
||||||
|
for _,l in pairs(mobs) do
|
||||||
|
if l then
|
||||||
|
if clear then
|
||||||
|
l.cram = nil
|
||||||
|
elseif l.cram == nil and not self.child then
|
||||||
|
table.insert(ncram,l)
|
||||||
|
elseif l.cram then
|
||||||
|
l:damage_mob("cramming",CRAMMING_DAMAGE)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i,l in pairs(ncram) do
|
||||||
|
if i > ENTITY_CRAMMING_MAX then
|
||||||
|
l.cram = true
|
||||||
|
else
|
||||||
|
l.cram = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- falling and fall damage
|
||||||
|
-- returns true if mob died
|
||||||
|
function mob_class:falling(pos)
|
||||||
|
|
||||||
|
if self.fly and self.state ~= "die" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED end
|
||||||
|
|
||||||
|
if mcl_portals ~= nil then
|
||||||
|
if mcl_portals.nether_portal_cooloff(self.object) then
|
||||||
|
return false -- mob has teleported through Nether portal - it's 99% not falling
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- floating in water (or falling)
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
|
||||||
|
if v.y > 0 then
|
||||||
|
|
||||||
|
-- apply gravity when moving up
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = 0,
|
||||||
|
y = DEFAULT_FALL_SPEED,
|
||||||
|
z = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
elseif v.y <= 0 and v.y > self.fall_speed then
|
||||||
|
|
||||||
|
-- fall downwards at set speed
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = 0,
|
||||||
|
y = self.fall_speed,
|
||||||
|
z = 0
|
||||||
|
})
|
||||||
|
else
|
||||||
|
-- stop accelerating once max fall speed hit
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.registered_nodes[node_ok(pos).name].groups.lava then
|
||||||
|
|
||||||
|
if self.floats_on_lava == 1 then
|
||||||
|
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = 0,
|
||||||
|
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
|
||||||
|
z = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- in water then float up
|
||||||
|
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
||||||
|
|
||||||
|
if self.floats == 1 then
|
||||||
|
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = 0,
|
||||||
|
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
|
||||||
|
z = 0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
|
||||||
|
-- fall damage onto solid ground
|
||||||
|
if self.fall_damage == 1
|
||||||
|
and self.object:get_velocity().y == 0 then
|
||||||
|
local n = node_ok(vector.offset(pos,0,-1,0)).name
|
||||||
|
local d = (self.old_y or 0) - self.object:get_pos().y
|
||||||
|
|
||||||
|
if d > 5 and n ~= "air" and n ~= "ignore" then
|
||||||
|
local add = minetest.get_item_group(self.standing_on, "fall_damage_add_percent")
|
||||||
|
local damage = d - 5
|
||||||
|
if add ~= 0 then
|
||||||
|
damage = damage + damage * (add/100)
|
||||||
|
end
|
||||||
|
self:damage_mob("fall",damage)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.old_y = self.object:get_pos().y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_water_flow()
|
||||||
|
-- Add water flowing for mobs from mcl_item_entity
|
||||||
|
local p, node, nn, def
|
||||||
|
p = self.object:get_pos()
|
||||||
|
node = minetest.get_node_or_nil(p)
|
||||||
|
if node then
|
||||||
|
nn = node.name
|
||||||
|
def = minetest.registered_nodes[nn]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move item around on flowing liquids
|
||||||
|
if def and def.liquidtype == "flowing" then
|
||||||
|
|
||||||
|
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
||||||
|
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
||||||
|
Luckily, this is exactly what we need if we only care about water, which has this flowing distance. ]]
|
||||||
|
local vec = flowlib.quick_flow(p, node)
|
||||||
|
-- Just to make sure we don't manipulate the speed for no reason
|
||||||
|
if vec.x ~= 0 or vec.y ~= 0 or vec.z ~= 0 then
|
||||||
|
-- Minecraft Wiki: Flowing speed is "about 1.39 meters per second"
|
||||||
|
local f = 1.39
|
||||||
|
-- Set new item moving speed into the direciton of the liquid
|
||||||
|
local newv = vector.multiply(vec, f)
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
self.object:set_velocity({x = newv.x, y = -0.22, z = newv.z})
|
||||||
|
|
||||||
|
self.physical_state = true
|
||||||
|
self._flowing = true
|
||||||
|
self.object:set_properties({
|
||||||
|
physical = true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
elseif self._flowing == true then
|
||||||
|
-- Disable flowing physics if not on/in flowing liquid
|
||||||
|
self._flowing = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_dying()
|
||||||
|
if ((self.state and self.state=="die") or self:check_for_death()) and not self.animation.die_end then
|
||||||
|
local rot = self.object:get_rotation()
|
||||||
|
rot.z = ((math.pi/2-rot.z)*.2)+rot.z
|
||||||
|
self.object:set_rotation(rot)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_suspend()
|
||||||
|
if not self:player_in_active_range() then
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local node_under = node_ok(vector.offset(pos,0,-1,0)).name
|
||||||
|
local acc = self.object:get_acceleration()
|
||||||
|
self:set_animation( "stand", true)
|
||||||
|
if acc.y > 0 or node_under ~= "air" then
|
||||||
|
self.object:set_acceleration(vector.new(0,0,0))
|
||||||
|
self.object:set_velocity(vector.new(0,0,0))
|
||||||
|
end
|
||||||
|
if acc.y == 0 and node_under == "air" then
|
||||||
|
self:falling(pos)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,7 @@
|
||||||
--lua locals
|
--lua locals
|
||||||
local minetest,vector,math,table = minetest,vector,math,table
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
local get_node = minetest.get_node
|
local get_node = minetest.get_node
|
||||||
local get_item_group = minetest.get_item_group
|
local get_item_group = minetest.get_item_group
|
||||||
local get_node_light = minetest.get_node_light
|
local get_node_light = minetest.get_node_light
|
||||||
|
@ -29,6 +31,7 @@ local dbg_spawn_succ = 0
|
||||||
local dbg_spawn_counts = {}
|
local dbg_spawn_counts = {}
|
||||||
-- range for mob count
|
-- range for mob count
|
||||||
local aoc_range = 136
|
local aoc_range = 136
|
||||||
|
local remove_far = true
|
||||||
|
|
||||||
local mob_cap = {
|
local mob_cap = {
|
||||||
monster = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
monster = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
||||||
|
@ -661,34 +664,36 @@ if mobs_spawn then
|
||||||
end
|
end
|
||||||
local mob_def = mob_library_worker_table[mob_index]
|
local mob_def = mob_library_worker_table[mob_index]
|
||||||
--minetest.log(mob_def.name.." "..step_chance.. " "..mob_chance)
|
--minetest.log(mob_def.name.." "..step_chance.. " "..mob_chance)
|
||||||
local spawn_in_group = minetest.registered_entities[mob_def.name].spawn_in_group or 4
|
if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then
|
||||||
local spawn_in_group_min = minetest.registered_entities[mob_def.name].spawn_in_group_min or 1
|
local spawn_in_group = minetest.registered_entities[mob_def.name].spawn_in_group or 4
|
||||||
local mob_type = minetest.registered_entities[mob_def.name].type
|
local spawn_in_group_min = minetest.registered_entities[mob_def.name].spawn_in_group_min or 1
|
||||||
if spawn_check(spawning_position,mob_def) then
|
local mob_type = minetest.registered_entities[mob_def.name].type
|
||||||
if mob_def.type_of_spawning == "water" then
|
if spawn_check(spawning_position,mob_def) then
|
||||||
spawning_position = get_water_spawn(spawning_position)
|
if mob_def.type_of_spawning == "water" then
|
||||||
if not spawning_position then
|
spawning_position = get_water_spawn(spawning_position)
|
||||||
minetest.log("warning","[mcl_mobs] no water spawn for mob "..mob_def.name.." found at "..minetest.pos_to_string(vector.round(pos)))
|
if not spawning_position then
|
||||||
|
minetest.log("warning","[mcl_mobs] no water spawn for mob "..mob_def.name.." found at "..minetest.pos_to_string(vector.round(pos)))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(spawning_position) then
|
||||||
|
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
--everything is correct, spawn mob
|
||||||
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(pos) then
|
local object
|
||||||
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(pos)))
|
if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then
|
||||||
return
|
if logging then
|
||||||
end
|
minetest.log("action", "[mcl_mobs] A group of mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
||||||
--everything is correct, spawn mob
|
end
|
||||||
local object
|
object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min)
|
||||||
if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then
|
|
||||||
if logging then
|
|
||||||
minetest.log("action", "[mcl_mobs] A group of mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
|
||||||
end
|
|
||||||
object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min)
|
|
||||||
|
|
||||||
else
|
else
|
||||||
if logging then
|
if logging then
|
||||||
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at ".. minetest.pos_to_string(spawning_position, 1))
|
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at ".. minetest.pos_to_string(spawning_position, 1))
|
||||||
|
end
|
||||||
|
object = mcl_mobs.spawn(spawning_position, mob_def.name)
|
||||||
end
|
end
|
||||||
object = mcl_mobs.spawn(spawning_position, mob_def.name)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
current_summary_chance = current_summary_chance - mob_chance
|
current_summary_chance = current_summary_chance - mob_chance
|
||||||
|
@ -716,14 +721,36 @@ if mobs_spawn then
|
||||||
-- ignore void and unloaded area
|
-- ignore void and unloaded area
|
||||||
if dimension ~= "void" and dimension ~= "default" then
|
if dimension ~= "void" and dimension ~= "default" then
|
||||||
local y_min, y_max = decypher_limits(pos.y)
|
local y_min, y_max = decypher_limits(pos.y)
|
||||||
for i = 1, math_random(1, 4) do
|
spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
spawn_a_mob(pos, dimension, y_min, y_max)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mob_class:check_despawn(pos)
|
||||||
|
-- Despawning: when lifetimer expires, remove mob
|
||||||
|
if remove_far
|
||||||
|
and self.can_despawn == true
|
||||||
|
and ((not self.nametag) or (self.nametag == ""))
|
||||||
|
and self.state ~= "attack"
|
||||||
|
and self.following == nil then
|
||||||
|
if self.despawn_immediately or self.lifetimer <= 0 then
|
||||||
|
if logging then
|
||||||
|
minetest.log("action", "[mcl_mobs] Mob "..self.name.." despawns at "..minetest.pos_to_string(pos, 1) .. " lifetimer ran out")
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return true
|
||||||
|
elseif self.lifetimer <= 10 then
|
||||||
|
if math.random(10) < 4 then
|
||||||
|
self.despawn_immediately = true
|
||||||
|
else
|
||||||
|
self.lifetimer = 20
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
minetest.register_chatcommand("mobstats",{
|
minetest.register_chatcommand("mobstats",{
|
||||||
privs = { debug = true },
|
privs = { debug = true },
|
||||||
func = function(n,param)
|
func = function(n,param)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_paintings
|
||||||
|
Painting=絵画
|
|
@ -58,6 +58,7 @@ This mod adds mobs which closely resemble the mobs from the game Minecraft, vers
|
||||||
|
|
||||||
### Peaceful mobs
|
### Peaceful mobs
|
||||||
|
|
||||||
|
* Axolotl
|
||||||
* Chicken
|
* Chicken
|
||||||
* Cow
|
* Cow
|
||||||
* Pig
|
* Pig
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
local axolotl = {
|
||||||
|
type = "animal",
|
||||||
|
spawn_class = "water",
|
||||||
|
can_despawn = true,
|
||||||
|
passive = false,
|
||||||
|
hp_min = 14,
|
||||||
|
hp_max = 14,
|
||||||
|
xp_min = 1,
|
||||||
|
xp_max = 7,
|
||||||
|
|
||||||
|
head_swivel = "head.control",
|
||||||
|
bone_eye_height = -1,
|
||||||
|
head_eye_height = -0.5,
|
||||||
|
horrizonatal_head_height = 0,
|
||||||
|
curiosity = 10,
|
||||||
|
head_yaw="z",
|
||||||
|
|
||||||
|
armor = 100,
|
||||||
|
rotate = 180,
|
||||||
|
spawn_in_group_min = 1,
|
||||||
|
spawn_in_group = 4,
|
||||||
|
tilt_swim = true,
|
||||||
|
collisionbox = {-0.5, 0.0, -0.5, 0.5, 0.8, 0.5},
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "mobs_mc_axolotl.b3d",
|
||||||
|
textures = {
|
||||||
|
{"mobs_mc_axolotl_brown.png"},
|
||||||
|
{"mobs_mc_axolotl_yellow.png"},
|
||||||
|
{"mobs_mc_axolotl_green.png"},
|
||||||
|
{"mobs_mc_axolotl_pink.png"},
|
||||||
|
{"mobs_mc_axolotl_black.png"},
|
||||||
|
{"mobs_mc_axolotl_purple.png"},
|
||||||
|
{"mobs_mc_axolotl_white.png"}
|
||||||
|
},
|
||||||
|
sounds = {
|
||||||
|
random = "mobs_mc_axolotl",
|
||||||
|
damage = "mobs_mc_axolotl_hurt",
|
||||||
|
distance = 16,
|
||||||
|
},
|
||||||
|
animation = {-- Stand: 1-20; Walk: 20-60; Swim: 61-81
|
||||||
|
stand_start = 61, stand_end = 81, stand_speed = 15,
|
||||||
|
walk_start = 61, walk_end = 81, walk_speed = 15,
|
||||||
|
run_start = 61, run_end = 81, run_speed = 20,
|
||||||
|
},
|
||||||
|
|
||||||
|
follow = {
|
||||||
|
"mcl_fishing:clownfish_raw"
|
||||||
|
},
|
||||||
|
|
||||||
|
view_range = 16,
|
||||||
|
fear_height = 4,
|
||||||
|
|
||||||
|
on_rightclick = function(self, clicker)
|
||||||
|
local bn = clicker:get_wielded_item():get_name()
|
||||||
|
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
||||||
|
if clicker:set_wielded_item("mcl_buckets:bucket_axolotl") then
|
||||||
|
local it = clicker:get_wielded_item()
|
||||||
|
local m = it:get_meta()
|
||||||
|
m:set_string("properties",minetest.serialize(self.object:get_properties()))
|
||||||
|
clicker:set_wielded_item(it)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
awards.unlock(clicker:get_player_name(), "mcl:cutestPredator")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
|
end,
|
||||||
|
makes_footstep_sound = false,
|
||||||
|
fly = true,
|
||||||
|
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||||
|
breathes_in_water = true,
|
||||||
|
jump = true,
|
||||||
|
damage = 2,
|
||||||
|
reach = 2,
|
||||||
|
attack_type = "dogfight",
|
||||||
|
attack_animals = true,
|
||||||
|
specific_attack = {
|
||||||
|
"extra_mobs_cod",
|
||||||
|
"mobs_mc:sheep",
|
||||||
|
"extra_mobs_glow_squid",
|
||||||
|
"extra_mobs_salmon",
|
||||||
|
"extra_mobs_tropical_fish",
|
||||||
|
"mobs_mc_squid"
|
||||||
|
},
|
||||||
|
runaway = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_mobs.register_mob("mobs_mc:axolotl", axolotl)
|
||||||
|
|
||||||
|
local water = 0
|
||||||
|
|
||||||
|
mcl_mobs:spawn_specific(
|
||||||
|
"mobs_mc:axolotl",
|
||||||
|
"overworld",
|
||||||
|
"water",
|
||||||
|
{
|
||||||
|
"Swampland",
|
||||||
|
"MushroomIsland",
|
||||||
|
"RoofedForest",
|
||||||
|
"FlowerForest_beach",
|
||||||
|
"Forest_beach",
|
||||||
|
"StoneBeach",
|
||||||
|
"Taiga_beach",
|
||||||
|
"Savanna_beach",
|
||||||
|
"Plains_beach",
|
||||||
|
"ExtremeHills_beach",
|
||||||
|
"Swampland_shore",
|
||||||
|
"MushroomIslandShore",
|
||||||
|
"JungleM_shore",
|
||||||
|
"Jungle_shore",
|
||||||
|
"RoofedForest_ocean",
|
||||||
|
"JungleEdgeM_ocean",
|
||||||
|
"BirchForestM_ocean",
|
||||||
|
"BirchForest_ocean",
|
||||||
|
"IcePlains_deep_ocean",
|
||||||
|
"Jungle_deep_ocean",
|
||||||
|
"Savanna_ocean",
|
||||||
|
"MesaPlateauF_ocean",
|
||||||
|
"SunflowerPlains_ocean",
|
||||||
|
"Swampland_ocean",
|
||||||
|
"ExtremeHillsM_ocean",
|
||||||
|
"Mesa_ocean",
|
||||||
|
"StoneBeach_ocean",
|
||||||
|
"Plains_ocean",
|
||||||
|
"MesaPlateauFM_ocean",
|
||||||
|
"MushroomIsland_ocean",
|
||||||
|
"MegaTaiga_ocean",
|
||||||
|
"StoneBeach_deep_ocean",
|
||||||
|
"SavannaM_ocean",
|
||||||
|
"ExtremeHills_ocean",
|
||||||
|
"Forest_ocean",
|
||||||
|
"JungleEdge_ocean",
|
||||||
|
"MesaBryce_ocean",
|
||||||
|
"MegaSpruceTaiga_ocean",
|
||||||
|
"ExtremeHills+_ocean",
|
||||||
|
"Jungle_ocean",
|
||||||
|
"FlowerForest_ocean",
|
||||||
|
"Desert_ocean",
|
||||||
|
"Taiga_ocean",
|
||||||
|
"JungleM_ocean",
|
||||||
|
"FlowerForest_underground",
|
||||||
|
"JungleEdge_underground",
|
||||||
|
"StoneBeach_underground",
|
||||||
|
"MesaBryce_underground",
|
||||||
|
"Mesa_underground",
|
||||||
|
"RoofedForest_underground",
|
||||||
|
"Jungle_underground",
|
||||||
|
"Swampland_underground",
|
||||||
|
"MushroomIsland_underground",
|
||||||
|
"BirchForest_underground",
|
||||||
|
"Plains_underground",
|
||||||
|
"MesaPlateauF_underground",
|
||||||
|
"ExtremeHills_underground",
|
||||||
|
"MegaSpruceTaiga_underground",
|
||||||
|
"BirchForestM_underground",
|
||||||
|
"SavannaM_underground",
|
||||||
|
"MesaPlateauFM_underground",
|
||||||
|
"Desert_underground",
|
||||||
|
"Savanna_underground",
|
||||||
|
"Forest_underground",
|
||||||
|
"SunflowerPlains_underground",
|
||||||
|
"MegaTaiga_underground",
|
||||||
|
"Taiga_underground",
|
||||||
|
"ExtremeHills+_underground",
|
||||||
|
"JungleM_underground",
|
||||||
|
"ExtremeHillsM_underground",
|
||||||
|
"JungleEdgeM_underground",
|
||||||
|
"LushCaves",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
minetest.LIGHT_MAX+1,
|
||||||
|
30,
|
||||||
|
4000,
|
||||||
|
3,
|
||||||
|
water-16,
|
||||||
|
water+1)
|
||||||
|
|
||||||
|
-- spawn eggs
|
||||||
|
mcl_mobs.register_egg("mobs_mc:axolotl", S("Axolotl"), "#e890bf", "#b83D7e", 0)
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:bat", {
|
mcl_mobs.register_mob("mobs_mc:bat", {
|
||||||
description = S("Bat"),
|
description = S("Bat"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "ambient",
|
spawn_class = "ambient",
|
||||||
|
@ -144,4 +144,4 @@ mobs_mc.water_level-1)
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:bat", S("Bat"), "#4c3e30", "#0f0f0f", 0)
|
mcl_mobs.register_egg("mobs_mc:bat", S("Bat"), "#4c3e30", "#0f0f0f", 0)
|
||||||
|
|
|
@ -12,7 +12,7 @@ local mod_target = minetest.get_modpath("mcl_target")
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:blaze", {
|
mcl_mobs.register_mob("mobs_mc:blaze", {
|
||||||
description = S("Blaze"),
|
description = S("Blaze"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -153,7 +153,7 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- Blaze fireball
|
-- Blaze fireball
|
||||||
mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
mcl_mobs.register_arrow("mobs_mc:blaze_fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 0.3, y = 0.3},
|
visual_size = {x = 0.3, y = 0.3},
|
||||||
textures = {"mcl_fire_fire_charge.png"},
|
textures = {"mcl_fire_fire_charge.png"},
|
||||||
|
@ -208,4 +208,4 @@ mcl_mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||||
})
|
})
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:blaze", S("Blaze"), "#f6b201", "#fff87e", 0)
|
mcl_mobs.register_egg("mobs_mc:blaze", S("Blaze"), "#f6b201", "#fff87e", 0)
|
||||||
|
|
|
@ -8,7 +8,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:chicken", {
|
mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
description = S("Chicken"),
|
description = S("Chicken"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -83,7 +83,7 @@ mcl_mobs:register_mob("mobs_mc:chicken", {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
|
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
||||||
end,
|
end,
|
||||||
|
@ -163,4 +163,4 @@ mobs_mc.water_level,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:chicken", S("Chicken"), "#a1a1a1", "#ff0000", 0)
|
mcl_mobs.register_egg("mobs_mc:chicken", S("Chicken"), "#a1a1a1", "#ff0000", 0)
|
||||||
|
|
|
@ -102,7 +102,8 @@ local cod = {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
local bn = clicker:get_wielded_item():get_name()
|
||||||
|
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
clicker:set_wielded_item("mcl_buckets:bucket_cod")
|
clicker:set_wielded_item("mcl_buckets:bucket_cod")
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||||
|
@ -110,7 +111,7 @@ local cod = {
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:cod", cod)
|
mcl_mobs.register_mob("mobs_mc:cod", cod)
|
||||||
|
|
||||||
|
|
||||||
--spawning TODO: in schools
|
--spawning TODO: in schools
|
||||||
|
@ -271,4 +272,4 @@ water-16,
|
||||||
water+1)
|
water+1)
|
||||||
|
|
||||||
--spawn egg
|
--spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:cod", S("Cod"), "#c1a76a", "#e5c48b", 0)
|
mcl_mobs.register_egg("mobs_mc:cod", S("Cod"), "#c1a76a", "#e5c48b", 0)
|
||||||
|
|
|
@ -59,7 +59,7 @@ local cow_def = {
|
||||||
run_start = 41, run_end = 81, run_speed = 60,
|
run_start = 41, run_end = 81, run_speed = 60,
|
||||||
},
|
},
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
if self.child then
|
if self.child then
|
||||||
|
@ -88,7 +88,7 @@ local cow_def = {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:cow", cow_def)
|
mcl_mobs.register_mob("mobs_mc:cow", cow_def)
|
||||||
|
|
||||||
-- Mooshroom
|
-- Mooshroom
|
||||||
local mooshroom_def = table.copy(cow_def)
|
local mooshroom_def = table.copy(cow_def)
|
||||||
|
@ -97,7 +97,7 @@ mooshroom_def.spawn_in_group_min = 4
|
||||||
mooshroom_def.spawn_in_group = 8
|
mooshroom_def.spawn_in_group = 8
|
||||||
mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } }
|
mooshroom_def.textures = { {"mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png"}, {"mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" } }
|
||||||
mooshroom_def.on_rightclick = function(self, clicker)
|
mooshroom_def.on_rightclick = function(self, clicker)
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
if self.child then
|
if self.child then
|
||||||
|
@ -153,7 +153,17 @@ mooshroom_def.on_rightclick = function(self, clicker)
|
||||||
end
|
end
|
||||||
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
mcl_mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
||||||
end
|
end
|
||||||
mcl_mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
|
||||||
|
mooshroom_def.on_lightning_strike = function(self, pos, pos2, objects)
|
||||||
|
if self.base_texture[1] == "mobs_mc_mooshroom_brown.png" then
|
||||||
|
self.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
|
||||||
|
else
|
||||||
|
self.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
|
||||||
|
end
|
||||||
|
self.object:set_properties({ textures = self.base_texture })
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
mcl_mobs.register_mob("mobs_mc:mooshroom", mooshroom_def)
|
||||||
|
|
||||||
|
|
||||||
-- Spawning
|
-- Spawning
|
||||||
|
@ -221,5 +231,5 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:cow", S("Cow"), "#443626", "#a1a1a1", 0)
|
mcl_mobs.register_egg("mobs_mc:cow", S("Cow"), "#443626", "#a1a1a1", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:mooshroom", S("Mooshroom"), "#a00f10", "#b7b7b7", 0)
|
mcl_mobs.register_egg("mobs_mc:mooshroom", S("Mooshroom"), "#a00f10", "#b7b7b7", 0)
|
||||||
|
|
|
@ -9,7 +9,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:creeper", {
|
mcl_mobs.register_mob("mobs_mc:creeper", {
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
spawn_in_group = 1,
|
spawn_in_group = 1,
|
||||||
|
@ -81,7 +81,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
||||||
if self._forced_explosion_countdown_timer ~= nil then
|
if self._forced_explosion_countdown_timer ~= nil then
|
||||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||||
if self._forced_explosion_countdown_timer <= 0 then
|
if self._forced_explosion_countdown_timer <= 0 then
|
||||||
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -133,7 +133,7 @@ mcl_mobs:register_mob("mobs_mc:creeper", {
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
mcl_mobs.register_mob("mobs_mc:creeper_charged", {
|
||||||
description = S("Creeper"),
|
description = S("Creeper"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -203,7 +203,7 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
if self._forced_explosion_countdown_timer ~= nil then
|
if self._forced_explosion_countdown_timer ~= nil then
|
||||||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||||
if self._forced_explosion_countdown_timer <= 0 then
|
if self._forced_explosion_countdown_timer <= 0 then
|
||||||
mcl_mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -219,6 +219,10 @@ mcl_mobs:register_mob("mobs_mc:creeper_charged", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
on_lightning_strike = function(self, pos, pos2, objects)
|
||||||
|
mcl_util.replace_mob(self.object, "mobs_mc:creeper_charged")
|
||||||
|
return true
|
||||||
|
end,
|
||||||
maxdrops = 2,
|
maxdrops = 2,
|
||||||
drops = {
|
drops = {
|
||||||
{name = "mcl_mobitems:gunpowder",
|
{name = "mcl_mobitems:gunpowder",
|
||||||
|
@ -407,4 +411,4 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:creeper", S("Creeper"), "#0da70a", "#000000", 0)
|
mcl_mobs.register_egg("mobs_mc:creeper", S("Creeper"), "#0da70a", "#000000", 0)
|
||||||
|
|
|
@ -92,7 +92,7 @@ local dolphin = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:dolphin", dolphin)
|
mcl_mobs.register_mob("mobs_mc:dolphin", dolphin)
|
||||||
|
|
||||||
|
|
||||||
--spawning TO DO: in schools
|
--spawning TO DO: in schools
|
||||||
|
@ -250,4 +250,4 @@ water-16,
|
||||||
water+1)
|
water+1)
|
||||||
|
|
||||||
--spawn egg
|
--spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:dolphin", S("Dolphin"), "#223b4d", "#f9f9f9", 0)
|
mcl_mobs.register_egg("mobs_mc:dolphin", S("Dolphin"), "#223b4d", "#f9f9f9", 0)
|
||||||
|
|
|
@ -48,7 +48,7 @@ local function check_pos(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
||||||
description = S("Ender Dragon"),
|
description = S("Ender Dragon"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -142,7 +142,7 @@ mcl_mobs:register_mob("mobs_mc:enderdragon", {
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
|
|
||||||
-- dragon fireball (projectile)
|
-- dragon fireball (projectile)
|
||||||
mcl_mobs:register_arrow("mobs_mc:dragon_fireball", {
|
mcl_mobs.register_arrow("mobs_mc:dragon_fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 1.25, y = 1.25},
|
visual_size = {x = 1.25, y = 1.25},
|
||||||
textures = {"mobs_mc_dragon_fireball.png"},
|
textures = {"mobs_mc_dragon_fireball.png"},
|
||||||
|
@ -166,11 +166,11 @@ mcl_mobs:register_arrow("mobs_mc:dragon_fireball", {
|
||||||
|
|
||||||
-- node hit, explode
|
-- node hit, explode
|
||||||
hit_node = function(self, pos, node)
|
hit_node = function(self, pos, node)
|
||||||
mcl_mobs:boom(self, pos, 2)
|
mcl_mobs.mob_class.boom(self,pos, 2)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "#252525", "#b313c9", 0, true)
|
mcl_mobs.register_egg("mobs_mc:enderdragon", S("Ender Dragon"), "#252525", "#b313c9", 0, true)
|
||||||
|
|
||||||
|
|
||||||
mcl_wip.register_wip_item("mobs_mc:enderdragon")
|
mcl_wip.register_wip_item("mobs_mc:enderdragon")
|
||||||
|
|
|
@ -255,7 +255,7 @@ local psdefs = {{
|
||||||
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
|
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:enderman", {
|
mcl_mobs.register_mob("mobs_mc:enderman", {
|
||||||
description = S("Enderman"),
|
description = S("Enderman"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -497,7 +497,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
||||||
self.base_texture = create_enderman_textures(block_type, self._taken_node)
|
self.base_texture = create_enderman_textures(block_type, self._taken_node)
|
||||||
self.object:set_properties({ textures = self.base_texture })
|
self.object:set_properties({ textures = self.base_texture })
|
||||||
self.animation = select_enderman_animation("block")
|
self.animation = select_enderman_animation("block")
|
||||||
mcl_mobs:set_animation(self, self.animation.current)
|
self:set_animation(self.animation.current)
|
||||||
if def.sounds and def.sounds.dug then
|
if def.sounds and def.sounds.dug then
|
||||||
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
|
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
|
||||||
end
|
end
|
||||||
|
@ -520,7 +520,7 @@ mcl_mobs:register_mob("mobs_mc:enderman", {
|
||||||
local def = minetest.registered_nodes[self._taken_node]
|
local def = minetest.registered_nodes[self._taken_node]
|
||||||
-- Update animation accordingly (removes visible block)
|
-- Update animation accordingly (removes visible block)
|
||||||
self.animation = select_enderman_animation("normal")
|
self.animation = select_enderman_animation("normal")
|
||||||
mcl_mobs:set_animation(self, self.animation.current)
|
self:set_animation(self.animation.current)
|
||||||
if def.sounds and def.sounds.place then
|
if def.sounds and def.sounds.place then
|
||||||
minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16}, true)
|
minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16}, true)
|
||||||
end
|
end
|
||||||
|
@ -832,4 +832,4 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:enderman", S("Enderman"), "#252525", "#151515", 0)
|
mcl_mobs.register_egg("mobs_mc:enderman", S("Enderman"), "#252525", "#151515", 0)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:endermite", {
|
mcl_mobs.register_mob("mobs_mc:endermite", {
|
||||||
description = S("Endermite"),
|
description = S("Endermite"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -38,4 +38,4 @@ mcl_mobs:register_mob("mobs_mc:endermite", {
|
||||||
reach = 1,
|
reach = 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:endermite", S("Endermite"), "#161616", "#6d6d6d", 0)
|
mcl_mobs.register_egg("mobs_mc:endermite", S("Endermite"), "#161616", "#6d6d6d", 0)
|
||||||
|
|
|
@ -10,7 +10,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:ghast", {
|
mcl_mobs.register_mob("mobs_mc:ghast", {
|
||||||
description = S("Ghast"),
|
description = S("Ghast"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -103,7 +103,7 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- fireball (projectile)
|
-- fireball (projectile)
|
||||||
mcl_mobs:register_arrow("mobs_mc:fireball", {
|
mcl_mobs.register_arrow("mobs_mc:fireball", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 1, y = 1},
|
visual_size = {x = 1, y = 1},
|
||||||
textures = {"mcl_fire_fire_charge.png"},
|
textures = {"mcl_fire_fire_charge.png"},
|
||||||
|
@ -118,9 +118,9 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
||||||
}, nil)
|
}, nil)
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
if p then
|
if p then
|
||||||
mcl_mobs:boom(self, p, 1, true)
|
mcl_mobs.mob_class.boom(self,p, 1, true)
|
||||||
else
|
else
|
||||||
mcl_mobs:boom(self, player:get_pos(), 1, true)
|
mcl_mobs.mob_class.boom(self,player:get_pos(), 1, true)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -129,11 +129,11 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
||||||
full_punch_interval = 1.0,
|
full_punch_interval = 1.0,
|
||||||
damage_groups = {fleshy = 6},
|
damage_groups = {fleshy = 6},
|
||||||
}, nil)
|
}, nil)
|
||||||
mcl_mobs:boom(self, self.object:get_pos(), 1, true)
|
mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1, true)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
hit_node = function(self, pos, node)
|
hit_node = function(self, pos, node)
|
||||||
mcl_mobs:boom(self, pos, 1, true)
|
mcl_mobs.mob_class.boom(self,pos, 1, true)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -141,4 +141,4 @@ mcl_mobs:register_arrow("mobs_mc:fireball", {
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:ghast", S("Ghast"), "#f9f9f9", "#bcbcbc", 0)
|
mcl_mobs.register_egg("mobs_mc:ghast", S("Ghast"), "#f9f9f9", "#bcbcbc", 0)
|
||||||
|
|
|
@ -29,7 +29,7 @@ for i=1,4 do
|
||||||
table.insert(psdefs,p)
|
table.insert(psdefs,p)
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:glow_squid", {
|
mcl_mobs.register_mob("mobs_mc:glow_squid", {
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "water",
|
spawn_class = "water",
|
||||||
can_despawn = true,
|
can_despawn = true,
|
||||||
|
@ -243,4 +243,4 @@ mcl_mobs:spawn_specific(
|
||||||
water)
|
water)
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:glow_squid", S("Glow Squid"), "#095757", "#87f6c0", 0)
|
mcl_mobs.register_egg("mobs_mc:glow_squid", S("Glow Squid"), "#095757", "#87f6c0", 0)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:guardian", {
|
mcl_mobs.register_mob("mobs_mc:guardian", {
|
||||||
description = S("Guardian"),
|
description = S("Guardian"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -104,4 +104,4 @@ mcl_mobs:register_mob("mobs_mc:guardian", {
|
||||||
--mcl_mobs:spawn_specific("mobs_mc:guardian", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level - 10)
|
--mcl_mobs:spawn_specific("mobs_mc:guardian", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 25000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level - 10)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:guardian", S("Guardian"), "#5a8272", "#f17d31", 0)
|
mcl_mobs.register_egg("mobs_mc:guardian", S("Guardian"), "#5a8272", "#f17d31", 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
mcl_mobs.register_mob("mobs_mc:guardian_elder", {
|
||||||
description = S("Elder Guardian"),
|
description = S("Elder Guardian"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -112,6 +112,6 @@ mcl_mobs:register_mob("mobs_mc:guardian_elder", {
|
||||||
-- mcl_mobs:spawn_specific("mobs_mc:guardian_elder", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level-18)
|
-- mcl_mobs:spawn_specific("mobs_mc:guardian_elder", { "mcl_core:water_source", "mclx_core:river_water_source" }, { "mcl_core:water_source", "mclx_core:river_water_source" }, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mcl_vars.mg_overworld_min, mobs_mc.water_level-18)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "#ceccba", "#747693", 0)
|
mcl_mobs.register_egg("mobs_mc:guardian_elder", S("Elder Guardian"), "#ceccba", "#747693", 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ local hoglin = {
|
||||||
attack_animals = true,
|
attack_animals = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:hoglin", hoglin)
|
mcl_mobs.register_mob("mobs_mc:hoglin", hoglin)
|
||||||
|
|
||||||
local zoglin = table.copy(hoglin)
|
local zoglin = table.copy(hoglin)
|
||||||
zoglin.fire_resistant = 1
|
zoglin.fire_resistant = 1
|
||||||
|
@ -95,7 +95,7 @@ end
|
||||||
zoglin.attacks_monsters = true
|
zoglin.attacks_monsters = true
|
||||||
zoglin.lava_damage = 0
|
zoglin.lava_damage = 0
|
||||||
zoglin.fire_damage = 0
|
zoglin.fire_damage = 0
|
||||||
mcl_mobs:register_mob("mobs_mc:zoglin", zoglin)
|
mcl_mobs.register_mob("mobs_mc:zoglin", zoglin)
|
||||||
|
|
||||||
-- Baby hoglin.
|
-- Baby hoglin.
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ baby_hoglin.walk_velocity = 1.2
|
||||||
baby_hoglin.run_velocity = 2.4
|
baby_hoglin.run_velocity = 2.4
|
||||||
baby_hoglin.child = 1
|
baby_hoglin.child = 1
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:baby_hoglin", baby_hoglin)
|
mcl_mobs.register_mob("mobs_mc:baby_hoglin", baby_hoglin)
|
||||||
|
|
||||||
-- Regular spawning in the Nether
|
-- Regular spawning in the Nether
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
@ -132,4 +132,4 @@ mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:hoglin", S("Hoglin"), "#85682e", "#2b2140", 0)
|
mcl_mobs.register_egg("mobs_mc:hoglin", S("Hoglin"), "#85682e", "#2b2140", 0)
|
||||||
|
|
|
@ -181,6 +181,12 @@ local horse = {
|
||||||
on_spawn = update_textures,
|
on_spawn = update_textures,
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
|
if not self._horse_speed then
|
||||||
|
self._horse_speed = math.random(486, 1457)/100
|
||||||
|
elseif self.run_velocity ~= self._horse_speed then
|
||||||
|
self.run_velocity = self._horse_speed
|
||||||
|
end
|
||||||
|
|
||||||
-- set needed values if not already present
|
-- set needed values if not already present
|
||||||
if not self._regentimer then
|
if not self._regentimer then
|
||||||
self._regentimer = 0
|
self._regentimer = 0
|
||||||
|
@ -331,7 +337,7 @@ local horse = {
|
||||||
elseif (iname == "mcl_farming:carrot_item_gold") then
|
elseif (iname == "mcl_farming:carrot_item_gold") then
|
||||||
heal = 4
|
heal = 4
|
||||||
end
|
end
|
||||||
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, true, false) then
|
if heal > 0 and self:feed_tame(clicker, heal, true, false) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -346,7 +352,7 @@ local horse = {
|
||||||
elseif (iname == "mcl_farming:hay_block") then
|
elseif (iname == "mcl_farming:hay_block") then
|
||||||
heal = 20
|
heal = 20
|
||||||
end
|
end
|
||||||
if heal > 0 and mcl_mobs:feed_tame(self, clicker, heal, false, false) then
|
if heal > 0 and self:feed_tame(clicker, heal, false, false) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -435,7 +441,7 @@ local horse = {
|
||||||
|
|
||||||
on_breed = function(parent1, parent2)
|
on_breed = function(parent1, parent2)
|
||||||
local pos = parent1.object:get_pos()
|
local pos = parent1.object:get_pos()
|
||||||
local child = mcl_mobs:spawn_child(pos, parent1.name)
|
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
local p = math.random(1, 2)
|
local p = math.random(1, 2)
|
||||||
|
@ -484,7 +490,7 @@ local horse = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:horse", horse)
|
mcl_mobs.register_mob("mobs_mc:horse", horse)
|
||||||
|
|
||||||
-- Skeleton horse
|
-- Skeleton horse
|
||||||
local skeleton_horse = table.copy(horse)
|
local skeleton_horse = table.copy(horse)
|
||||||
|
@ -507,7 +513,7 @@ skeleton_horse.sounds = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
}
|
}
|
||||||
skeleton_horse.harmed_by_heal = true
|
skeleton_horse.harmed_by_heal = true
|
||||||
mcl_mobs:register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
mcl_mobs.register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
||||||
|
|
||||||
-- Zombie horse
|
-- Zombie horse
|
||||||
local zombie_horse = table.copy(horse)
|
local zombie_horse = table.copy(horse)
|
||||||
|
@ -531,7 +537,7 @@ zombie_horse.sounds = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
}
|
}
|
||||||
zombie_horse.harmed_by_heal = true
|
zombie_horse.harmed_by_heal = true
|
||||||
mcl_mobs:register_mob("mobs_mc:zombie_horse", zombie_horse)
|
mcl_mobs.register_mob("mobs_mc:zombie_horse", zombie_horse)
|
||||||
|
|
||||||
-- Donkey
|
-- Donkey
|
||||||
local d = 0.86 -- donkey scale
|
local d = 0.86 -- donkey scale
|
||||||
|
@ -565,7 +571,7 @@ donkey.jump = true
|
||||||
donkey.jump_height = 3.75 -- can clear 1 block height
|
donkey.jump_height = 3.75 -- can clear 1 block height
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:donkey", donkey)
|
mcl_mobs.register_mob("mobs_mc:donkey", donkey)
|
||||||
mcl_entity_invs.register_inv("mobs_mc:donkey","Donkey",15,true)
|
mcl_entity_invs.register_inv("mobs_mc:donkey","Donkey",15,true)
|
||||||
-- Mule
|
-- Mule
|
||||||
local m = 0.94
|
local m = 0.94
|
||||||
|
@ -583,7 +589,7 @@ mule.collisionbox = {
|
||||||
horse.collisionbox[5] * m,
|
horse.collisionbox[5] * m,
|
||||||
horse.collisionbox[6] * m,
|
horse.collisionbox[6] * m,
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:mule", mule)
|
mcl_mobs.register_mob("mobs_mc:mule", mule)
|
||||||
mcl_entity_invs.register_inv("mobs_mc:mule","Mule",15,true)
|
mcl_entity_invs.register_inv("mobs_mc:mule","Mule",15,true)
|
||||||
|
|
||||||
--===========================
|
--===========================
|
||||||
|
@ -635,8 +641,8 @@ mobs_mc.water_level+3,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:horse", S("Horse"), "#c09e7d", "#eee500", 0)
|
mcl_mobs.register_egg("mobs_mc:horse", S("Horse"), "#c09e7d", "#eee500", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "#68684f", "#e5e5d8", 0)
|
mcl_mobs.register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "#68684f", "#e5e5d8", 0)
|
||||||
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "#2a5a37", "#84d080", 0)
|
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "#2a5a37", "#84d080", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:donkey", S("Donkey"), "#534539", "#867566", 0)
|
mcl_mobs.register_egg("mobs_mc:donkey", S("Donkey"), "#534539", "#867566", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:mule", S("Mule"), "#1b0200", "#51331d", 0)
|
mcl_mobs.register_egg("mobs_mc:mule", S("Mule"), "#1b0200", "#51331d", 0)
|
||||||
|
|
|
@ -96,6 +96,7 @@ mobs_mc.water_level = tonumber(minetest.settings:get("water_level")) or 0
|
||||||
|
|
||||||
-- Animals
|
-- Animals
|
||||||
local path = minetest.get_modpath("mobs_mc")
|
local path = minetest.get_modpath("mobs_mc")
|
||||||
|
dofile(path .. "/axolotl.lua") -- Mesh and animation by JoeEnderman, Textures by Nova Wustra, modified by JoeEnderman
|
||||||
dofile(path .. "/bat.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/bat.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/rabbit.lua") -- Mesh and animation byExeterDad
|
dofile(path .. "/rabbit.lua") -- Mesh and animation byExeterDad
|
||||||
dofile(path .. "/chicken.lua") -- Mesh and animation by Pavel_S
|
dofile(path .. "/chicken.lua") -- Mesh and animation by Pavel_S
|
||||||
|
@ -154,3 +155,5 @@ dofile(path .. "/glow_squid.lua")
|
||||||
|
|
||||||
dofile(path .. "/piglin.lua")
|
dofile(path .. "/piglin.lua")
|
||||||
dofile(path .. "/hoglin+zoglin.lua")
|
dofile(path .. "/hoglin+zoglin.lua")
|
||||||
|
|
||||||
|
dofile(path .. "/strider.lua")
|
||||||
|
|
|
@ -11,7 +11,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
local etime = 0
|
local etime = 0
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
description = S("Iron Golem"),
|
description = S("Iron Golem"),
|
||||||
type = "npc",
|
type = "npc",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -89,7 +89,7 @@ mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
||||||
etime = etime + dtime
|
etime = etime + dtime
|
||||||
if etime > 10 then
|
if etime > 10 then
|
||||||
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
|
if self._home and vector.distance(self._home,self.object:get_pos()) > 50 then
|
||||||
mcl_mobs:gopath(self,self._home)
|
self:gopath(self._home)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -97,7 +97,7 @@ mcl_mobs:register_mob("mobs_mc:iron_golem", {
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:iron_golem", S("Iron Golem"), "#3b3b3b", "#f57223", 0)
|
mcl_mobs.register_egg("mobs_mc:iron_golem", S("Iron Golem"), "#3b3b3b", "#f57223", 0)
|
||||||
|
|
||||||
--[[ This is to be called when a pumpkin or jack'o lantern has been placed. Recommended: In the on_construct function of the node.
|
--[[ This is to be called when a pumpkin or jack'o lantern has been placed. Recommended: In the on_construct function of the node.
|
||||||
This summons an iron golen if placing the pumpkin created an iron golem summon pattern:
|
This summons an iron golen if placing the pumpkin created an iron golem summon pattern:
|
||||||
|
|
|
@ -47,7 +47,7 @@ local function get_drops(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:llama", {
|
mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
description = S("Llama"),
|
description = S("Llama"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -156,7 +156,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
||||||
local item = clicker:get_wielded_item()
|
local item = clicker:get_wielded_item()
|
||||||
if item:get_name() == "mcl_farming:hay_block" then
|
if item:get_name() == "mcl_farming:hay_block" then
|
||||||
-- Breed with hay bale
|
-- Breed with hay bale
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
elseif not self._has_chest and item:get_name() == "mcl_chests:chest" then
|
elseif not self._has_chest and item:get_name() == "mcl_chests:chest" then
|
||||||
item:take_item()
|
item:take_item()
|
||||||
clicker:set_wielded_item(item)
|
clicker:set_wielded_item(item)
|
||||||
|
@ -173,7 +173,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
-- Feed with anything else
|
-- Feed with anything else
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, false, true) then return end
|
if self:feed_tame(clicker, 1, false, true) then return end
|
||||||
end
|
end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
||||||
else
|
else
|
||||||
parent = parent2
|
parent = parent2
|
||||||
end
|
end
|
||||||
child = mcl_mobs:spawn_child(pos, parent.name)
|
child = mcl_mobs.spawn_child(pos, parent.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
ent_c.base_texture = table.copy(ent_c.base_texture)
|
ent_c.base_texture = table.copy(ent_c.base_texture)
|
||||||
|
@ -254,7 +254,7 @@ mcl_mobs:register_mob("mobs_mc:llama", {
|
||||||
mcl_entity_invs.register_inv("mobs_mc:llama","Llama",nil,true)
|
mcl_entity_invs.register_inv("mobs_mc:llama","Llama",nil,true)
|
||||||
|
|
||||||
-- spit arrow (weapon)
|
-- spit arrow (weapon)
|
||||||
mcl_mobs:register_arrow("mobs_mc:llamaspit", {
|
mcl_mobs.register_arrow("mobs_mc:llamaspit", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 0.10, y = 0.10},
|
visual_size = {x = 0.10, y = 0.10},
|
||||||
textures = {"mobs_mc_llama_spit.png"},
|
textures = {"mobs_mc_llama_spit.png"},
|
||||||
|
@ -297,4 +297,4 @@ mobs_mc.water_level+15,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:llama", S("Llama"), "#c09e7d", "#995f40", 0)
|
mcl_mobs.register_egg("mobs_mc:llama", S("Llama"), "#c09e7d", "#995f40", 0)
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
# textdomain: mobs_mc
|
||||||
|
Agent=エージェント
|
||||||
|
Axolotl=ウーパールーパー
|
||||||
|
Bat=コウモリ
|
||||||
|
Blaze=ブレイズ
|
||||||
|
Chicken=ニワトリ
|
||||||
|
Cow=ウシ
|
||||||
|
Mooshroom=ムーシュルーム
|
||||||
|
Creeper=クリーパー
|
||||||
|
Ender Dragon=エンダードラゴン
|
||||||
|
Enderman=エンダーマン
|
||||||
|
Endermite=エンダーマイト
|
||||||
|
Ghast=ガスト
|
||||||
|
Elder Guardian=エルダーガーディアン
|
||||||
|
Guardian=ガーディアン
|
||||||
|
Horse=ウマ
|
||||||
|
Skeleton Horse=スケルトンホース
|
||||||
|
Zombie Horse=ゾンビホース
|
||||||
|
Donkey=ロバ
|
||||||
|
Mule=ラバ
|
||||||
|
Iron Golem=アイアンゴーレム
|
||||||
|
Llama=ラマ
|
||||||
|
Ocelot=ヤマネコ
|
||||||
|
Parrot=オウム
|
||||||
|
Pig=ブタ
|
||||||
|
Polar Bear=シロクマ
|
||||||
|
Rabbit=ウサギ
|
||||||
|
Killer Bunny=殺人ウサギ
|
||||||
|
Sheep=ヒツジ
|
||||||
|
Shulker=シュルカー
|
||||||
|
Silverfish=シルバーフィッシュ
|
||||||
|
Skeleton=スケルトン
|
||||||
|
Stray=ストレイ
|
||||||
|
Wither Skeleton=ウィザースケルトン
|
||||||
|
Magma Cube=マグマキューブ
|
||||||
|
Slime=スライム
|
||||||
|
Snow Golem=スノーゴーレム
|
||||||
|
Spider=クモ
|
||||||
|
Cave Spider=洞窟グモ
|
||||||
|
Squid=イカ
|
||||||
|
Vex=ヴェックス
|
||||||
|
Evoker=エヴォーカー
|
||||||
|
Illusioner=イリュージョナー
|
||||||
|
Villager=村人
|
||||||
|
Vindicator=ヴィンディケーター
|
||||||
|
Zombie Villager=村人ゾンビ
|
||||||
|
Witch=魔女
|
||||||
|
Wither=ウィザー
|
||||||
|
Wolf=オオカミ
|
||||||
|
Husk=ハスク
|
||||||
|
Zombie=ゾンビ
|
||||||
|
Zombie Pigman=ゾンビピッグマン
|
||||||
|
Farmer=農民
|
||||||
|
Fisherman=漁師
|
||||||
|
Fletcher=矢師
|
||||||
|
Shepherd=羊飼い
|
||||||
|
Librarian=司書
|
||||||
|
Cartographer=製図家
|
||||||
|
Armorer=防具鍛冶
|
||||||
|
Leatherworker=革職人
|
||||||
|
Butcher=肉屋
|
||||||
|
Weapon Smith=武器鍛冶
|
||||||
|
Tool Smith=道具鍛冶
|
||||||
|
Cleric=聖職者
|
||||||
|
Nitwit=求職者
|
||||||
|
Cod=タラ
|
||||||
|
Salmon=サケ
|
||||||
|
Dolphin=イルカ
|
||||||
|
Pillager=ピリジャー
|
||||||
|
Tropical fish=クマノミ
|
|
@ -1,5 +1,6 @@
|
||||||
# textdomain: mobs_mc
|
# textdomain: mobs_mc
|
||||||
Agent=
|
Agent=
|
||||||
|
Axolotl=
|
||||||
Bat=
|
Bat=
|
||||||
Blaze=
|
Blaze=
|
||||||
Chicken=
|
Chicken=
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
name = mobs_mc
|
name = mobs_mc
|
||||||
author = maikerumine
|
author = maikerumine
|
||||||
description = Adds Minecraft-like monsters and animals.
|
description = Adds Minecraft-like monsters and animals.
|
||||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core
|
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util
|
||||||
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items
|
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ local ocelot = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:ocelot", ocelot)
|
mcl_mobs.register_mob("mobs_mc:ocelot", ocelot)
|
||||||
|
|
||||||
-- Cat
|
-- Cat
|
||||||
local cat = table.copy(ocelot)
|
local cat = table.copy(ocelot)
|
||||||
|
@ -130,7 +130,7 @@ cat.sounds = {
|
||||||
distance = 16,
|
distance = 16,
|
||||||
}
|
}
|
||||||
cat.on_rightclick = function(self, clicker)
|
cat.on_rightclick = function(self, clicker)
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
if mcl_mobs:capture_mob(self, clicker, 0, 60, 5, false, nil) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ cat.on_spawn = function(self)
|
||||||
self.object:set_properties({textures = {self._texture}})
|
self.object:set_properties({textures = {self._texture}})
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:cat", cat)
|
mcl_mobs.register_mob("mobs_mc:cat", cat)
|
||||||
|
|
||||||
local base_spawn_chance = 5000
|
local base_spawn_chance = 5000
|
||||||
|
|
||||||
|
@ -249,4 +249,4 @@ mobs:spawn({
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:ocelot", S("Ocelot"), "#efde7d", "#564434", 0)
|
mcl_mobs.register_egg("mobs_mc:ocelot", S("Ocelot"), "#efde7d", "#564434", 0)
|
||||||
|
|
|
@ -87,7 +87,7 @@ local function perch(self,player)
|
||||||
local shoulder = get_shoulder(player)
|
local shoulder = get_shoulder(player)
|
||||||
if not shoulder then return true end
|
if not shoulder then return true end
|
||||||
self.object:set_attach(player,"",shoulder,vector.new(0,0,0),true)
|
self.object:set_attach(player,"",shoulder,vector.new(0,0,0),true)
|
||||||
mcl_mobs:set_animation(self, "stand")
|
self:set_animation("stand")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ local function check_perch(self,dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:parrot", {
|
mcl_mobs.register_mob("mobs_mc:parrot", {
|
||||||
description = S("Parrot"),
|
description = S("Parrot"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -207,7 +207,7 @@ mcl_mobs:register_mob("mobs_mc:parrot", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Feed to tame, but not breed
|
-- Feed to tame, but not breed
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, false, true) then return end
|
if self:feed_tame(clicker, 1, false, true) then return end
|
||||||
perch(self,clicker)
|
perch(self,clicker)
|
||||||
end,
|
end,
|
||||||
do_custom = function(self,dtime)
|
do_custom = function(self,dtime)
|
||||||
|
@ -241,4 +241,4 @@ mobs_mc.water_level+7,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:parrot", S("Parrot"), "#0da70a", "#ff0000", 0)
|
mcl_mobs.register_egg("mobs_mc:parrot", S("Parrot"), "#0da70a", "#ff0000", 0)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:pig", {
|
mcl_mobs.register_mob("mobs_mc:pig", {
|
||||||
description = S("Pig"),
|
description = S("Pig"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -60,6 +60,10 @@ mcl_mobs:register_mob("mobs_mc:pig", {
|
||||||
"mcl_mobitems:carrot_on_a_stick"
|
"mcl_mobitems:carrot_on_a_stick"
|
||||||
},
|
},
|
||||||
view_range = 8,
|
view_range = 8,
|
||||||
|
on_lightning_strike = function(self, pos, pos2, objects)
|
||||||
|
mcl_util.replace_mob(self.object, "mobs_mc:zombified_piglin")
|
||||||
|
return true
|
||||||
|
end,
|
||||||
do_custom = function(self, dtime)
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
-- set needed values if not already present
|
-- set needed values if not already present
|
||||||
|
@ -104,7 +108,7 @@ mcl_mobs:register_mob("mobs_mc:pig", {
|
||||||
local wielditem = clicker:get_wielded_item()
|
local wielditem = clicker:get_wielded_item()
|
||||||
-- Feed pig
|
-- Feed pig
|
||||||
if wielditem:get_name() ~= "mcl_mobitems:carrot_on_a_stick" then
|
if wielditem:get_name() ~= "mcl_mobitems:carrot_on_a_stick" then
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
end
|
end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
|
@ -182,7 +186,7 @@ mcl_mobs:register_mob("mobs_mc:pig", {
|
||||||
|
|
||||||
on_breed = function(parent1, parent2)
|
on_breed = function(parent1, parent2)
|
||||||
local pos = parent1.object:get_pos()
|
local pos = parent1.object:get_pos()
|
||||||
local child = mcl_mobs:spawn_child(pos, parent1.name)
|
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
ent_c.tamed = true
|
ent_c.tamed = true
|
||||||
|
@ -238,4 +242,4 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:pig", S("Pig"), "#f0a5a2", "#db635f", 0)
|
mcl_mobs.register_egg("mobs_mc:pig", S("Pig"), "#f0a5a2", "#db635f", 0)
|
||||||
|
|
|
@ -174,7 +174,7 @@ local piglin = {
|
||||||
specific_attack = { "player", "mobs_mc:hoglin" },
|
specific_attack = { "player", "mobs_mc:hoglin" },
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:piglin", piglin)
|
mcl_mobs.register_mob("mobs_mc:piglin", piglin)
|
||||||
|
|
||||||
|
|
||||||
local sword_piglin = table.copy(piglin)
|
local sword_piglin = table.copy(piglin)
|
||||||
|
@ -206,7 +206,7 @@ sword_piglin.animation = {
|
||||||
punch_start = 189,
|
punch_start = 189,
|
||||||
punch_end = 198,
|
punch_end = 198,
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:sword_piglin", sword_piglin)
|
mcl_mobs.register_mob("mobs_mc:sword_piglin", sword_piglin)
|
||||||
|
|
||||||
local zombified_piglin = table.copy(piglin)
|
local zombified_piglin = table.copy(piglin)
|
||||||
zombified_piglin.fire_resistant = 1
|
zombified_piglin.fire_resistant = 1
|
||||||
|
@ -239,7 +239,7 @@ zombified_piglin.animation = {
|
||||||
punch_start = 189,
|
punch_start = 189,
|
||||||
punch_end = 198,
|
punch_end = 198,
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:zombified_piglin", zombified_piglin)
|
mcl_mobs.register_mob("mobs_mc:zombified_piglin", zombified_piglin)
|
||||||
|
|
||||||
|
|
||||||
local piglin_brute = table.copy(piglin)
|
local piglin_brute = table.copy(piglin)
|
||||||
|
@ -280,7 +280,7 @@ piglin_brute.animation = {
|
||||||
}
|
}
|
||||||
piglin_brute.can_despawn = false
|
piglin_brute.can_despawn = false
|
||||||
piglin_brute.group_attack = { "mobs_mc:piglin", "mobs_mc:piglin_brute" }
|
piglin_brute.group_attack = { "mobs_mc:piglin", "mobs_mc:piglin_brute" }
|
||||||
mcl_mobs:register_mob("mobs_mc:piglin_brute", piglin_brute)
|
mcl_mobs.register_mob("mobs_mc:piglin_brute", piglin_brute)
|
||||||
|
|
||||||
|
|
||||||
-- Regular spawning in the Nether
|
-- Regular spawning in the Nether
|
||||||
|
@ -316,5 +316,5 @@ minetest.LIGHT_MAX+1,
|
||||||
mcl_vars.mg_lava_nether_max,
|
mcl_vars.mg_lava_nether_max,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:piglin", S("Piglin"), "#7b4a17","#d5c381", 0)
|
mcl_mobs.register_egg("mobs_mc:piglin", S("Piglin"), "#7b4a17","#d5c381", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:piglin_brute", S("Piglin Brute"), "#562b0c","#ddc89d", 0)
|
mcl_mobs.register_egg("mobs_mc:piglin_brute", S("Piglin Brute"), "#562b0c","#ddc89d", 0)
|
||||||
|
|
|
@ -12,7 +12,7 @@ end
|
||||||
local function reset_animation(self, animation)
|
local function reset_animation(self, animation)
|
||||||
if not self.object:get_pos() or self._current_animation ~= animation then return end
|
if not self.object:get_pos() or self._current_animation ~= animation then return end
|
||||||
self._current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
self._current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
||||||
mcl_mobs:set_animation(self, animation)
|
self:set_animation(animation)
|
||||||
end
|
end
|
||||||
|
|
||||||
pillager = {
|
pillager = {
|
||||||
|
@ -96,10 +96,10 @@ pillager = {
|
||||||
self.object:set_properties(props)
|
self.object:set_properties(props)
|
||||||
local old_anim = self._current_animation
|
local old_anim = self._current_animation
|
||||||
if old_anim == "run" or old_anim == "walk" then
|
if old_anim == "run" or old_anim == "walk" then
|
||||||
mcl_mobs:set_animation(self, "reload_run")
|
self:set_animation("reload_run")
|
||||||
end
|
end
|
||||||
if old_anim == "stand" then
|
if old_anim == "stand" then
|
||||||
mcl_mobs:set_animation(self, "reload_stand")
|
self:set_animation("reload_stand")
|
||||||
end
|
end
|
||||||
self._current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
self._current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
||||||
minetest.after(1, reload, self)
|
minetest.after(1, reload, self)
|
||||||
|
@ -120,5 +120,5 @@ pillager = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:pillager", pillager)
|
mcl_mobs.register_mob("mobs_mc:pillager", pillager)
|
||||||
mcl_mobs:register_egg("mobs_mc:pillager", S("Pillager"), "#532f36", "#959b9b", 0)
|
mcl_mobs.register_egg("mobs_mc:pillager", S("Pillager"), "#532f36", "#959b9b", 0)
|
||||||
|
|
|
@ -7,7 +7,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:polar_bear", {
|
mcl_mobs.register_mob("mobs_mc:polar_bear", {
|
||||||
description = S("Polar Bear"),
|
description = S("Polar Bear"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -92,4 +92,4 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:polar_bear", S("Polar Bear"), "#f2f2f2", "#959590", 0)
|
mcl_mobs.register_egg("mobs_mc:polar_bear", S("Polar Bear"), "#f2f2f2", "#959590", 0)
|
||||||
|
|
|
@ -83,7 +83,7 @@ local rabbit = {
|
||||||
},
|
},
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
-- Feed, tame protect or capture
|
-- Feed, tame protect or capture
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
if mcl_mobs:capture_mob(self, clicker, 0, 50, 80, false, nil) then return end
|
if mcl_mobs:capture_mob(self, clicker, 0, 50, 80, false, nil) then return end
|
||||||
end,
|
end,
|
||||||
|
@ -102,7 +102,7 @@ local rabbit = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:rabbit", rabbit)
|
mcl_mobs.register_mob("mobs_mc:rabbit", rabbit)
|
||||||
|
|
||||||
-- The killer bunny (Only with spawn egg)
|
-- The killer bunny (Only with spawn egg)
|
||||||
local killer_bunny = table.copy(rabbit)
|
local killer_bunny = table.copy(rabbit)
|
||||||
|
@ -128,7 +128,7 @@ killer_bunny.do_custom = function(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
mcl_mobs.register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||||
|
|
||||||
-- Mob spawning rules.
|
-- Mob spawning rules.
|
||||||
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
||||||
|
@ -213,7 +213,7 @@ mcl_mobs:spawn(spawn_grass)
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
-- Spawn egg
|
-- Spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "#995f40", "#734831", 0)
|
mcl_mobs.register_egg("mobs_mc:rabbit", S("Rabbit"), "#995f40", "#734831", 0)
|
||||||
|
|
||||||
-- Note: This spawn egg does not exist in Minecraft
|
-- Note: This spawn egg does not exist in Minecraft
|
||||||
mcl_mobs:register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "#f2f2f2", "#ff0000", 0)
|
mcl_mobs.register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "#f2f2f2", "#ff0000", 0)
|
||||||
|
|
|
@ -58,7 +58,8 @@ local salmon = {
|
||||||
runaway = true,
|
runaway = true,
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
local bn = clicker:get_wielded_item():get_name()
|
||||||
|
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
clicker:set_wielded_item("mcl_buckets:bucket_salmon")
|
clicker:set_wielded_item("mcl_buckets:bucket_salmon")
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||||
|
@ -66,7 +67,7 @@ local salmon = {
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:salmon", salmon)
|
mcl_mobs.register_mob("mobs_mc:salmon", salmon)
|
||||||
|
|
||||||
|
|
||||||
--spawning TODO: in schools
|
--spawning TODO: in schools
|
||||||
|
@ -225,4 +226,4 @@ water-16,
|
||||||
water+1)
|
water+1)
|
||||||
|
|
||||||
--spawn egg
|
--spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:salmon", S("Salmon"), "#a00f10", "#0e8474", 0)
|
mcl_mobs.register_egg("mobs_mc:salmon", S("Salmon"), "#a00f10", "#0e8474", 0)
|
||||||
|
|
|
@ -52,7 +52,7 @@ end
|
||||||
local gotten_texture = { "blank.png", "mobs_mc_sheep.png" }
|
local gotten_texture = { "blank.png", "mobs_mc_sheep.png" }
|
||||||
|
|
||||||
--mcsheep
|
--mcsheep
|
||||||
mcl_mobs:register_mob("mobs_mc:sheep", {
|
mcl_mobs.register_mob("mobs_mc:sheep", {
|
||||||
description = S("Sheep"),
|
description = S("Sheep"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -205,7 +205,7 @@ mcl_mobs:register_mob("mobs_mc:sheep", {
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
local item = clicker:get_wielded_item()
|
local item = clicker:get_wielded_item()
|
||||||
|
|
||||||
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
|
if self:feed_tame(clicker, 1, true, false) then return end
|
||||||
if mcl_mobs:protect(self, clicker) then return end
|
if mcl_mobs:protect(self, clicker) then return end
|
||||||
|
|
||||||
if item:get_name() == "mcl_tools:shears" and not self.gotten and not self.child then
|
if item:get_name() == "mcl_tools:shears" and not self.gotten and not self.child then
|
||||||
|
@ -267,7 +267,7 @@ mcl_mobs:register_mob("mobs_mc:sheep", {
|
||||||
on_breed = function(parent1, parent2)
|
on_breed = function(parent1, parent2)
|
||||||
-- Breed sheep and choose a fur color for the child.
|
-- Breed sheep and choose a fur color for the child.
|
||||||
local pos = parent1.object:get_pos()
|
local pos = parent1.object:get_pos()
|
||||||
local child = mcl_mobs:spawn_child(pos, parent1.name)
|
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||||
if child then
|
if child then
|
||||||
local ent_c = child:get_luaentity()
|
local ent_c = child:get_luaentity()
|
||||||
local color1 = parent1.color
|
local color1 = parent1.color
|
||||||
|
@ -365,4 +365,4 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:sheep", S("Sheep"), "#e7e7e7", "#ffb5b5", 0)
|
mcl_mobs.register_egg("mobs_mc:sheep", S("Sheep"), "#e7e7e7", "#ffb5b5", 0)
|
||||||
|
|
|
@ -30,7 +30,7 @@ local function check_spot(pos)
|
||||||
end
|
end
|
||||||
local pr = PseudoRandom(os.time()*(-334))
|
local pr = PseudoRandom(os.time()*(-334))
|
||||||
-- animation 45-80 is transition between passive and attack stance
|
-- animation 45-80 is transition between passive and attack stance
|
||||||
mcl_mobs:register_mob("mobs_mc:shulker", {
|
mcl_mobs.register_mob("mobs_mc:shulker", {
|
||||||
description = S("Shulker"),
|
description = S("Shulker"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -83,10 +83,10 @@ mcl_mobs:register_mob("mobs_mc:shulker", {
|
||||||
end
|
end
|
||||||
if self.state == "walk" or self.state == "stand" then
|
if self.state == "walk" or self.state == "stand" then
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
mcl_mobs:set_animation(self, "stand")
|
self:set_animation("stand")
|
||||||
end
|
end
|
||||||
if self.state == "attack" then
|
if self.state == "attack" then
|
||||||
mcl_mobs:set_animation(self, "punch")
|
self:set_animation("punch")
|
||||||
end
|
end
|
||||||
self.path.way = false
|
self.path.way = false
|
||||||
self.look_at_players = false
|
self.look_at_players = false
|
||||||
|
@ -134,7 +134,7 @@ mcl_mobs:register_mob("mobs_mc:shulker", {
|
||||||
for n=1, math.min(8, #nodes) do
|
for n=1, math.min(8, #nodes) do
|
||||||
local r = pr:next(1, #nodes)
|
local r = pr:next(1, #nodes)
|
||||||
local nodepos = nodes[r]
|
local nodepos = nodes[r]
|
||||||
local tg = vector.offset(nodepos,0,1,0)
|
local tg = vector.offset(nodepos,0,0.5,0)
|
||||||
if check_spot(tg) then
|
if check_spot(tg) then
|
||||||
self.object:set_pos(tg)
|
self.object:set_pos(tg)
|
||||||
node_ok = true
|
node_ok = true
|
||||||
|
@ -152,7 +152,7 @@ mcl_mobs:register_mob("mobs_mc:shulker", {
|
||||||
})
|
})
|
||||||
|
|
||||||
-- bullet arrow (weapon)
|
-- bullet arrow (weapon)
|
||||||
mcl_mobs:register_arrow("mobs_mc:shulkerbullet", {
|
mcl_mobs.register_arrow("mobs_mc:shulkerbullet", {
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
visual_size = {x = 0.25, y = 0.25},
|
visual_size = {x = 0.25, y = 0.25},
|
||||||
textures = {"mobs_mc_shulkerbullet.png"},
|
textures = {"mobs_mc_shulkerbullet.png"},
|
||||||
|
@ -177,7 +177,7 @@ mcl_mobs:register_arrow("mobs_mc:shulkerbullet", {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:shulker", S("Shulker"), "#946694", "#4d3852", 0)
|
mcl_mobs.register_egg("mobs_mc:shulker", S("Shulker"), "#946694", "#4d3852", 0)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:silverfish", {
|
mcl_mobs.register_mob("mobs_mc:silverfish", {
|
||||||
description = S("Silverfish"),
|
description = S("Silverfish"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -23,7 +23,7 @@ mcl_mobs:register_mob("mobs_mc:silverfish", {
|
||||||
{"mobs_mc_silverfish.png"},
|
{"mobs_mc_silverfish.png"},
|
||||||
},
|
},
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
visual_size = {x=3, y=3},
|
visual_size = {x = 3, y = 3},
|
||||||
sounds = {
|
sounds = {
|
||||||
random = "mobs_mc_silverfish_idle",
|
random = "mobs_mc_silverfish_idle",
|
||||||
death = "mobs_mc_silverfish_death",
|
death = "mobs_mc_silverfish_death",
|
||||||
|
@ -45,96 +45,14 @@ mcl_mobs:register_mob("mobs_mc:silverfish", {
|
||||||
},
|
},
|
||||||
replace_rate = 2,
|
replace_rate = 2,
|
||||||
animation = {
|
animation = {
|
||||||
speed_normal = 25, speed_run = 50,
|
speed_normal = 25, speed_run = 50,
|
||||||
stand_start = 0, stand_end = 20,
|
stand_start = 0, stand_end = 20,
|
||||||
walk_start = 0, walk_end = 20,
|
walk_start = 0, walk_end = 20,
|
||||||
run_start = 0, run_end = 20,
|
run_start = 0, run_end = 20,
|
||||||
},
|
},
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
damage = 1,
|
damage = 1,
|
||||||
reach = 1,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:silverfish", S("Silverfish"), "#6d6d6d", "#313131", 0)
|
mcl_mobs.register_egg("mobs_mc:silverfish", S("Silverfish"), "#6d6d6d", "#313131", 0)
|
||||||
|
|
||||||
-- Monster egg blocks (Minetest Game)
|
|
||||||
if minetest.get_modpath("default") and mobs_mc.create_monster_egg_nodes then
|
|
||||||
local spawn_silverfish = function(pos, oldnode, oldmetadata, digger)
|
|
||||||
if not minetest.is_creative_enabled(digger:get_player_name()) then
|
|
||||||
minetest.add_entity(pos, "mobs_mc:silverfish")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
minetest.register_node("mobs_mc:monster_egg_stone", {
|
|
||||||
description = "Stone Monster Egg",
|
|
||||||
tiles = {"default_stone.png"},
|
|
||||||
groups = {oddly_breakable_by_hand = 2, spawns_silverfish = 1},
|
|
||||||
drop = '',
|
|
||||||
is_ground_content = true,
|
|
||||||
sounds = default.node_sound_stone_defaults(),
|
|
||||||
after_dig_node = spawn_silverfish,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_node("mobs_mc:monster_egg_cobble", {
|
|
||||||
description = "Cobblestone Monster Egg",
|
|
||||||
tiles = {"default_cobble.png"},
|
|
||||||
is_ground_content = false,
|
|
||||||
groups = {oddly_breakable_by_hand = 2, spawns_silverfish = 1},
|
|
||||||
drop = '',
|
|
||||||
sounds = default.node_sound_stone_defaults(),
|
|
||||||
after_dig_node = spawn_silverfish,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_node("mobs_mc:monster_egg_mossycobble", {
|
|
||||||
description = "Mossy Cobblestone Monster Egg",
|
|
||||||
tiles = {"default_mossycobble.png"},
|
|
||||||
is_ground_content = false,
|
|
||||||
groups = {oddly_breakable_by_hand = 2, spawns_silverfish = 1},
|
|
||||||
drop = '',
|
|
||||||
sounds = default.node_sound_stone_defaults(),
|
|
||||||
after_dig_node = spawn_silverfish,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_node("mobs_mc:monster_egg_stonebrick", {
|
|
||||||
description = "Stone Brick Monster Egg",
|
|
||||||
paramtype2 = "facedir",
|
|
||||||
place_param2 = 0,
|
|
||||||
tiles = {"default_stone_brick.png"},
|
|
||||||
is_ground_content = false,
|
|
||||||
groups = {oddly_breakable_by_hand = 2, spawns_silverfish = 1},
|
|
||||||
drop = '',
|
|
||||||
sounds = default.node_sound_stone_defaults(),
|
|
||||||
after_dig_node = spawn_silverfish,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_node("mobs_mc:monster_egg_stone_block", {
|
|
||||||
description = "Stone Block Monster Egg",
|
|
||||||
tiles = {"default_stone_block.png"},
|
|
||||||
is_ground_content = false,
|
|
||||||
groups = {oddly_breakable_by_hand = 2, spawns_silverfish = 1},
|
|
||||||
drop = '',
|
|
||||||
sounds = default.node_sound_stone_defaults(),
|
|
||||||
after_dig_node = spawn_silverfish,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Randomly spawn stone monster eggs in the world
|
|
||||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
|
||||||
local scarcity
|
|
||||||
if mg_name == "v6" then
|
|
||||||
scarcity = 28 * 28 * 28
|
|
||||||
else
|
|
||||||
scarcity = 22 * 22 * 22
|
|
||||||
end
|
|
||||||
minetest.register_ore({
|
|
||||||
ore_type = "scatter",
|
|
||||||
ore = "mobs_mc:monster_egg_stone",
|
|
||||||
wherein = "default:stone",
|
|
||||||
clust_scarcity = scarcity,
|
|
||||||
clust_num_ores = 3,
|
|
||||||
clust_size = 2,
|
|
||||||
y_min = -31000,
|
|
||||||
y_max = 31000,
|
|
||||||
biomes = { "grassland" },
|
|
||||||
})
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ local skeleton = {
|
||||||
curiosity = 6,
|
curiosity = 6,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_skeleton.b3d",
|
mesh = "mobs_mc_skeleton.b3d",
|
||||||
|
shooter_avoid_enemy = true,
|
||||||
|
strafes = true,
|
||||||
textures = { {
|
textures = { {
|
||||||
"mcl_bows_bow_0.png", -- bow
|
"mcl_bows_bow_0.png", -- bow
|
||||||
"mobs_mc_skeleton.png", -- skeleton
|
"mobs_mc_skeleton.png", -- skeleton
|
||||||
|
@ -127,7 +129,7 @@ local skeleton = {
|
||||||
harmed_by_heal = true,
|
harmed_by_heal = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:skeleton", skeleton)
|
mcl_mobs.register_mob("mobs_mc:skeleton", skeleton)
|
||||||
|
|
||||||
|
|
||||||
--###################
|
--###################
|
||||||
|
@ -164,7 +166,7 @@ table.insert(stray.drops, {
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:stray", stray)
|
mcl_mobs.register_mob("mobs_mc:stray", stray)
|
||||||
|
|
||||||
-- Overworld spawn
|
-- Overworld spawn
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
@ -354,6 +356,6 @@ mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:skeleton", S("Skeleton"), "#c1c1c1", "#494949", 0)
|
mcl_mobs.register_egg("mobs_mc:skeleton", S("Skeleton"), "#c1c1c1", "#494949", 0)
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:stray", S("Stray"), "#5f7476", "#dae8e7", 0)
|
mcl_mobs.register_egg("mobs_mc:stray", S("Stray"), "#5f7476", "#dae8e7", 0)
|
||||||
|
|
|
@ -9,7 +9,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
--################### WITHER SKELETON
|
--################### WITHER SKELETON
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:witherskeleton", {
|
mcl_mobs.register_mob("mobs_mc:witherskeleton", {
|
||||||
description = S("Wither Skeleton"),
|
description = S("Wither Skeleton"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -99,6 +99,7 @@ mcl_mobs:register_mob("mobs_mc:witherskeleton", {
|
||||||
})
|
})
|
||||||
|
|
||||||
--spawn
|
--spawn
|
||||||
|
--[[]
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
"mobs_mc:witherskeleton",
|
"mobs_mc:witherskeleton",
|
||||||
"nether",
|
"nether",
|
||||||
|
@ -114,6 +115,6 @@ mcl_mobs:spawn_specific(
|
||||||
5,
|
5,
|
||||||
mcl_vars.mg_nether_min,
|
mcl_vars.mg_nether_min,
|
||||||
mcl_vars.mg_nether_max)
|
mcl_vars.mg_nether_max)
|
||||||
|
--]]
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "#141414", "#474d4d", 0)
|
mcl_mobs.register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "#141414", "#474d4d", 0)
|
||||||
|
|
|
@ -109,7 +109,7 @@ local slime_big = {
|
||||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
|
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
|
||||||
use_texture_alpha = true,
|
use_texture_alpha = true,
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:slime_big", slime_big)
|
mcl_mobs.register_mob("mobs_mc:slime_big", slime_big)
|
||||||
|
|
||||||
local slime_small = table.copy(slime_big)
|
local slime_small = table.copy(slime_big)
|
||||||
slime_small.sounds.base_pitch = 1.15
|
slime_small.sounds.base_pitch = 1.15
|
||||||
|
@ -126,7 +126,7 @@ slime_small.run_velocity = 1.3
|
||||||
slime_small.jump_height = 4.3
|
slime_small.jump_height = 4.3
|
||||||
slime_small.spawn_small_alternative = "mobs_mc:slime_tiny"
|
slime_small.spawn_small_alternative = "mobs_mc:slime_tiny"
|
||||||
slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 4, 0.6, 1.0)
|
slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 4, 0.6, 1.0)
|
||||||
mcl_mobs:register_mob("mobs_mc:slime_small", slime_small)
|
mcl_mobs.register_mob("mobs_mc:slime_small", slime_small)
|
||||||
|
|
||||||
local slime_tiny = table.copy(slime_big)
|
local slime_tiny = table.copy(slime_big)
|
||||||
slime_tiny.sounds.base_pitch = 1.3
|
slime_tiny.sounds.base_pitch = 1.3
|
||||||
|
@ -151,7 +151,7 @@ slime_tiny.jump_height = 3
|
||||||
slime_tiny.spawn_small_alternative = nil
|
slime_tiny.spawn_small_alternative = nil
|
||||||
slime_tiny.on_die = nil
|
slime_tiny.on_die = nil
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:slime_tiny", slime_tiny)
|
mcl_mobs.register_mob("mobs_mc:slime_tiny", slime_tiny)
|
||||||
|
|
||||||
local smin = mcl_vars.mg_overworld_min
|
local smin = mcl_vars.mg_overworld_min
|
||||||
local smax = mobs_mc.water_level - 23
|
local smax = mobs_mc.water_level - 23
|
||||||
|
@ -348,7 +348,7 @@ local magma_cube_big = {
|
||||||
on_die = spawn_children_on_die("mobs_mc:magma_cube_small", 3, 0.8, 1.5),
|
on_die = spawn_children_on_die("mobs_mc:magma_cube_small", 3, 0.8, 1.5),
|
||||||
fire_resistant = true,
|
fire_resistant = true,
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:magma_cube_big", magma_cube_big)
|
mcl_mobs.register_mob("mobs_mc:magma_cube_big", magma_cube_big)
|
||||||
|
|
||||||
local magma_cube_small = table.copy(magma_cube_big)
|
local magma_cube_small = table.copy(magma_cube_big)
|
||||||
magma_cube_small.sounds.jump = "mobs_mc_magma_cube_small"
|
magma_cube_small.sounds.jump = "mobs_mc_magma_cube_small"
|
||||||
|
@ -369,7 +369,7 @@ magma_cube_small.reach = 2.75
|
||||||
magma_cube_small.armor = 66
|
magma_cube_small.armor = 66
|
||||||
magma_cube_small.spawn_small_alternative = "mobs_mc:magma_cube_tiny"
|
magma_cube_small.spawn_small_alternative = "mobs_mc:magma_cube_tiny"
|
||||||
magma_cube_small.on_die = spawn_children_on_die("mobs_mc:magma_cube_tiny", 4, 0.6, 1.0)
|
magma_cube_small.on_die = spawn_children_on_die("mobs_mc:magma_cube_tiny", 4, 0.6, 1.0)
|
||||||
mcl_mobs:register_mob("mobs_mc:magma_cube_small", magma_cube_small)
|
mcl_mobs.register_mob("mobs_mc:magma_cube_small", magma_cube_small)
|
||||||
|
|
||||||
local magma_cube_tiny = table.copy(magma_cube_big)
|
local magma_cube_tiny = table.copy(magma_cube_big)
|
||||||
magma_cube_tiny.sounds.jump = "mobs_mc_magma_cube_small"
|
magma_cube_tiny.sounds.jump = "mobs_mc_magma_cube_small"
|
||||||
|
@ -391,7 +391,7 @@ magma_cube_tiny.drops = {}
|
||||||
magma_cube_tiny.spawn_small_alternative = nil
|
magma_cube_tiny.spawn_small_alternative = nil
|
||||||
magma_cube_tiny.on_die = nil
|
magma_cube_tiny.on_die = nil
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:magma_cube_tiny", magma_cube_tiny)
|
mcl_mobs.register_mob("mobs_mc:magma_cube_tiny", magma_cube_tiny)
|
||||||
|
|
||||||
|
|
||||||
local mmin = mcl_vars.mg_nether_min
|
local mmin = mcl_vars.mg_nether_min
|
||||||
|
@ -447,6 +447,6 @@ mmin,
|
||||||
mmax)
|
mmax)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:magma_cube_big", S("Magma Cube"), "#350000", "#fcfc00")
|
mcl_mobs.register_egg("mobs_mc:magma_cube_big", S("Magma Cube"), "#350000", "#fcfc00")
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:slime_big", S("Slime"), "#52a03e", "#7ebf6d")
|
mcl_mobs.register_egg("mobs_mc:slime_big", S("Slime"), "#52a03e", "#7ebf6d")
|
||||||
|
|
|
@ -20,7 +20,7 @@ local gotten_texture = {
|
||||||
"blank.png",
|
"blank.png",
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:snowman", {
|
mcl_mobs.register_mob("mobs_mc:snowman", {
|
||||||
description = S("Snow Golem"),
|
description = S("Snow Golem"),
|
||||||
type = "npc",
|
type = "npc",
|
||||||
spawn_class = "passive",
|
spawn_class = "passive",
|
||||||
|
@ -196,4 +196,4 @@ function mobs_mc.check_snow_golem_summon(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawn egg
|
-- Spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:snowman", S("Snow Golem"), "#f2f2f2", "#fd8f47", 0)
|
mcl_mobs.register_egg("mobs_mc:snowman", S("Snow Golem"), "#f2f2f2", "#fd8f47", 0)
|
||||||
|
|
|
@ -12,6 +12,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
-- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
-- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
||||||
minetest.register_entity("mobs_mc:spider_eyes", {
|
minetest.register_entity("mobs_mc:spider_eyes", {
|
||||||
|
pointable = false,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_spider.b3d",
|
mesh = "mobs_mc_spider.b3d",
|
||||||
visual_size = {x=1.01/3, y=1.01/3},
|
visual_size = {x=1.01/3, y=1.01/3},
|
||||||
|
@ -110,7 +111,7 @@ local spider = {
|
||||||
run_end = 20,
|
run_end = 20,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mcl_mobs:register_mob("mobs_mc:spider", spider)
|
mcl_mobs.register_mob("mobs_mc:spider", spider)
|
||||||
|
|
||||||
-- Cave spider
|
-- Cave spider
|
||||||
local cave_spider = table.copy(spider)
|
local cave_spider = table.copy(spider)
|
||||||
|
@ -121,13 +122,26 @@ cave_spider.textures = { {"mobs_mc_cave_spider.png^(mobs_mc_spider_eyes.png^[mak
|
||||||
cave_spider.damage = 3 -- damage increased to undo non-existing poison
|
cave_spider.damage = 3 -- damage increased to undo non-existing poison
|
||||||
cave_spider.hp_min = 1
|
cave_spider.hp_min = 1
|
||||||
cave_spider.hp_max = 12
|
cave_spider.hp_max = 12
|
||||||
cave_spider.collisionbox = {-0.35, -0.01, -0.35, 0.35, 0.49, 0.35}
|
cave_spider.collisionbox = {-0.35, -0.01, -0.35, 0.35, 0.46, 0.35}
|
||||||
cave_spider.visual_size = {x=1.66666, y=1.5}
|
cave_spider.visual_size = {x=0.55,y=0.5}
|
||||||
|
cave_spider.on_spawn = function(self)
|
||||||
|
self.object:set_properties({visual_size={x=0.55,y=0.5}})
|
||||||
|
local spider_eyes=false
|
||||||
|
for n = 1, #self.object:get_children() do
|
||||||
|
local obj = self.object:get_children()[n]
|
||||||
|
if obj:get_luaentity() and self.object:get_luaentity().name == "mobs_mc:spider_eyes" then
|
||||||
|
spider_eyes = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not spider_eyes then
|
||||||
|
minetest.add_entity(self.object:get_pos(), "mobs_mc:spider_eyes"):set_attach(self.object, "body.head", vector.new(0,-0.98,2), vector.new(90,180,180))
|
||||||
|
end
|
||||||
|
end
|
||||||
cave_spider.walk_velocity = 1.3
|
cave_spider.walk_velocity = 1.3
|
||||||
cave_spider.run_velocity = 3.2
|
cave_spider.run_velocity = 3.2
|
||||||
cave_spider.sounds = table.copy(spider.sounds)
|
cave_spider.sounds = table.copy(spider.sounds)
|
||||||
cave_spider.sounds.base_pitch = 1.25
|
cave_spider.sounds.base_pitch = 1.25
|
||||||
mcl_mobs:register_mob("mobs_mc:cave_spider", cave_spider)
|
mcl_mobs.register_mob("mobs_mc:cave_spider", cave_spider)
|
||||||
|
|
||||||
|
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
@ -279,5 +293,5 @@ mcl_vars.mg_overworld_min,
|
||||||
mcl_vars.mg_overworld_max)
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:spider", S("Spider"), "#342d26", "#a80e0e", 0)
|
mcl_mobs.register_egg("mobs_mc:spider", S("Spider"), "#342d26", "#a80e0e", 0)
|
||||||
mcl_mobs:register_egg("mobs_mc:cave_spider", S("Cave Spider"), "#0c424e", "#a80e0e", 0)
|
mcl_mobs.register_egg("mobs_mc:cave_spider", S("Cave Spider"), "#0c424e", "#a80e0e", 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:squid", {
|
mcl_mobs.register_mob("mobs_mc:squid", {
|
||||||
description = S("Squid"),
|
description = S("Squid"),
|
||||||
type = "animal",
|
type = "animal",
|
||||||
spawn_class = "water",
|
spawn_class = "water",
|
||||||
|
@ -217,4 +217,4 @@ water-16,
|
||||||
water+1)
|
water+1)
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:squid", S("Squid"), "#223b4d", "#708999", 0)
|
mcl_mobs.register_egg("mobs_mc:squid", S("Squid"), "#223b4d", "#708999", 0)
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
--MCmobs v0.4
|
||||||
|
--maikerumine
|
||||||
|
--made for MC like Survival game
|
||||||
|
--License for code WTFPL and otherwise stated in readmes
|
||||||
|
|
||||||
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
--###################
|
||||||
|
--################### STRIDER
|
||||||
|
--###################
|
||||||
|
|
||||||
|
|
||||||
|
local strider = {
|
||||||
|
type = "animal",
|
||||||
|
passive = true,
|
||||||
|
spawn_class = "passive",
|
||||||
|
hp_min = 20,
|
||||||
|
hp_max = 20,
|
||||||
|
xp_min = 9,
|
||||||
|
xp_max = 9,
|
||||||
|
armor = {fleshy = 90},
|
||||||
|
attack_type = "dogfight",
|
||||||
|
damage = 2,
|
||||||
|
reach = 2,
|
||||||
|
collisionbox = {-.6, -0.01, -.6, .6, 1.94, .6},
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "extra_mobs_strider.b3d",
|
||||||
|
textures = { {
|
||||||
|
"extra_mobs_strider.png",
|
||||||
|
} },
|
||||||
|
visual_size = {x=3, y=3},
|
||||||
|
sounds = {
|
||||||
|
},
|
||||||
|
jump = true,
|
||||||
|
makes_footstep_sound = true,
|
||||||
|
walk_velocity = 2,
|
||||||
|
run_velocity = 4,
|
||||||
|
runaway = true,
|
||||||
|
drops = {
|
||||||
|
{name = "mcl_mobsitems:string",
|
||||||
|
chance = 1,
|
||||||
|
min = 2,
|
||||||
|
max = 5,},
|
||||||
|
},
|
||||||
|
animation = {
|
||||||
|
stand_speed = 15,
|
||||||
|
walk_speed = 15,
|
||||||
|
run_speed = 30,
|
||||||
|
stand_start = 5,
|
||||||
|
stand_end = 5,
|
||||||
|
walk_start = 1,
|
||||||
|
walk_end = 20,
|
||||||
|
},
|
||||||
|
lava_damage = 0,
|
||||||
|
fire_damage = 0,
|
||||||
|
light_damage = 0,
|
||||||
|
water_damage = 5,
|
||||||
|
fear_height = 4,
|
||||||
|
view_range = 16,
|
||||||
|
fire_resistant = true,
|
||||||
|
floats_on_lava = 1,
|
||||||
|
floats = 0,
|
||||||
|
can_spawn = function(pos)
|
||||||
|
local l = minetest.find_node_near(pos,2,{"mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing"})
|
||||||
|
return l ~= nil
|
||||||
|
end,
|
||||||
|
do_custom = function(self, dtime)
|
||||||
|
|
||||||
|
if minetest.find_node_near(self.object:get_pos(), 2, {"mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing"}) then
|
||||||
|
self.walk_velocity = 2
|
||||||
|
self.run_velocity = 4
|
||||||
|
self.base_texture[1] = "extra_mobs_strider.png"
|
||||||
|
self.shaking = false
|
||||||
|
else
|
||||||
|
self.base_texture[1] = "extra_mobs_strider_cold.png"
|
||||||
|
self.walk_velocity = .5
|
||||||
|
self.run_velocity = 1
|
||||||
|
self.shaking = true
|
||||||
|
end
|
||||||
|
|
||||||
|
self.object:set_properties({textures=self.base_texture, shaking=self.shaking, run_velocity=self.run_velocity, walk_velocity=self.walk_velocity})
|
||||||
|
|
||||||
|
-- set needed values if not already present
|
||||||
|
if not self.v2 then
|
||||||
|
self.v2 = 0
|
||||||
|
self.max_speed_forward = 8
|
||||||
|
self.max_speed_reverse = 4
|
||||||
|
self.accel = 2
|
||||||
|
self.terrain_type = 3
|
||||||
|
self.driver_attach_at = {x = 0, y = 5.5, z = -1.75}
|
||||||
|
self.driver_eye_offset = {x = 0, y = 10, z = 0}
|
||||||
|
self.driver_scale = {x = 1/self.visual_size.x, y = 1/self.visual_size.y}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if driver present allow control of horse
|
||||||
|
if self.driver then
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
self.object:set_velocity(vector.new(v.x,0,v.z))
|
||||||
|
mcl_mobs.drive(self, "walk", "stand", false, dtime)
|
||||||
|
local l = minetest.find_node_near(pos,2,{"group:lava"})
|
||||||
|
if l then self.object:set_pos(vector.new(pos.x,l.y+0.5,pos.z)) end
|
||||||
|
return false -- skip rest of mob functions
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_die = function(self, pos)
|
||||||
|
|
||||||
|
-- drop saddle when horse is killed while riding
|
||||||
|
-- also detach from horse properly
|
||||||
|
if self.driver then
|
||||||
|
mcl_mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_rightclick = function(self, clicker)
|
||||||
|
if not clicker or not clicker:is_player() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local wielditem = clicker:get_wielded_item()
|
||||||
|
|
||||||
|
if wielditem:get_name() ~= "mcl_crimson:warped_fungus" then
|
||||||
|
if self:feed_tame(clicker, 1, true, true) then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.child then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local item = clicker:get_wielded_item()
|
||||||
|
if item:get_name() == "mcl_mobitems:saddle" and self.saddle ~= "yes" then
|
||||||
|
self.base_texture = {
|
||||||
|
"extra_mobs_strider.png",
|
||||||
|
"mobs_mc_pig_saddle.png", -- saddle
|
||||||
|
}
|
||||||
|
self.object:set_properties({
|
||||||
|
textures = self.base_texture
|
||||||
|
})
|
||||||
|
self.saddle = "yes"
|
||||||
|
self.tamed = true
|
||||||
|
self.drops = {
|
||||||
|
{name = "mcl_mobitems:string",
|
||||||
|
chance = 1,
|
||||||
|
min = 1,
|
||||||
|
max = 3,},
|
||||||
|
{name = "mcl_mobitems:saddle",
|
||||||
|
chance = 1,
|
||||||
|
min = 1,
|
||||||
|
max = 1,},
|
||||||
|
}
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
wielditem:take_item(1)
|
||||||
|
clicker:get_inventory():set_stack("main", clicker:get_wield_index(), wielditem)
|
||||||
|
end
|
||||||
|
minetest.sound_play({name = "mcl_armor_equip_leather"}, {gain=0.5, max_hear_distance=8, pos=self.object:get_pos()}, true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mount or detach player
|
||||||
|
local name = clicker:get_player_name()
|
||||||
|
if self.driver and clicker == self.driver then
|
||||||
|
-- Detach if already attached
|
||||||
|
mcl_mobs.detach(clicker, {x=1, y=0, z=0})
|
||||||
|
return
|
||||||
|
|
||||||
|
elseif not self.driver and self.saddle == "yes" and wielditem:get_name() == "mcl_mobitems:warped_fungus_on_a_stick" then
|
||||||
|
-- Ride pig if it has a saddle and player uses a carrot on a stick
|
||||||
|
|
||||||
|
mcl_mobs.attach(self, clicker)
|
||||||
|
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
|
||||||
|
local inv = self.driver:get_inventory()
|
||||||
|
-- 26 uses
|
||||||
|
if wielditem:get_wear() > 63000 then
|
||||||
|
-- Break carrot on a stick
|
||||||
|
local def = wielditem:get_definition()
|
||||||
|
if def.sounds and def.sounds.breaks then
|
||||||
|
minetest.sound_play(def.sounds.breaks, {pos = clicker:get_pos(), max_hear_distance = 8, gain = 0.5}, true)
|
||||||
|
end
|
||||||
|
wielditem = {name = mobs_mc.items.fishing_rod, count = 1}
|
||||||
|
else
|
||||||
|
wielditem:add_wear(2521)
|
||||||
|
end
|
||||||
|
inv:set_stack("main",self.driver:get_wield_index(), wielditem)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_mobs.register_mob("mobs_mc:strider", strider)
|
||||||
|
|
||||||
|
-- Baby strider.
|
||||||
|
|
||||||
|
local baby_strider = table.copy(strider)
|
||||||
|
baby_strider.collisionbox = {-.3, -0.01, -.3, .3, 0.94, .3}
|
||||||
|
baby_strider.xp_min = 13
|
||||||
|
baby_strider.xp_max = 13
|
||||||
|
textures = { {
|
||||||
|
"extra_mobs_strider.png",
|
||||||
|
"extra_mobs_trans.png",
|
||||||
|
} }
|
||||||
|
baby_strider.walk_velocity = 1.2
|
||||||
|
baby_strider.run_velocity = 2.4
|
||||||
|
baby_strider.child = 1
|
||||||
|
|
||||||
|
mcl_mobs.register_mob("mobs_mc:baby_strider", baby_strider)
|
||||||
|
|
||||||
|
-- Regular spawning in the Nether
|
||||||
|
|
||||||
|
mcl_mobs:spawn_setup({
|
||||||
|
name = "mobs_mc:strider",
|
||||||
|
type_of_spawning = "lava",
|
||||||
|
dimension = "nether",
|
||||||
|
biomes = {
|
||||||
|
"Nether",
|
||||||
|
"BasaltDelta",
|
||||||
|
"WarpedForest",
|
||||||
|
"CrimsonForest",
|
||||||
|
"SoulsandValley"
|
||||||
|
},
|
||||||
|
min_height = mcl_vars.mg_nether_min,
|
||||||
|
max_height = mcl_vars.mg_nether_max,
|
||||||
|
chance = 2000,
|
||||||
|
})
|
||||||
|
|
||||||
|
mcl_mobs:spawn_setup({
|
||||||
|
name = "mobs_mc:baby_strider",
|
||||||
|
type_of_spawning = "lava",
|
||||||
|
dimension = "nether",
|
||||||
|
biomes = {
|
||||||
|
"Nether",
|
||||||
|
"BasaltDelta",
|
||||||
|
"WarpedForest",
|
||||||
|
"CrimsonForest",
|
||||||
|
"SoulsandValley"
|
||||||
|
},
|
||||||
|
min_height = mcl_vars.mg_nether_min,
|
||||||
|
max_height = mcl_vars.mg_nether_max,
|
||||||
|
chance = 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- spawn eggs
|
||||||
|
mcl_mobs.register_egg("mobs_mc:strider", S("Strider"), "#000000", "#FF0000", 0)
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 8.3 KiB |
|
@ -103,16 +103,22 @@ local tropical_fish = {
|
||||||
runaway = true,
|
runaway = true,
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
on_rightclick = function(self, clicker)
|
on_rightclick = function(self, clicker)
|
||||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
local bn = clicker:get_wielded_item():get_name()
|
||||||
self.object:remove()
|
if bn == "mcl_buckets:bucket_water" or bn == "mcl_buckets:bucket_river_water" then
|
||||||
clicker:set_wielded_item("mcl_buckets:bucket_tropical_fish")
|
if clicker:set_wielded_item("mcl_buckets:bucket_tropical_fish") then
|
||||||
|
local it = clicker:get_wielded_item()
|
||||||
|
local m = it:get_meta()
|
||||||
|
m:set_string("properties",minetest.serialize(self.object:get_properties()))
|
||||||
|
clicker:set_wielded_item(it)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_spawn = set_textures,
|
on_spawn = set_textures,
|
||||||
}
|
}
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:tropical_fish", tropical_fish)
|
mcl_mobs.register_mob("mobs_mc:tropical_fish", tropical_fish)
|
||||||
|
|
||||||
local water = 0
|
local water = 0
|
||||||
mcl_mobs:spawn_specific(
|
mcl_mobs:spawn_specific(
|
||||||
|
@ -183,4 +189,4 @@ water-16,
|
||||||
water+1)
|
water+1)
|
||||||
|
|
||||||
--spawn egg
|
--spawn egg
|
||||||
mcl_mobs:register_egg("mobs_mc:tropical_fish", S("Tropical fish"), "#ef6915", "#fff9ef", 0)
|
mcl_mobs.register_egg("mobs_mc:tropical_fish", S("Tropical fish"), "#ef6915", "#fff9ef", 0)
|
||||||
|
|
|
@ -9,7 +9,7 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
--################### VEX
|
--################### VEX
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:vex", {
|
mcl_mobs.register_mob("mobs_mc:vex", {
|
||||||
description = S("Vex"),
|
description = S("Vex"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -94,4 +94,4 @@ mcl_mobs:register_mob("mobs_mc:vex", {
|
||||||
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:vex", S("Vex"), "#7a90a4", "#e8edf1", 0)
|
mcl_mobs.register_egg("mobs_mc:vex", S("Vex"), "#7a90a4", "#e8edf1", 0)
|
||||||
|
|
|
@ -11,7 +11,9 @@ local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
local pr = PseudoRandom(os.time()*666)
|
local pr = PseudoRandom(os.time()*666)
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:evoker", {
|
local spawned_vexes = {} --this is stored locally so the mobs engine doesn't try to store it in staticdata
|
||||||
|
|
||||||
|
mcl_mobs.register_mob("mobs_mc:evoker", {
|
||||||
description = S("Evoker"),
|
description = S("Evoker"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -42,16 +44,24 @@ mcl_mobs:register_mob("mobs_mc:evoker", {
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
-- Summon vexes
|
-- Summon vexes
|
||||||
custom_attack = function(self, to_attack)
|
custom_attack = function(self, to_attack)
|
||||||
local r = pr:next(2,4)
|
if not spawned_vexes[self] then spawned_vexes[self] = {} end
|
||||||
|
if #spawned_vexes[self] >= 7 then return end
|
||||||
|
for k,v in pairs(spawned_vexes[self]) do
|
||||||
|
if not v or v.health <= 0 then table.remove(spawned_vexes[self],k) end
|
||||||
|
end
|
||||||
|
local r = pr:next(1,4)
|
||||||
local basepos = self.object:get_pos()
|
local basepos = self.object:get_pos()
|
||||||
basepos.y = basepos.y + 1
|
basepos.y = basepos.y + 1
|
||||||
for i=1, r do
|
for i=1, r do
|
||||||
local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360)))
|
local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360)))
|
||||||
local vex = minetest.add_entity(spawnpos, "mobs_mc:vex")
|
local vex = minetest.add_entity(spawnpos, "mobs_mc:vex")
|
||||||
local ent = vex:get_luaentity()
|
local ent = vex:get_luaentity()
|
||||||
|
|
||||||
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
|
-- Mark vexes as summoned and start their life clock (they take damage it reaches 0)
|
||||||
ent._summoned = true
|
ent._summoned = true
|
||||||
ent._lifetimer = pr:next(33, 108)
|
ent._lifetimer = pr:next(33, 108)
|
||||||
|
|
||||||
|
table.insert(spawned_vexes[self],ent)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
shoot_interval = 15,
|
shoot_interval = 15,
|
||||||
|
@ -79,4 +89,4 @@ mcl_mobs:register_mob("mobs_mc:evoker", {
|
||||||
})
|
})
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mcl_mobs:register_egg("mobs_mc:evoker", S("Evoker"), "#959b9b", "#1e1c1a", 0)
|
mcl_mobs.register_egg("mobs_mc:evoker", S("Evoker"), "#959b9b", "#1e1c1a", 0)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
local mod_bows = minetest.get_modpath("mcl_bows") ~= nil
|
local mod_bows = minetest.get_modpath("mcl_bows") ~= nil
|
||||||
|
|
||||||
mcl_mobs:register_mob("mobs_mc:illusioner", {
|
mcl_mobs.register_mob("mobs_mc:illusioner", {
|
||||||
description = S("Illusioner"),
|
description = S("Illusioner"),
|
||||||
type = "monster",
|
type = "monster",
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
|
@ -65,4 +65,4 @@ mcl_mobs:register_mob("mobs_mc:illusioner", {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
})
|
})
|
||||||
|
|
||||||
mcl_mobs:register_egg("mobs_mc:illusioner", S("Illusioner"), "#3f5cbb", "#8a8686", 0)
|
mcl_mobs.register_egg("mobs_mc:illusioner", S("Illusioner"), "#3f5cbb", "#8a8686", 0)
|
||||||
|
|