Compare commits
878 Commits
mcl-struct
...
master
Author | SHA1 | Date |
---|---|---|
mrminer | 01f814267e | |
mrminer | 8f4c69084d | |
ancientmarinerdev | 2ad60b06ea | |
ancientmarinerdev | 245ce99223 | |
ancientmarinerdev | 1f107ec0c7 | |
ancientmarinerdev | c209537cfe | |
ancientmarinerdev | d583ccb986 | |
ancientmarinerdev | 7d803e174c | |
ancientmarinerdev | 63360bcab2 | |
ancientmarinerdev | 0b1881d1d4 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 8cf6269dda | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | da7320b14c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 66ff8ab8a9 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | a1cc24f8d3 | |
FossFanatic | f3bed9f599 | |
FossFanatic | f84bdbd777 | |
FossFanatic | 7712c45c4e | |
ancientmarinerdev | 02ee8b757d | |
PrairieWind | 5c32d1c106 | |
ancientmarinerdev | c4ea504123 | |
ancientmarinerdev | 6d60fb4160 | |
ancientmarinerdev | e4f26a4688 | |
ancientmarinerdev | af86e73280 | |
ancientmarinerdev | 32be8f9602 | |
ancientmarinerdev | 15560d969c | |
ancientmarinerdev | 6bbf3af97b | |
ancientmarinerdev | 84317afc93 | |
ancientmarinerdev | 2cd6629ae1 | |
FossFanatic | 9afdd09d9d | |
ancientmarinerdev | 9a276489d1 | |
CyberMango | e2cbd4267c | |
CyberMango | 4ec506b534 | |
CyberMango | 06435e0f4c | |
CyberMango | a1d98c080f | |
CyberMango | 96cd2657db | |
CyberMango | cd63f32cdd | |
FossFanatic | 2d81d153bd | |
FossFanatic | dc7a46df4e | |
FossFanatic | ad25b0bc4b | |
FossFanatic | b77260253a | |
ancientmarinerdev | ad9beebc70 | |
FossFanatic | b6951d00b9 | |
ancientmarinerdev | 86e480aad0 | |
ancientmarinerdev | ce7d493890 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 995679d7ae | |
CyberMango | c57f797226 | |
CyberMango | 78f32d2dd4 | |
FossFanatic | a1ad84370c | |
FossFanatic | f6a3fe2128 | |
ancientmarinerdev | 1c65e0ad68 | |
ancientmarinerdev | 6900748429 | |
ancientmarinerdev | f09b723885 | |
ancientmarinerdev | efd3420d52 | |
ancientmarinerdev | 62be5a06f6 | |
ancientmarinerdev | 683799aea5 | |
ancientmarinerdev | 383cbf96a6 | |
ancientmarinerdev | a7632e767d | |
ancientmarinerdev | 99d09c76ae | |
ancientmarinerdev | c7ebe1b8cd | |
ancientmarinerdev | 5c464f1c1d | |
ancientmarinerdev | 87e6842c00 | |
Michieal | b258ccffdc | |
ancientmarinerdev | 83b0807218 | |
Michieal | a00ef4500d | |
PrairieWind | c49aef3251 | |
ancientmarinerdev | f3ba32d739 | |
Michieal | ed03cb470d | |
FossFanatic | 9746dbc376 | |
FossFanatic | d89a7e0326 | |
FossFanatic | 5fd5cc9f1c | |
FossFanatic | c75ec6916c | |
FossFanatic | 4b6b4d8398 | |
FossFanatic | 513d148eaf | |
FossFanatic | 26e032687a | |
Michieal | b99487b6ab | |
ancientmarinerdev | 8adc1d48b2 | |
Michieal | ac30e25065 | |
Michieal | 0931af21c8 | |
Michieal | 77a8ca689f | |
ancientmarinerdev | 30d3b7ee23 | |
Michieal | 55bb9800f4 | |
Michieal | b805ae9926 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 90c74f4a6a | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 6e6c11cde2 | |
ancientmarinerdev | 8feefcdd7b | |
ancientmarinerdev | 9b28baaad6 | |
PrairieWind | 60a1a364dc | |
b3nderman | f9b021e4c6 | |
ancientmarinerdev | 65fb911e5f | |
ancientmarinerdev | 38732e801d | |
ancientmarinerdev | 5ad2a990d4 | |
ancientmarinerdev | 8f6c932e88 | |
Michieal | e9c202ae45 | |
Michieal | d6ee21d192 | |
PrairieWind | 876fc2fb50 | |
Michieal | 0c61035df0 | |
b3nderman | a7fac7c550 | |
ancientmarinerdev | 93c9fdfaae | |
AFCMS | 3027965314 | |
AFCMS | e9c9f151de | |
AFCMS | fb8262bf57 | |
AFCMS | 726358da4b | |
AFCMS | 515204d296 | |
AFCMS | eb5f5678d6 | |
AFCMS | 33e2b79b2b | |
AFCMS | 7c497d9604 | |
AFCMS | 7c20896d5e | |
AFCMS | cd70dd5b88 | |
AFCMS | 302970d220 | |
AFCMS | 79876635af | |
AFCMS | b9238b4069 | |
AFCMS | bf25fca47d | |
AFCMS | 92261e5fb5 | |
AFCMS | 1e31e383d4 | |
AFCMS | 4457432d32 | |
ancientmarinerdev | fa96f9d593 | |
FossFanatic | fbb4cf084f | |
FossFanatic | 60b19b31ab | |
FossFanatic | 871c4f24c2 | |
FossFanatic | 18c0e1f050 | |
FossFanatic | fb8e41047d | |
FossFanatic | 2bd5e6a84f | |
FossFanatic | 23a13f14e0 | |
gldrk | 5412206743 | |
gldrk | 30521a8b2f | |
gldrk | 1536338199 | |
gldrk | 3697ef8069 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | dd15092052 | |
ancientmarinerdev | ce485e89cd | |
ancientmarinerdev | 09a7348256 | |
FossFanatic | a500528613 | |
FossFanatic | 292e7129f2 | |
Michieal | 3091e85b1c | |
Michieal | da277f9dc7 | |
Michieal | 27a487195a | |
Michieal | 2fdc8cbdd4 | |
Michieal | 0079cf807e | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 899cfd5157 | |
CyberMango | 64a7f76d5b | |
CyberMango | b0d9eed3e1 | |
CyberMango | 685a7ff256 | |
CyberMango | 949a2b787e | |
FossFanatic | 8849b2e98f | |
Michieal | 9e76cd963d | |
Michieal | 522a7577de | |
Michieal | 7912239562 | |
Michieal | 396fedd8e7 | |
Michieal | f81eec6fff | |
Michieal | d2625d3ace | |
Michieal | a04d946879 | |
ancientmarinerdev | 482a6071f5 | |
Michieal | 6993559330 | |
Michieal | b572363b8e | |
Michieal | 00408b998c | |
Michieal | e7c238c17e | |
Michieal | 1de806f6f8 | |
Michieal | b07e6fccdc | |
Michieal | 04efa74115 | |
Michieal | 2e8f56d098 | |
Michieal | ff7299a444 | |
Michieal | aad2d114f9 | |
ancientmarinerdev | 2ed3c1c480 | |
ancientmarinerdev | 111c885417 | |
ancientmarinerdev | 8e1f00d428 | |
ancientmarinerdev | 4fd4425aae | |
ancientmarinerdev | ac4cd2c325 | |
ancientmarinerdev | 01c8009c6a | |
ancientmarinerdev | ef90820f67 | |
ancientmarinerdev | d6d11b9526 | |
ancientmarinerdev | b0264b2736 | |
Michieal | c12f2cc565 | |
Michieal | 9f94f12127 | |
Michieal | 784f3ec226 | |
Michieal | 44a769397d | |
PrairieWind | b3da85be64 | |
ancientmarinerdev | 3ed32abc4e | |
kay27 | 9db6050638 | |
kay27 | 7ceb953a56 | |
kay27 | 1894d8c5f0 | |
kay27 | 419d61edde | |
PrairieWind | 9820309762 | |
Michieal | 726eba7ed8 | |
Michieal | f46581905a | |
Michieal | 4f6de581dd | |
Michieal | eb8f7360a2 | |
ancientmarinerdev | db62631540 | |
ancientmarinerdev | fb30564827 | |
Michieal | 8c355db3ce | |
Michieal | eae08f3010 | |
Michieal | 4701c4d6e7 | |
Michieal | 8df0b2a48e | |
ancientmarinerdev | 66f368531d | |
PrairieWind | 7cb5c55d68 | |
Michieal | 34faca3f2c | |
Michieal | d5dda679eb | |
Michieal | c65bbcd5a5 | |
Michieal | 533f59bf27 | |
Michieal | 3074c15d14 | |
Michieal | 132178b8c7 | |
Michieal | ede21a8627 | |
Michieal | 82fae13b47 | |
Michieal | f92ea77849 | |
Michieal | 7ebf9b7ab8 | |
Michieal | 07a56165ed | |
Michieal | ee0b355f51 | |
PrairieWind | 6fd799ac42 | |
ancientmarinerdev | 01bb753549 | |
ancientmarinerdev | 59694ebc7c | |
Michieal | cc24144bc6 | |
Michieal | e1cd16c971 | |
ancientmarinerdev | 00c4ecf643 | |
ancientmarinerdev | e4db91d35c | |
ancientmarinerdev | 2527479401 | |
ancientmarinerdev | 85f7bbdb80 | |
ancientmarinerdev | 465a919f6b | |
ancientmarinerdev | 325a666c62 | |
ancientmarinerdev | e9b54e85c2 | |
ancientmarinerdev | 4324fe2489 | |
ancientmarinerdev | d6804bf4b7 | |
ancientmarinerdev | 87f04bdd9f | |
ancientmarinerdev | e3307d647b | |
ancientmarinerdev | 29cd73cb84 | |
ancientmarinerdev | 5c0a763b83 | |
ancientmarinerdev | 9b1ceebf0d | |
Michieal | ed64e7f733 | |
ancientmarinerdev | 856a60bcc2 | |
Michieal | 08f9c0074a | |
FossFanatic | 09db6017c3 | |
Michieal | c4969c13cd | |
Michieal | f70176da34 | |
Michieal | e314addeb3 | |
Michieal | df8fc65e1e | |
Michieal | 687887fe27 | |
Michieal | 65aa956d9c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | ddd004c0f0 | |
ancientmarinerdev | 609ef220ad | |
ancientmarinerdev | 32a7eb4087 | |
CyberMango | 5e969ba928 | |
CyberMango | 17e02aec3c | |
CyberMango | fb28e192e6 | |
CyberMango | 0f569fdbaa | |
CyberMango | 8a7fcfde82 | |
CyberMango | fb51067c78 | |
Michieal | 8dd4a2611e | |
Michieal | f86ee99abf | |
FossFanatic | 6cf1fa7b81 | |
ancientmarinerdev | 7ea41a2f21 | |
FossFanatic | 39bcf2f961 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 0b1916b807 | |
Michieal | d1a017f6b2 | |
Michieal | 50e50e2904 | |
Michieal | 966c914a8e | |
Michieal | 9351f72c15 | |
Michieal | ef7fb0d2e3 | |
Michieal | 5ef7d9f7a0 | |
ancientmarinerdev | 0fca1ce469 | |
Michieal | 388632cd46 | |
Michieal | dd4a0a4172 | |
ancientmarinerdev | df6d1c026a | |
Michieal | d586b3fecc | |
Michieal | a7a0c4791a | |
Michieal | 23b62c296a | |
ancientmarinerdev | f8ae702ce4 | |
ancientmarinerdev | 5faf060122 | |
ancientmarinerdev | 8c648d1fc3 | |
ancientmarinerdev | 46052e5b7e | |
iliekprogrammar | 6430fcf103 | |
iliekprogrammar | 7376b08c61 | |
iliekprogrammar | 2e28a3386b | |
iliekprogrammar | 98dac6dcd7 | |
iliekprogrammar | 3026808a71 | |
test1 | 7c7521ff15 | |
test1 | 50d8e95c16 | |
iliekprogrammar | fc74bd5cfe | |
kabou | 2a37d38f6c | |
kabou | 89821a8329 | |
iliekprogrammar | 3249c13752 | |
iliekprogrammar | 2ea72ccda8 | |
iliekprogrammar | 5237eca31e | |
iliekprogrammar | c2ba70a601 | |
iliekprogrammar | 723fe9c532 | |
iliekprogrammar | 661afed46c | |
FossFanatic | 3d2955c394 | |
ancientmarinerdev | e7ace0c87c | |
FossFanatic | 9605e2ae0d | |
FossFanatic | 80bb954814 | |
Michieal | 10dc83b2a6 | |
FossFanatic | b95c623a74 | |
FossFanatic | 0ab4553648 | |
FossFanatic | 65557152fb | |
FossFanatic | 1357193a78 | |
FossFanatic | aacbaddfe0 | |
FossFanatic | e246bf7ddf | |
FossFanatic | 11bd29ed44 | |
FossFanatic | de574729a2 | |
FossFanatic | 95e269a61f | |
FossFanatic | 5bdc145872 | |
FossFanatic | 20e178d5d1 | |
FossFanatic | bdbc484c48 | |
FossFanatic | 56c62e9fba | |
FossFanatic | 3b1acc60db | |
FossFanatic | 6026687e93 | |
FossFanatic | e813c48ed5 | |
FossFanatic | 75716d97e3 | |
ancientmarinerdev | 6fe5bf8674 | |
ancientmarinerdev | 9574ba7f09 | |
ancientmarinerdev | 0df4f9518a | |
ancientmarinerdev | 3ee4a62107 | |
ancientmarinerdev | e6e898671a | |
Michieal | 87327abfb9 | |
Michieal | 34263c2ef3 | |
Michieal | 38c6969292 | |
FossFanatic | 54548ecfdf | |
FossFanatic | 5ec7b8ed89 | |
FossFanatic | 137179ac8e | |
FossFanatic | 3afb42b2f7 | |
FossFanatic | ab1cfde34d | |
FossFanatic | d5f8129ea7 | |
Michieal | 1d0968f4f5 | |
Michieal | 0f9d263e02 | |
Michieal | 26c1be3722 | |
ancientmarinerdev | 1295b2dc12 | |
kabou | 44f724f677 | |
kabou | 92ee402971 | |
kabou | 7d8fdc6ec3 | |
kabou | d2edc75407 | |
kabou | 7311071303 | |
ancientmarinerdev | 437842134e | |
kabou | 60b0cfe89a | |
kabou | d8a883e81f | |
kabou | 15f5c3c30a | |
kabou | 4af8148850 | |
kabou | ea6201d32f | |
kabou | da17ff8ea0 | |
kabou | 6756ee340c | |
kabou | 0c454a34c9 | |
kabou | 8279dcb3dd | |
kabou | ea19f02e14 | |
kabou | 95cbac78a8 | |
Michieal | acc2be7253 | |
Michieal | 3015bfb8c4 | |
Michieal | 28cb06421d | |
Michieal | 731468cf5a | |
Michieal | ff7693937a | |
FossFanatic | c1cde073c8 | |
Michieal | 76669e1f3c | |
FossFanatic | ef15477e37 | |
FossFanatic | a0c5a04d26 | |
FossFanatic | 18e932997d | |
FossFanatic | 1f601c68c5 | |
FossFanatic | 9cf6c9ed5e | |
ancientmarinerdev | 81daec14a2 | |
FossFanatic | 9f9cc3a629 | |
FossFanatic | ac90350257 | |
FossFanatic | a6841fe38b | |
kabou | 5e129985f2 | |
Michieal | 6f05992c8b | |
PrairieWind | 0dc31611ac | |
PrairieWind | b0070a3b83 | |
3raven | 5964f25fa7 | |
FossFanatic | 2db9ea6459 | |
FossFanatic | e0a9aca469 | |
FossFanatic | fb8f59e918 | |
FossFanatic | 9eb86f011b | |
3raven | d3e8e24f42 | |
FossFanatic | 770dd0ec47 | |
FossFanatic | 00b2afecad | |
Michieal | c55332bf42 | |
ancientmarinerdev | cbf090485c | |
Michieal | 42f8fbf9b8 | |
CyberMango | bd38827f60 | |
Michieal | b0978d275d | |
FossFanatic | 522bbbbe26 | |
FossFanatic | e1f26ea70d | |
FossFanatic | 9ef4a850b1 | |
FossFanatic | 4c23601d03 | |
FossFanatic | 8d51e16b8e | |
Michieal | 2b11b5c17a | |
Michieal | 9445b02dcc | |
SakuraRiu | 1b980849db | |
Gregor Parzefall | 14adcbbecb | |
Michieal | e0d48400a7 | |
Gregor Parzefall | 7312c30f8e | |
Michieal | 742a625c42 | |
Michieal | 95d3f9be07 | |
Michieal | 16d94a88db | |
FossFanatic | a50908e1eb | |
FossFanatic | afbb8e2288 | |
FossFanatic | 79b1db4b1a | |
FossFanatic | 1c752019f0 | |
FossFanatic | 1977dbf9a1 | |
FossFanatic | 91836f3a1d | |
FossFanatic | 35b2f3b638 | |
Gregor Parzefall | 4fcbe05be5 | |
Riu Sakura | 8f80c6e6a6 | |
Michieal | b14f055154 | |
Riu Sakura | 3faa930bdc | |
ancientmarinerdev | a6e0da0e07 | |
Michieal | 41ab80183e | |
ancientmarinerdev | 1c24adb714 | |
Michieal | 7003459b8d | |
Michieal | 8d440f8464 | |
ancientmarinerdev | 658a08181c | |
Gregor Parzefall | 605e4de455 | |
Gregor Parzefall | 8522b87444 | |
Gregor Parzefall | e4abbbf112 | |
Gregor Parzefall | 718cdb553d | |
Gregor Parzefall | e2ec660f25 | |
Wbjitscool | 1b4bcda9da | |
Michieal | 39714fb096 | |
Wbjitscool | 258a1534d2 | |
Michieal | c995c8418c | |
Wbjitscool | 00121d9d97 | |
Michieal | 4f90fbcedd | |
Michieal | c7104a6f72 | |
Wbjitscool | 223f6640d2 | |
Michieal | feea815604 | |
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 | |
Wbjitscool | 40f7215368 | |
Wbjitscool | 620cc97bad | |
Michieal | 64c8afc8f0 | |
Michieal | f737df79e5 | |
Michieal | 7a3f373425 | |
Michieal | 63ea851e63 | |
Michieal | fe26f117da | |
Michieal | 5191326fc7 | |
ancientmarinerdev | 45c85841e8 | |
Wbjitscool | 4b8493f1a4 | |
PrairieWind | 0a2f57476e | |
PrairieWind | 256655df83 | |
Wbjitscool | b3a8ba57e7 | |
ancientmarinerdev | 20ca34233c | |
ancientmarinerdev | 93814880d0 | |
Michieal | 1861d7d30d | |
Wbjitscool | 49fb36bac2 | |
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 | |
Michieal | 773c760727 | |
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 | |
AFCMS | 96e83e866c | |
AFCMS | 3bb86fd436 | |
cora | 033b051689 | |
anarquimico | 507f14c317 | |
AFCMS | 4acee8e64e | |
TheOnlyJoeEnderman | 6a9387b752 | |
TheOnlyJoeEnderman | cbb7a8edb0 | |
TheOnlyJoeEnderman | 012fe4b37e | |
TheOnlyJoeEnderman | 3110f97868 | |
TheOnlyJoeEnderman | dff028595c | |
TheOnlyJoeEnderman | 6e328153d4 | |
TheOnlyJoeEnderman | 4695707188 | |
TheOnlyJoeEnderman | 432e196794 | |
TheOnlyJoeEnderman | a592d69990 | |
TheOnlyJoeEnderman | 310237be33 | |
TheOnlyJoeEnderman | cf6d04c716 | |
TheOnlyJoeEnderman | c22997db09 | |
TheOnlyJoeEnderman | 8e74209c9b | |
TheOnlyJoeEnderman | faa7ca1f68 | |
TheOnlyJoeEnderman | 7e3d5f3553 | |
TheOnlyJoeEnderman | 126de74904 | |
TheOnlyJoeEnderman | fbc57192a0 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 963547a0ca | |
TheOnlyJoeEnderman | d5ca4fca9b | |
TheOnlyJoeEnderman | 3bdd1ea160 | |
TheOnlyJoeEnderman | 5cc835df93 | |
TheOnlyJoeEnderman | 88d02da047 | |
TheOnlyJoeEnderman | 7aafb67923 | |
TheOnlyJoeEnderman | e7bcc89e65 | |
TheOnlyJoeEnderman | 1e003a2571 | |
TheOnlyJoeEnderman | 3001832b8f | |
TheOnlyJoeEnderman | e2107601f4 | |
TheOnlyJoeEnderman | 0624d9d169 | |
TheOnlyJoeEnderman | 2d7d577f31 | |
TheOnlyJoeEnderman | 0fc09c6e89 | |
TheOnlyJoeEnderman | 35ac5c75f5 | |
TheOnlyJoeEnderman | b1355513dd | |
TheOnlyJoeEnderman | 77bf8e0fb0 | |
TheOnlyJoeEnderman | 92d4e4f1e1 | |
TheOnlyJoeEnderman | ca0786f549 | |
TheOnlyJoeEnderman | 7f291aa109 | |
TheOnlyJoeEnderman | 4683384da1 | |
TheOnlyJoeEnderman | d34710be85 | |
TheOnlyJoeEnderman | d324197609 | |
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 | |
AFCMS | bacec2c7e6 | |
AFCMS | a3d459f020 | |
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
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: "Bug report"
|
||||||
|
about: "File a bug report"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- unconfirmed
|
||||||
|
- bug
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
|
Please follow our contributing guidelines first:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
What version of MineClone2 are you using? We do not provide support for outdated versions of MineClone2.
|
||||||
|
Current latest version is listed here, at the top:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/tags
|
||||||
|
-->
|
||||||
|
MineClone2 version:
|
||||||
|
|
||||||
|
### What happened?
|
||||||
|
Report about the bug! Please send large log snippets as an attachement file.
|
||||||
|
|
||||||
|
### What should happen:
|
||||||
|
Tell us what should happen!
|
||||||
|
|
||||||
|
### Steps to reproduce
|
||||||
|
Tell us how we can reproduce the bug!
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: "Feature request"
|
||||||
|
about: "File a feature request not in Minecraft"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- "non-Minecraft feature"
|
||||||
|
- "needs discussion"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Got a new non-Minecraft feature request? Explain to us why we should consider your idea.
|
||||||
|
|
||||||
|
Please follow our contributing guidelines first:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
Tell us about your requested feature not in Minecraft!
|
||||||
|
|
||||||
|
### Why
|
||||||
|
Tell us why should we implement it!
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: "Missing Feature request"
|
||||||
|
about: "File a missing feature request in Minecraft but not in MineClone2"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
- "missing feature"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thanks for taking the time to fill out this missing feature request!
|
||||||
|
|
||||||
|
Please follow our contributing guidelines first:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
|
||||||
|
|
||||||
|
By submitting this issue, you agree to follow our Code of Conduct:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Current feature in Minecraft
|
||||||
|
Tell us about the feature currently in Minecraft! What is it like on Minecraft?
|
||||||
|
|
||||||
|
### Current feature in MineClone2
|
||||||
|
Tell us about the feature currently in MineClone2! What is different?
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: "Pull request"
|
||||||
|
about: "Submit a pull request"
|
||||||
|
labels:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please follow our contributing guidelines first:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#how-you-can-help-as-a-programmer
|
||||||
|
|
||||||
|
By submitting this pull request, you agree to follow our Code of Conduct:
|
||||||
|
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
|
||||||
|
-->
|
||||||
|
|
||||||
|
Tell us about your pull request! Reference related issues, if necessary
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
Tell us how to test your changes!
|
|
@ -3,3 +3,5 @@
|
||||||
*.blend1
|
*.blend1
|
||||||
*.blend2
|
*.blend2
|
||||||
*.blend3
|
*.blend3
|
||||||
|
/.idea/
|
||||||
|
*.xcf
|
|
@ -60,7 +60,7 @@ representative at an online or offline event.
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement at
|
reported to the community leaders responsible for enforcement at
|
||||||
eliasfleckenstein@web.de.
|
ancientmariner_dev@proton.me.
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
|
20
CREDITS.md
|
@ -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,8 @@
|
||||||
* SumianVoice
|
* SumianVoice
|
||||||
* MrRar
|
* MrRar
|
||||||
* talamh
|
* talamh
|
||||||
|
* Faerraven / Michieal
|
||||||
|
* FossFanatic
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
* Laurent Rocher
|
* Laurent Rocher
|
||||||
|
@ -89,6 +92,13 @@
|
||||||
* FossFanatic
|
* FossFanatic
|
||||||
* Herbert West
|
* Herbert West
|
||||||
* GuyLiner
|
* GuyLiner
|
||||||
|
* 3raven
|
||||||
|
* anarquimico
|
||||||
|
* TheOnlyJoeEnderman
|
||||||
|
* Ranko Saotome
|
||||||
|
* Gregor Parzefall
|
||||||
|
* Wbjitscool
|
||||||
|
* b3nderman
|
||||||
|
|
||||||
## MineClone5
|
## MineClone5
|
||||||
* kay27
|
* kay27
|
||||||
|
@ -140,11 +150,13 @@
|
||||||
* jordan4ibanez
|
* jordan4ibanez
|
||||||
* paramat
|
* paramat
|
||||||
* cora
|
* cora
|
||||||
|
* Faerraven / Michieal
|
||||||
|
|
||||||
## 3D Models
|
## 3D Models
|
||||||
* 22i
|
* 22i
|
||||||
* tobyplowy
|
* tobyplowy
|
||||||
* epCode
|
* epCode
|
||||||
|
* Faerraven / Michieal
|
||||||
|
|
||||||
## Textures
|
## Textures
|
||||||
* XSSheep
|
* XSSheep
|
||||||
|
@ -158,6 +170,8 @@
|
||||||
* MysticTempest
|
* MysticTempest
|
||||||
* RandomLegoBrick
|
* RandomLegoBrick
|
||||||
* cora
|
* cora
|
||||||
|
* Faerraven / Michieal
|
||||||
|
* Nicu
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
* Wuzzy
|
* Wuzzy
|
||||||
|
@ -171,9 +185,12 @@
|
||||||
* Emojigit
|
* Emojigit
|
||||||
* snowyu
|
* snowyu
|
||||||
* 3raven
|
* 3raven
|
||||||
|
* SakuraRiu
|
||||||
|
|
||||||
## Funders
|
## Funders
|
||||||
* 40W
|
* 40W
|
||||||
|
* bauknecht
|
||||||
|
* Cora
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
* celeron55 for creating Minetest
|
* celeron55 for creating Minetest
|
||||||
|
@ -181,3 +198,4 @@
|
||||||
* wsor for working tirelessly in the shadows for the good of all of us, particularly helping with solving contentDB and copyright issues.
|
* wsor for working tirelessly in the shadows for the good of all of us, particularly helping with solving contentDB and copyright issues.
|
||||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||||
* Notch and Jeb for being the major forces behind Minecraft
|
* Notch and Jeb for being the major forces behind Minecraft
|
||||||
|
* Dark Reaven Music (https://soundcloud.com/dark-reaven-music) for the main menu theme (Calmed Cube), which is licensed under https://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
Survive, farm, build, explore, play with friends, and do much more. Inspired by a well known block game, pushing beyond.
|
||||||
|
|
||||||
|
How to play:
|
||||||
|
|
||||||
|
#### Download Minetest
|
||||||
|
- Navigate to https://www.minetest.net/ to download the client.
|
||||||
|
- Once installed, open and select the "Content" tab
|
||||||
|
|
||||||
|
#### Install MineClone2 from ContentDB
|
||||||
|
- Click "Browse Online Content" and filter by Games (select "Games" from the dropdown box)
|
||||||
|
- Find "MineClone2" (should be first on the list or on the first page)
|
||||||
|
- Click the [+] button next to MineClone2 and wait for download to finish
|
||||||
|
- Click "Back to Main Menu"
|
||||||
|
|
||||||
|
#### Create new world and play
|
||||||
|
- Click "Start Game" tab
|
||||||
|
- At the bottom click the MineClone2 icon (the 2 dirt with grass blocks)
|
||||||
|
- Click "New", give your world a name
|
||||||
|
- You can leave seed blank or put in a word of your choice
|
||||||
|
- Select your new world
|
||||||
|
- Click "Play Game" and enjoy!
|
|
@ -2,8 +2,6 @@
|
||||||
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)
|
|
||||||
|
|
||||||
### 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
|
||||||
the world and dig and build almost every block in the world to create new
|
the world and dig and build almost every block in the world to create new
|
||||||
|
@ -158,7 +156,7 @@ The following features are incomplete:
|
||||||
|
|
||||||
* Some monsters and animals
|
* Some monsters and animals
|
||||||
* Redstone-related things
|
* Redstone-related things
|
||||||
* Special minecarts
|
* Some special minecarts (hopper and chest minecarts work)
|
||||||
* A couple of non-trivial blocks and items
|
* A couple of non-trivial blocks and items
|
||||||
|
|
||||||
Bonus features (not found in Minecraft):
|
Bonus features (not found in Minecraft):
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#File to document release steps with a view to evolving into a script
|
||||||
|
|
||||||
|
#Update CREDITS.md
|
||||||
|
#Update version in game.conf
|
||||||
|
|
||||||
|
lua tools/generate_ingame_credits.lua
|
||||||
|
|
||||||
|
git add CREDITS.md
|
||||||
|
git add mods/HUD/mcl_credits/people.lua
|
||||||
|
git add game.conf
|
||||||
|
|
||||||
|
#git add RELEASE.md
|
||||||
|
|
||||||
|
git commit -m "Pre-release update credits and set version 0.82.0"
|
||||||
|
|
||||||
|
git tag 0.82.0
|
||||||
|
|
||||||
|
git push origin 0.82.0
|
||||||
|
|
||||||
|
#Update version in game.conf to -SNAPSHOT
|
||||||
|
|
||||||
|
git commit -m "Post-release set version 0.82.0-SNAPSHOT"
|
|
@ -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=0.82.0-SNAPSHOT
|
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 990 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 865 KiB |
After Width: | Height: | Size: 916 KiB |
After Width: | Height: | Size: 606 KiB |
BIN
menu/footer.png
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 82 KiB |
BIN
menu/header.png
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 122 KiB |
BIN
menu/overlay.png
Before Width: | Height: | Size: 91 KiB |
|
@ -204,7 +204,7 @@ end
|
||||||
|
|
||||||
-- Checks if the given node would drop its useful drop if dug by a given tool.
|
-- Checks if the given node would drop its useful drop if dug by a given tool.
|
||||||
-- Returns true if it will yield its useful drop, false otherwise.
|
-- Returns true if it will yield its useful drop, false otherwise.
|
||||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
function mcl_autogroup.can_harvest(nodename, toolname, player)
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
|
||||||
if not ndef then
|
if not ndef then
|
||||||
|
@ -228,7 +228,9 @@ function mcl_autogroup.can_harvest(nodename, toolname)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if it can be dug by hand
|
-- Check if it can be dug by hand
|
||||||
local tdef = minetest.registered_tools[""]
|
if not player or not player:is_player() then return false end
|
||||||
|
local name = player:get_inventory():get_stack("hand", 1):get_name()
|
||||||
|
local tdef = minetest.registered_items[name]
|
||||||
if tdef then
|
if tdef then
|
||||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
for g, gdef in pairs(tdef._mcl_diggroups) do
|
||||||
if ndef.groups[g] then
|
if ndef.groups[g] then
|
||||||
|
@ -260,7 +262,7 @@ local function get_tool_capabilities(tdef)
|
||||||
|
|
||||||
-- If the damage group and punch interval from hand is not included,
|
-- If the damage group and punch interval from hand is not included,
|
||||||
-- then the user will not be able to attack with the tool.
|
-- then the user will not be able to attack with the tool.
|
||||||
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
|
local hand_toolcaps = mcl_meshhand.survival_hand_tool_caps
|
||||||
return {
|
return {
|
||||||
full_punch_interval = hand_toolcaps.full_punch_interval,
|
full_punch_interval = hand_toolcaps.full_punch_interval,
|
||||||
damage_groups = hand_toolcaps.damage_groups
|
damage_groups = hand_toolcaps.damage_groups
|
||||||
|
@ -280,7 +282,7 @@ end
|
||||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||||
-- loading order.
|
-- loading order.
|
||||||
function mcl_autogroup.get_groupcaps(toolname, efficiency)
|
function mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||||
local tdef = minetest.registered_tools[toolname]
|
local tdef = minetest.registered_items[toolname]
|
||||||
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
|
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
|
||||||
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
|
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
|
||||||
return groupcaps
|
return groupcaps
|
||||||
|
@ -350,7 +352,7 @@ local function overwrite()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for tname, tdef in pairs(minetest.registered_tools) do
|
for tname, tdef in pairs(minetest.registered_items) do
|
||||||
-- Assign groupcaps for digging the registered digging groups
|
-- Assign groupcaps for digging the registered digging groups
|
||||||
-- depending on the _mcl_diggroups in the tool definition
|
-- depending on the _mcl_diggroups in the tool definition
|
||||||
if tdef._mcl_diggroups then
|
if tdef._mcl_diggroups then
|
||||||
|
@ -360,6 +362,12 @@ local function overwrite()
|
||||||
minetest.override_item(tname, {
|
minetest.override_item(tname, {
|
||||||
tool_capabilities = toolcaps
|
tool_capabilities = toolcaps
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
-- This is needed to deal damage when punching mobs
|
||||||
|
-- with random items in hand in survival mode
|
||||||
|
minetest.override_item(tname, {
|
||||||
|
tool_capabilities = mcl_meshhand.survival_hand_tool_caps
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# mcl_autogroup
|
# mcl_autogroup
|
||||||
This mod emulate digging times from mc.
|
This mod emulate digging times from mc.
|
||||||
|
|
||||||
## mcl_autogroup.can_harvest(nodename, toolname)
|
## mcl_autogroup.can_harvest(nodename, toolname, player)
|
||||||
Return true if <nodename> can be dig with <toolname>.
|
Return true if <nodename> can be dig with <toolname> by <player>.
|
||||||
* nodename: string, valid nodename
|
* nodename: string, valid nodename
|
||||||
* toolname: (optional) string, valid toolname
|
* toolname: (optional) string, valid toolname
|
||||||
|
* player: (optinal) ObjectRef, valid player
|
||||||
|
|
||||||
## mcl_autogroup.get_groupcaps(toolname, efficiency)
|
## mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||||
This function is used to calculate diggroups for tools.
|
This function is used to calculate diggroups for tools.
|
||||||
|
|
|
@ -155,7 +155,6 @@ end, true)
|
||||||
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
if not damage_enabled then return 0 end
|
if not damage_enabled then return 0 end
|
||||||
if player:get_hp() > 0 then
|
if player:get_hp() > 0 then
|
||||||
mt_reason.approved = true
|
|
||||||
if hp_change < 0 then
|
if hp_change < 0 then
|
||||||
mcl_damage.run_damage_callbacks(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
mcl_damage.run_damage_callbacks(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
||||||
end
|
end
|
||||||
|
@ -163,9 +162,7 @@ minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
end, false)
|
end, false)
|
||||||
|
|
||||||
minetest.register_on_dieplayer(function(player, mt_reason)
|
minetest.register_on_dieplayer(function(player, mt_reason)
|
||||||
if mt_reason.approved then
|
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
||||||
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
|
|
||||||
end
|
|
||||||
minetest.log("action","Player "..player:get_player_name().." died at "..minetest.pos_to_string(vector.round(player:get_pos())))
|
minetest.log("action","Player "..player:get_player_name().." died at "..minetest.pos_to_string(vector.round(player:get_pos())))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,155 @@
|
||||||
|
mcl_events = {}
|
||||||
|
mcl_events.registered_events = {}
|
||||||
|
local disabled_events = minetest.settings:get("mcl_disabled_events")
|
||||||
|
if disabled_events then disabled_events = disabled_events:split(",")
|
||||||
|
else disabled_events = {} end
|
||||||
|
local DBG = minetest.settings:get_bool("mcl_logging_event_api",false)
|
||||||
|
local active_events = {}
|
||||||
|
|
||||||
|
local event_tpl = {
|
||||||
|
stage = 0,
|
||||||
|
max_stage = 1,
|
||||||
|
percent = 100,
|
||||||
|
bars = {},
|
||||||
|
completed = false,
|
||||||
|
cond_start = function(event) end, --return table of positions
|
||||||
|
on_step = function(event) end,
|
||||||
|
on_start = function(event) end,
|
||||||
|
on_stage_begin = function(event) end,
|
||||||
|
cond_progress = function(event) end, --return next stage
|
||||||
|
cond_complete = function(event) end, --return success
|
||||||
|
}
|
||||||
|
|
||||||
|
local function mcl_log(m,l)
|
||||||
|
if DBG then
|
||||||
|
if not l then l = "action" end
|
||||||
|
minetest.log(l,"[mcl_events] "..m)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_events.register_event(name,def)
|
||||||
|
if table.indexof(disabled_events,name) ~= -1 then return end
|
||||||
|
mcl_events.registered_events[name] = def
|
||||||
|
mcl_events.registered_events[name].name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addbars(self)
|
||||||
|
if not self.enable_bossbar then return end
|
||||||
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
|
if vector.distance(self.pos,player:get_pos()) < 64 then
|
||||||
|
local bar = mcl_bossbars.add_bar(player, {color = "red", text = self.readable_name .. ": Wave "..self.stage.." / "..self.max_stage, percentage = self.percent }, true,1)
|
||||||
|
table.insert(self.bars,bar)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function start_event(p,e)
|
||||||
|
mcl_log("[mcl_events] Event started: "..e.readable_name.." at "..minetest.pos_to_string(vector.round(p.pos)))
|
||||||
|
local idx = #active_events + 1
|
||||||
|
active_events[idx] = table.copy(e)
|
||||||
|
setmetatable(active_events[idx],{__index = event_tpl})
|
||||||
|
for k,v in pairs(p) do active_events[idx][k] = v end
|
||||||
|
active_events[idx].stage = 0
|
||||||
|
active_events[idx].percent = 100
|
||||||
|
active_events[idx].bars = {}
|
||||||
|
active_events[idx].time_start = os.time()
|
||||||
|
if active_events[idx].on_start then
|
||||||
|
active_events[idx]:on_start(p.pos)
|
||||||
|
end
|
||||||
|
addbars(active_events[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
local function finish_event(self,idx)
|
||||||
|
mcl_log("[mcl_events] Finished: "..self.readable_name.." at "..minetest.pos_to_string(vector.round(self.pos)))
|
||||||
|
if self.on_complete then self:on_complete() end
|
||||||
|
for _,b in pairs(self.bars) do
|
||||||
|
mcl_bossbars.remove_bar(b)
|
||||||
|
end
|
||||||
|
table.remove(active_events,idx)
|
||||||
|
end
|
||||||
|
|
||||||
|
local etime = 0
|
||||||
|
function check_events(dtime)
|
||||||
|
--process active events
|
||||||
|
for idx,ae in pairs(active_events) do
|
||||||
|
if ae.cond_complete and ae:cond_complete() then
|
||||||
|
ae.finished = true
|
||||||
|
finish_event(ae,idx)
|
||||||
|
elseif not ae.cond_complete and ae.max_stage and ae.max_stage <= ae.stage then
|
||||||
|
ae.finished = true
|
||||||
|
finish_event(ae,idx)
|
||||||
|
elseif not ae.finished and ae.cond_progress then
|
||||||
|
local p = ae:cond_progress()
|
||||||
|
if p == true then
|
||||||
|
ae.stage = ae.stage + 1
|
||||||
|
if ae:on_stage_begin() == true then
|
||||||
|
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." failed at stage_begin of stage "..ae.stage )
|
||||||
|
active_events[idx] = nil
|
||||||
|
end
|
||||||
|
elseif tonumber(p) then
|
||||||
|
ae.stage = tonumber(p) or ae.stage + 1
|
||||||
|
ae:on_stage_begin()
|
||||||
|
end
|
||||||
|
elseif not ae.finished and ae.on_step then
|
||||||
|
ae:on_step(dtime)
|
||||||
|
end
|
||||||
|
addbars(ae)
|
||||||
|
end
|
||||||
|
-- check if a new event should be started
|
||||||
|
etime = etime - dtime
|
||||||
|
if etime > 0 then return end
|
||||||
|
etime = 10
|
||||||
|
for _,e in pairs(mcl_events.registered_events) do
|
||||||
|
local pp = e.cond_start()
|
||||||
|
if pp then
|
||||||
|
--minetest.log("It's gonna start the raid maybe")
|
||||||
|
for _,p in pairs(pp) do
|
||||||
|
local start = true
|
||||||
|
if e.exclusive_to_area then
|
||||||
|
for _,ae in pairs(active_events) do
|
||||||
|
if e.name == ae.name and vector.distance(p.pos,ae.pos) < e.exclusive_to_area then start = false end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if start then
|
||||||
|
--minetest.log("It's gonna start the raid definitely")
|
||||||
|
start_event(p,e)
|
||||||
|
elseif DBG then
|
||||||
|
mcl_log("[mcl_events] Event "..e.readable_name.." already active at "..minetest.pos_to_string(vector.round(p.pos)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--minetest.log("Do not start this raid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for idx,ae in pairs(active_events) do
|
||||||
|
local player_near = false
|
||||||
|
for _,pl in pairs(minetest.get_connected_players()) do
|
||||||
|
if ae.pos and vector.distance(pl:get_pos(),ae.pos) < 64 then player_near = true end
|
||||||
|
end
|
||||||
|
if ae.pos and not player_near then
|
||||||
|
mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." aborted - no players near." )
|
||||||
|
active_events[idx] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_globalstep(check_events)
|
||||||
|
|
||||||
|
mcl_info.register_debug_field("Active Events",{
|
||||||
|
level = 4,
|
||||||
|
func = function(pl,pos)
|
||||||
|
return tostring(#active_events)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("event_start",{
|
||||||
|
privs = {debug = true},
|
||||||
|
description = "Debug command to start events",
|
||||||
|
func = function(pname,param)
|
||||||
|
local p = minetest.get_player_by_name(pname)
|
||||||
|
local evdef = mcl_events.registered_events[param]
|
||||||
|
if not evdef then return false,"Event "..param.." doesn't exist.'" end
|
||||||
|
start_event({pos=p:get_pos(),player=pname,factor=1},evdef)
|
||||||
|
return true,"Started event "..param
|
||||||
|
end,
|
||||||
|
})
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = mcl_events
|
||||||
|
author = cora
|
||||||
|
depends = mcl_mobs,mcl_bossbars, mcl_info
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1は爆発に巻き込まれた。
|
|
@ -3,26 +3,26 @@ mcl_vars = {}
|
||||||
|
|
||||||
mcl_vars.redstone_tick = 0.1
|
mcl_vars.redstone_tick = 0.1
|
||||||
|
|
||||||
--- GUI / inventory menu settings
|
-- GUI / inventory menu settings
|
||||||
mcl_vars.gui_slots = "listcolors[#9990;#FFF7;#FFF0;#000;#FFF]"
|
mcl_vars.gui_slots = "listcolors[#9990;#FFF7;#FFF0;#000;#FFF]"
|
||||||
|
|
||||||
-- nonbg is added as formspec prepend in mcl_formspec_prepend
|
-- nonbg is added as formspec prepend in mcl_formspec_prepend
|
||||||
mcl_vars.gui_nonbg = mcl_vars.gui_slots ..
|
mcl_vars.gui_nonbg = table.concat({
|
||||||
"style_type[image_button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]"..
|
mcl_vars.gui_slots,
|
||||||
"style_type[button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]"..
|
"style_type[image_button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]",
|
||||||
"style_type[field;textcolor=#323232]"..
|
"style_type[button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]",
|
||||||
"style_type[label;textcolor=#323232]"..
|
"style_type[field;textcolor=#323232]",
|
||||||
"style_type[textarea;textcolor=#323232]"..
|
"style_type[label;textcolor=#323232]",
|
||||||
"style_type[checkbox;textcolor=#323232]"
|
"style_type[textarea;textcolor=#323232]",
|
||||||
|
"style_type[checkbox;textcolor=#323232]",
|
||||||
|
})
|
||||||
|
|
||||||
-- Background stuff must be manually added by mods (no formspec prepend)
|
-- Background stuff must be manually added by mods (no formspec prepend)
|
||||||
mcl_vars.gui_bg_color = "bgcolor[#00000000]"
|
mcl_vars.gui_bg_color = "bgcolor[#00000000]"
|
||||||
mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;true;7]"
|
mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;true;7]"
|
||||||
|
|
||||||
-- Legacy
|
|
||||||
mcl_vars.inventory_header = ""
|
|
||||||
|
|
||||||
-- Tool wield size
|
-- Tool wield size
|
||||||
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
|
mcl_vars.tool_wield_scale = vector.new(1.8, 1.8, 1)
|
||||||
|
|
||||||
-- Mapgen variables
|
-- Mapgen variables
|
||||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||||
|
@ -35,55 +35,69 @@ mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize
|
||||||
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
|
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
|
||||||
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
|
||||||
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
|
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
|
||||||
|
|
||||||
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
|
||||||
|
|
||||||
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||||
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
|
||||||
|
|
||||||
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
|
||||||
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
|
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
|
||||||
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
|
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
|
||||||
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
|
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
|
||||||
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
|
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) /
|
||||||
|
mcl_vars.MAP_BLOCKSIZE)
|
||||||
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
|
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
|
||||||
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
|
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
|
||||||
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
||||||
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||||
|
|
||||||
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
|
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
|
||||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
|
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
|
||||||
|
|
||||||
|
---@param x integer
|
||||||
|
---@return integer
|
||||||
local function coordinate_to_block(x)
|
local function coordinate_to_block(x)
|
||||||
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param x integer
|
||||||
|
---@return integer
|
||||||
local function coordinate_to_chunk(x)
|
local function coordinate_to_chunk(x)
|
||||||
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
|
---@return Vector
|
||||||
function mcl_vars.pos_to_block(pos)
|
function mcl_vars.pos_to_block(pos)
|
||||||
return {
|
return vector.new(
|
||||||
x = coordinate_to_block(pos.x),
|
coordinate_to_block(pos.x),
|
||||||
y = coordinate_to_block(pos.y),
|
coordinate_to_block(pos.y),
|
||||||
z = coordinate_to_block(pos.z)
|
coordinate_to_block(pos.z)
|
||||||
}
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
|
---@return Vector
|
||||||
function mcl_vars.pos_to_chunk(pos)
|
function mcl_vars.pos_to_chunk(pos)
|
||||||
return {
|
return vector.new(
|
||||||
x = coordinate_to_chunk(pos.x),
|
coordinate_to_chunk(pos.x),
|
||||||
y = coordinate_to_chunk(pos.y),
|
coordinate_to_chunk(pos.y),
|
||||||
z = coordinate_to_chunk(pos.z)
|
coordinate_to_chunk(pos.z)
|
||||||
}
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
||||||
local k_positive_z = k_positive * 2
|
local k_positive_z = k_positive * 2
|
||||||
local k_positive_y = k_positive_z * k_positive_z
|
local k_positive_y = k_positive_z * k_positive_z
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
|
---@return integer
|
||||||
function mcl_vars.get_chunk_number(pos) -- unsigned int
|
function mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||||
local c = mcl_vars.pos_to_chunk(pos)
|
local c = mcl_vars.pos_to_chunk(pos)
|
||||||
return
|
return (c.y + k_positive) * k_positive_y +
|
||||||
(c.y + k_positive) * k_positive_y +
|
|
||||||
(c.z + k_positive) * k_positive_z +
|
(c.z + k_positive) * k_positive_z +
|
||||||
c.x + k_positive
|
c.x + k_positive
|
||||||
end
|
end
|
||||||
|
|
||||||
if not superflat and not singlenode then
|
if not superflat and not singlenode then
|
||||||
|
@ -117,11 +131,8 @@ elseif singlenode then
|
||||||
mcl_vars.mg_bedrock_is_rough = false
|
mcl_vars.mg_bedrock_is_rough = false
|
||||||
else
|
else
|
||||||
-- Classic superflat
|
-- Classic superflat
|
||||||
local ground = minetest.get_mapgen_setting("mgflat_ground_level")
|
local ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
|
||||||
ground = tonumber(ground)
|
|
||||||
if not ground then
|
|
||||||
ground = 8
|
|
||||||
end
|
|
||||||
mcl_vars.mg_overworld_min = ground - 3
|
mcl_vars.mg_overworld_min = ground - 3
|
||||||
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
|
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
|
||||||
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
|
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
|
||||||
|
@ -181,14 +192,16 @@ minetest.craftitemdef_default.stack_max = 64
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
|
|
||||||
local chunks = {} -- intervals of chunks generated
|
local chunks = {} -- intervals of chunks generated
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
function mcl_vars.add_chunk(pos)
|
function mcl_vars.add_chunk(pos)
|
||||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||||
local prev
|
local prev
|
||||||
for i, d in pairs(chunks) do
|
for i, d in pairs(chunks) do
|
||||||
if n <= d[2] then -- we've found it
|
if n <= d[2] then -- we've found it
|
||||||
if (n == d[2]) or (n >= d[1]) then return end -- already here
|
if (n == d[2]) or (n >= d[1]) then return end -- already here
|
||||||
if n == d[1]-1 then -- right before:
|
if n == d[1] - 1 then -- right before:
|
||||||
if prev and (prev[2] == n-1) then
|
if prev and (prev[2] == n - 1) then
|
||||||
prev[2] = d[2]
|
prev[2] = d[2]
|
||||||
table.remove(chunks, i)
|
table.remove(chunks, i)
|
||||||
return
|
return
|
||||||
|
@ -196,17 +209,20 @@ function mcl_vars.add_chunk(pos)
|
||||||
d[1] = n
|
d[1] = n
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if prev and (prev[2] == n-1) then --join to previous
|
if prev and (prev[2] == n - 1) then --join to previous
|
||||||
prev[2] = n
|
prev[2] = n
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
table.insert(chunks, i, {n, n}) -- insert new interval before i
|
table.insert(chunks, i, { n, n }) -- insert new interval before i
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
prev = d
|
prev = d
|
||||||
end
|
end
|
||||||
chunks[#chunks+1] = {n, n}
|
chunks[#chunks + 1] = { n, n }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param pos Vector
|
||||||
|
---@return boolean
|
||||||
function mcl_vars.is_generated(pos)
|
function mcl_vars.is_generated(pos)
|
||||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||||
for i, d in pairs(chunks) do
|
for i, d in pairs(chunks) do
|
||||||
|
@ -217,47 +233,46 @@ function mcl_vars.is_generated(pos)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
|
---"Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
|
||||||
-- p: Position, if it's wrong, {name="error"} node will return.
|
---@param pos Vector Position, if it's wrong, `{name="error"}` node will return.
|
||||||
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
|
---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout.
|
||||||
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
|
---@param us_timeout? number Optional (default: `244 = 0.000244 s = 1/80/80/80`), set it at least to `3000000` to let mapgen to finish its job
|
||||||
--
|
---@return node # Node definition, eg. `{name="air"}`. Unfortunately still can return `{name="ignore"}`.
|
||||||
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
|
---@nodiscard
|
||||||
function mcl_vars.get_node(p, force, us_timeout)
|
function mcl_vars.get_node(pos, force, us_timeout)
|
||||||
-- check initial circumstances
|
-- check initial circumstances
|
||||||
if not p or not p.x or not p.y or not p.z then return {name="error"} end
|
if not pos or not pos.x or not pos.y or not pos.z then return { name = "error" } end
|
||||||
|
|
||||||
-- try common way
|
-- try common way
|
||||||
local node = minetest.get_node(p)
|
local node = minetest.get_node(pos)
|
||||||
if node.name ~= "ignore" then
|
if node.name ~= "ignore" then
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy table to get sure it won't changed by other threads
|
-- copy vector to get sure it won't changed by other threads
|
||||||
local pos = {x=p.x,y=p.y,z=p.z}
|
local pos_copy = vector.copy(pos)
|
||||||
|
|
||||||
-- try LVM
|
-- try LVM
|
||||||
minetest.get_voxel_manip():read_from_map(pos, pos)
|
minetest.get_voxel_manip():read_from_map(pos_copy, pos_copy)
|
||||||
node = minetest.get_node(pos)
|
node = minetest.get_node(pos_copy)
|
||||||
if node.name ~= "ignore" or not force then
|
if node.name ~= "ignore" or not force then
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
-- all ways failed - need to emerge (or forceload if generated)
|
-- all ways failed - need to emerge (or forceload if generated)
|
||||||
local us_timeout = us_timeout or 244
|
if mcl_vars.is_generated(pos_copy) then
|
||||||
if mcl_vars.is_generated(pos) then
|
|
||||||
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
|
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
|
||||||
minetest.forceload_block(pos)
|
minetest.forceload_block(pos_copy)
|
||||||
else
|
else
|
||||||
minetest.emerge_area(pos, pos)
|
minetest.emerge_area(pos_copy, pos_copy)
|
||||||
end
|
end
|
||||||
|
|
||||||
local t = minetest.get_us_time()
|
local t = minetest.get_us_time()
|
||||||
|
|
||||||
node = minetest.get_node(pos)
|
node = minetest.get_node(pos_copy)
|
||||||
|
|
||||||
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
|
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < (us_timeout or 244)) do
|
||||||
node = minetest.get_node(pos)
|
node = minetest.get_node(pos_copy)
|
||||||
end
|
end
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
|
@ -11,42 +11,22 @@ Creative Commons Attribution 3.0 Unported (CC BY-SA 3.0)
|
||||||
http://creativecommons.org/licenses/by/3.0/
|
http://creativecommons.org/licenses/by/3.0/
|
||||||
|
|
||||||
Glass breaking sounds (CC BY 3.0):
|
Glass breaking sounds (CC BY 3.0):
|
||||||
1: http://www.freesound.org/people/cmusounddesign/sounds/71947/
|
1: http://www.freesound.org/people/cmusounddesign/sounds/71947/
|
||||||
2: http://www.freesound.org/people/Tomlija/sounds/97669/
|
2: http://www.freesound.org/people/Tomlija/sounds/97669/
|
||||||
3: http://www.freesound.org/people/lsprice/sounds/88808/
|
3: http://www.freesound.org/people/lsprice/sounds/88808/
|
||||||
|
|
||||||
default_tool_breaks.ogg by EdgardEdition (CC BY 3.0), http://www.freesound.org/people/EdgardEdition
|
default_tool_breaks.ogg by EdgardEdition (CC BY 3.0), http://www.freesound.org/people/EdgardEdition
|
||||||
|
|
||||||
Mito551 (sounds) (CC BY-SA 3.0):
|
Mito551 (sounds) (CC BY-SA 3.0):
|
||||||
default_dig_choppy.ogg
|
default_dig_crumbly.ogg
|
||||||
default_dig_cracky.ogg
|
|
||||||
default_dig_crumbly.1.ogg
|
|
||||||
default_dig_crumbly.2.ogg
|
|
||||||
default_dig_oddly_breakable_by_hand.ogg
|
default_dig_oddly_breakable_by_hand.ogg
|
||||||
default_dug_node.1.ogg
|
default_dug_node.*.ogg
|
||||||
default_dug_node.2.ogg
|
default_grass_footstep.*.ogg
|
||||||
default_grass_footstep.1.ogg
|
default_gravel_footstep.*.ogg
|
||||||
default_grass_footstep.2.ogg
|
default_place_node.*.ogg
|
||||||
default_grass_footstep.3.ogg
|
default_place_node_hard.*.ogg
|
||||||
default_gravel_footstep.1.ogg
|
default_wood_footstep.*.ogg
|
||||||
default_gravel_footstep.2.ogg
|
default_dirt_footstep.*.ogg
|
||||||
default_gravel_footstep.3.ogg
|
|
||||||
default_gravel_footstep.4.ogg
|
|
||||||
default_grass_footstep.1.ogg
|
|
||||||
default_place_node.1.ogg
|
|
||||||
default_place_node.2.ogg
|
|
||||||
default_place_node.3.ogg
|
|
||||||
default_place_node_hard.1.ogg
|
|
||||||
default_place_node_hard.2.ogg
|
|
||||||
default_hard_footstep.1.ogg
|
|
||||||
default_hard_footstep.2.ogg
|
|
||||||
default_hard_footstep.3.ogg
|
|
||||||
default_sand_footstep.1.ogg
|
|
||||||
default_sand_footstep.2.ogg
|
|
||||||
default_wood_footstep.1.ogg
|
|
||||||
default_wood_footstep.2.ogg
|
|
||||||
default_dirt_footstep.1.ogg
|
|
||||||
default_dirt_footstep.2.ogg
|
|
||||||
default_glass_footstep.ogg
|
default_glass_footstep.ogg
|
||||||
|
|
||||||
Metal sounds:
|
Metal sounds:
|
||||||
|
@ -54,35 +34,64 @@ Metal sounds:
|
||||||
- https://www.freesound.org/people/yadronoff/sounds/320397/
|
- https://www.freesound.org/people/yadronoff/sounds/320397/
|
||||||
default_dug_metal.*.ogg - Iwan Gabovitch - qubodup - CC0
|
default_dug_metal.*.ogg - Iwan Gabovitch - qubodup - CC0
|
||||||
- http://opengameart.org/users/qubodup
|
- http://opengameart.org/users/qubodup
|
||||||
default_metal_footstep.*.ogg - Ottomaani138 - CC0
|
default_metal_footstep.*.ogg - (CC0 1.0) - CC0 1.0
|
||||||
- https://www.freesound.org/people/Ottomaani138/sounds/232692/
|
- https://freesound.org/people/mypantsfelldown/sounds/398937/
|
||||||
default_place_node_metal.*.ogg - Ogrebane - CC0
|
default_place_node_metal.*.ogg - Ogrebane - CC0
|
||||||
- http://opengameart.org/content/wood-and-metal-sound-effects-volume-2
|
- http://opengameart.org/content/wood-and-metal-sound-effects-volume-2
|
||||||
|
|
||||||
AGFX (CC BY 3.0)
|
AGFX (CC BY 3.0):
|
||||||
https://www.freesound.org/people/AGFX/packs/1253/
|
https://www.freesound.org/people/AGFX/packs/1253/
|
||||||
default_water_footstep.1.ogg
|
default_water_footstep.*.ogg
|
||||||
default_water_footstep.2.ogg
|
|
||||||
default_water_footstep.3.ogg
|
|
||||||
(default_water_footstep.4.ogg is silent)
|
|
||||||
|
|
||||||
blukotek (CC0 1.0)
|
blukotek (CC0 1.0):
|
||||||
https://www.freesound.org/people/blukotek/sounds/251660/
|
https://www.freesound.org/people/blukotek/sounds/251660/
|
||||||
default_dig_snappy.ogg
|
default_dig_snappy.ogg
|
||||||
|
|
||||||
sonictechtonic (CC BY 3.0)
|
sonictechtonic (CC BY 3.0):
|
||||||
https://www.freesound.org/people/sonictechtonic/sounds/241872/
|
https://www.freesound.org/people/sonictechtonic/sounds/241872/
|
||||||
player_damage.ogg
|
player_damage.ogg
|
||||||
|
|
||||||
Voxelands project <http://www.voxelands.com/> (CC BY-SA 3.0)
|
Sheyvan (CC0 1.0):
|
||||||
|
https://freesound.org/people/Sheyvan/sounds/476113/
|
||||||
|
default_dig_choppy.*.ogg
|
||||||
|
|
||||||
|
lolamadeus (CC0 1.0):
|
||||||
|
https://freesound.org/people/lolamadeus/sounds/179341/
|
||||||
|
default_gravel_dig.*.ogg
|
||||||
|
default_gravel_dug.*.ogg
|
||||||
|
|
||||||
|
Benboncan (CC BY 3.0):
|
||||||
|
https://freesound.org/people/Benboncan/sounds/71823/
|
||||||
|
default_dig_cracky.*.ogg
|
||||||
|
|
||||||
|
Erdie (CC BY 3.0):
|
||||||
|
https://freesound.org/people/Erdie/sounds/41579/
|
||||||
|
default_hard_footstep.*.ogg
|
||||||
|
|
||||||
|
worthahep88 (CC0 1.0):
|
||||||
|
https://freesound.org/people/worthahep88/sounds/319224/
|
||||||
|
default_sand_footstep.*.ogg
|
||||||
|
|
||||||
|
dheming (CC BY 3.0):
|
||||||
|
https://freesound.org/people/dheming/sounds/268023/
|
||||||
|
default_ice_dig.*.ogg
|
||||||
|
|
||||||
|
InspectorJ (CC BY 3.0):
|
||||||
|
https://freesound.org/people/InspectorJ/sounds/416967/
|
||||||
|
default_ice_footstep.*.ogg
|
||||||
|
|
||||||
|
Angel_Perez_Grandi (CC BY 3.0):
|
||||||
|
https://freesound.org/people/Angel_Perez_Grandi/sounds/49190/
|
||||||
|
default_ice_dug.ogg
|
||||||
|
|
||||||
|
Voxelands project <http://www.voxelands.com/> (CC BY-SA 3.0):
|
||||||
mcl_sounds_place_node_water.ogg
|
mcl_sounds_place_node_water.ogg
|
||||||
mcl_sounds_dug_water.ogg
|
mcl_sounds_dug_water.ogg
|
||||||
|
|
||||||
(Note: Artists from the Voxelands project include: sdzen, darkrose, sapier,
|
(Note: Artists from the Voxelands project include: sdzen, darkrose, sapier,
|
||||||
Tom Peter, Telaron, juskiddink)
|
Tom Peter, Telaron, juskiddink)
|
||||||
|
|
||||||
Michel Baradari <https://opengameart.org/content/lava-splash> (CC BY 3.0)
|
Michel Baradari <https://opengameart.org/content/lava-splash> (CC BY 3.0):
|
||||||
|
|
||||||
default_place_node_lava.ogg
|
default_place_node_lava.ogg
|
||||||
|
|
||||||
Adam_N (CC0 1.0):
|
Adam_N (CC0 1.0):
|
||||||
|
@ -90,7 +99,7 @@ Adam_N (CC0 1.0):
|
||||||
Source: <https://www.freesound.org/people/Adam_N/sounds/346692/>
|
Source: <https://www.freesound.org/people/Adam_N/sounds/346692/>
|
||||||
|
|
||||||
Alecia Shepherd (CC BY-SA 4.0):
|
Alecia Shepherd (CC BY-SA 4.0):
|
||||||
mcl_sounds_cloth.ogg
|
mcl_sounds_cloth.*.ogg
|
||||||
Source: SnowSong sound and music pack <https://opengameart.org/content/snowsong-sound-and-music-pack>
|
Source: SnowSong sound and music pack <https://opengameart.org/content/snowsong-sound-and-music-pack>
|
||||||
|
|
||||||
Unknown authors (WTFPL):
|
Unknown authors (WTFPL):
|
||||||
|
|
|
@ -11,7 +11,7 @@ function mcl_sounds.node_sound_defaults(table)
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_dug_node", gain=0.25}
|
{name="default_dug_node", gain=0.25}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_oddly_breakable_by_hand", gain=1.0}
|
{name="default_dig_oddly_breakable_by_hand", gain=0.5}
|
||||||
table.place = table.place or
|
table.place = table.place or
|
||||||
{name="default_place_node_hard", gain=1.0}
|
{name="default_place_node_hard", gain=1.0}
|
||||||
return table
|
return table
|
||||||
|
@ -20,11 +20,11 @@ end
|
||||||
function mcl_sounds.node_sound_stone_defaults(table)
|
function mcl_sounds.node_sound_stone_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_hard_footstep", gain=0.5}
|
{name="default_hard_footstep", gain=0.2}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_hard_footstep", gain=1.0}
|
{name="default_hard_footstep", gain=1.0}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_cracky", gain=1.0}
|
{name="default_dig_cracky", gain=0.5}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
@ -32,13 +32,13 @@ end
|
||||||
function mcl_sounds.node_sound_metal_defaults(table)
|
function mcl_sounds.node_sound_metal_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_metal_footstep", gain=0.5}
|
{name="default_metal_footstep", gain=0.2}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_dug_metal", gain=1.0}
|
{name="default_dug_metal", gain=0.5}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_metal", gain=1.0}
|
{name="default_dig_metal", gain=0.5}
|
||||||
table.place = table.place or
|
table.place = table.place or
|
||||||
{name="default_place_node_metal", gain=1.0}
|
{name="default_place_node_metal", gain=0.5}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
@ -46,11 +46,11 @@ end
|
||||||
function mcl_sounds.node_sound_dirt_defaults(table)
|
function mcl_sounds.node_sound_dirt_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_dirt_footstep", gain=1.0}
|
{name="default_dirt_footstep", gain=0.25}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_dirt_footstep", gain=1.5}
|
{name="default_dirt_footstep", gain=1.0}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_crumbly", gain=1.0}
|
{name="default_dig_crumbly", gain=0.4}
|
||||||
table.place = table.place or
|
table.place = table.place or
|
||||||
{name="default_place_node", gain=1.0}
|
{name="default_place_node", gain=1.0}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
|
@ -60,11 +60,25 @@ end
|
||||||
function mcl_sounds.node_sound_sand_defaults(table)
|
function mcl_sounds.node_sound_sand_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_sand_footstep", gain=0.5}
|
{name="default_sand_footstep", gain=0.05}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_sand_footstep", gain=1.0}
|
{name="default_sand_footstep", gain=0.15}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_crumbly", gain=1.0}
|
{name="default_dig_crumbly", gain=0.4}
|
||||||
|
table.place = table.place or
|
||||||
|
{name="default_place_node", gain=1.0}
|
||||||
|
mcl_sounds.node_sound_defaults(table)
|
||||||
|
return table
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_sounds.node_sound_gravel_defaults(table)
|
||||||
|
table = table or {}
|
||||||
|
table.footstep = table.footstep or
|
||||||
|
{name="default_gravel_footstep", gain=0.25}
|
||||||
|
table.dug = table.dug or
|
||||||
|
{name="default_gravel_dug", gain=1.0}
|
||||||
|
table.dig = table.dig or
|
||||||
|
{name="default_gravel_dig", gain=0.35}
|
||||||
table.place = table.place or
|
table.place = table.place or
|
||||||
{name="default_place_node", gain=1.0}
|
{name="default_place_node", gain=1.0}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
|
@ -78,21 +92,33 @@ function mcl_sounds.node_sound_snow_defaults(table)
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="pedology_snow_soft_footstep", gain=1.0}
|
{name="pedology_snow_soft_footstep", gain=1.0}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_crumbly", gain=1.0}
|
{name="pedology_snow_soft_footstep", gain=1.0}
|
||||||
table.place = table.place or
|
table.place = table.place or
|
||||||
{name="default_place_node", gain=1.0}
|
{name="default_place_node", gain=1.0}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mcl_sounds.node_sound_ice_defaults(table)
|
||||||
|
table = table or {}
|
||||||
|
table.footstep = table.footstep or
|
||||||
|
{name="default_ice_footstep", gain=0.15}
|
||||||
|
table.dug = table.dug or
|
||||||
|
{name="default_ice_dug", gain=0.5}
|
||||||
|
table.dig = table.dig or
|
||||||
|
{name="default_ice_dig", gain=0.5}
|
||||||
|
mcl_sounds.node_sound_defaults(table)
|
||||||
|
return table
|
||||||
|
end
|
||||||
|
|
||||||
function mcl_sounds.node_sound_wood_defaults(table)
|
function mcl_sounds.node_sound_wood_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_wood_footstep", gain=0.5}
|
{name="default_wood_footstep", gain=0.15}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_wood_footstep", gain=1.0}
|
{name="default_wood_footstep", gain=1.0}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_choppy", gain=1.0}
|
{name="default_dig_choppy", gain=0.4}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
@ -128,11 +154,11 @@ end
|
||||||
function mcl_sounds.node_sound_glass_defaults(table)
|
function mcl_sounds.node_sound_glass_defaults(table)
|
||||||
table = table or {}
|
table = table or {}
|
||||||
table.footstep = table.footstep or
|
table.footstep = table.footstep or
|
||||||
{name="default_glass_footstep", gain=0.5}
|
{name="default_glass_footstep", gain=0.3}
|
||||||
table.dug = table.dug or
|
table.dug = table.dug or
|
||||||
{name="default_break_glass", gain=1.0}
|
{name="default_break_glass", gain=1.0}
|
||||||
table.dig = table.dig or
|
table.dig = table.dig or
|
||||||
{name="default_dig_cracky", gain=1.0}
|
{name="default_dig_cracky", gain=0.5}
|
||||||
mcl_sounds.node_sound_defaults(table)
|
mcl_sounds.node_sound_defaults(table)
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,8 +2,8 @@ mcl_util = {}
|
||||||
|
|
||||||
-- Updates all values in t using values from to*.
|
-- Updates all values in t using values from to*.
|
||||||
function table.update(t, ...)
|
function table.update(t, ...)
|
||||||
for _, to in ipairs{...} do
|
for _, to in ipairs {...} do
|
||||||
for k,v in pairs(to) do
|
for k, v in pairs(to) do
|
||||||
t[k] = v
|
t[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,8 +12,8 @@ end
|
||||||
|
|
||||||
-- Updates nil values in t using values from to*.
|
-- Updates nil values in t using values from to*.
|
||||||
function table.update_nil(t, ...)
|
function table.update_nil(t, ...)
|
||||||
for _, to in ipairs{...} do
|
for _, to in ipairs {...} do
|
||||||
for k,v in pairs(to) do
|
for k, v in pairs(to) do
|
||||||
if t[k] == nil then
|
if t[k] == nil then
|
||||||
t[k] = v
|
t[k] = v
|
||||||
end
|
end
|
||||||
|
@ -22,9 +22,9 @@ function table.update_nil(t, ...)
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default",false)
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false)
|
||||||
local LOG_MODULE = "[MCL2]"
|
local LOG_MODULE = "[MCL2]"
|
||||||
function mcl_util.mcl_log (message, module, bypass_default_logger)
|
function mcl_util.mcl_log(message, module, bypass_default_logger)
|
||||||
local selected_module = LOG_MODULE
|
local selected_module = LOG_MODULE
|
||||||
if module then
|
if module then
|
||||||
selected_module = module
|
selected_module = module
|
||||||
|
@ -34,7 +34,6 @@ function mcl_util.mcl_log (message, module, bypass_default_logger)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function mcl_util.file_exists(name)
|
function mcl_util.file_exists(name)
|
||||||
if type(name) ~= "string" then return end
|
if type(name) ~= "string" then return end
|
||||||
local f = io.open(name)
|
local f = io.open(name)
|
||||||
|
@ -69,7 +68,7 @@ function mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing, infini
|
||||||
local undef = minetest.registered_nodes[unode.name]
|
local undef = minetest.registered_nodes[unode.name]
|
||||||
if undef and undef.on_rightclick then
|
if undef and undef.on_rightclick then
|
||||||
undef.on_rightclick(pointed_thing.under, unode, placer,
|
undef.on_rightclick(pointed_thing.under, unode, placer,
|
||||||
itemstack, pointed_thing)
|
itemstack, pointed_thing)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
|
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
|
||||||
|
@ -151,23 +150,23 @@ end
|
||||||
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
||||||
if side == "right" then
|
if side == "right" then
|
||||||
if param2 == 0 then
|
if param2 == 0 then
|
||||||
return {x=pos.x-1, y=pos.y, z=pos.z}
|
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
||||||
elseif param2 == 1 then
|
elseif param2 == 1 then
|
||||||
return {x=pos.x, y=pos.y, z=pos.z+1}
|
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||||
elseif param2 == 2 then
|
elseif param2 == 2 then
|
||||||
return {x=pos.x+1, y=pos.y, z=pos.z}
|
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
||||||
elseif param2 == 3 then
|
elseif param2 == 3 then
|
||||||
return {x=pos.x, y=pos.y, z=pos.z-1}
|
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if param2 == 0 then
|
if param2 == 0 then
|
||||||
return {x=pos.x+1, y=pos.y, z=pos.z}
|
return {x = pos.x + 1, y = pos.y, z = pos.z}
|
||||||
elseif param2 == 1 then
|
elseif param2 == 1 then
|
||||||
return {x=pos.x, y=pos.y, z=pos.z-1}
|
return {x = pos.x, y = pos.y, z = pos.z - 1}
|
||||||
elseif param2 == 2 then
|
elseif param2 == 2 then
|
||||||
return {x=pos.x-1, y=pos.y, z=pos.z}
|
return {x = pos.x - 1, y = pos.y, z = pos.z}
|
||||||
elseif param2 == 3 then
|
elseif param2 == 3 then
|
||||||
return {x=pos.x, y=pos.y, z=pos.z+1}
|
return {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -185,7 +184,7 @@ end
|
||||||
function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_inventory, dst_list, condition)
|
function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_inventory, dst_list, condition)
|
||||||
local size = src_inventory:get_size(src_list)
|
local size = src_inventory:get_size(src_list)
|
||||||
local stack
|
local stack
|
||||||
for i=1, size do
|
for i = 1, size do
|
||||||
stack = src_inventory:get_stack(src_list, i)
|
stack = src_inventory:get_stack(src_list, i)
|
||||||
if not stack:is_empty() and (condition == nil or condition(stack, src_inventory, src_list, dst_inventory, dst_list)) then
|
if not stack:is_empty() and (condition == nil or condition(stack, src_inventory, src_list, dst_inventory, dst_list)) then
|
||||||
return i
|
return i
|
||||||
|
@ -288,10 +287,10 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
|
||||||
-- Main inventory for most container types
|
-- Main inventory for most container types
|
||||||
if sctype == 2 or sctype == 3 or sctype == 5 or sctype == 6 or sctype == 7 then
|
if sctype == 2 or sctype == 3 or sctype == 5 or sctype == 6 or sctype == 7 then
|
||||||
source_list = "main"
|
source_list = "main"
|
||||||
-- Furnace: output
|
-- Furnace: output
|
||||||
elseif sctype == 4 then
|
elseif sctype == 4 then
|
||||||
source_list = "dst"
|
source_list = "dst"
|
||||||
-- Unknown source container type. Bail out
|
-- Unknown source container type. Bail out
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -344,7 +343,7 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
|
||||||
-- Main inventory for most container types
|
-- Main inventory for most container types
|
||||||
if dctype == 2 or dctype == 3 or dctype == 5 or dctype == 6 or dctype == 7 then
|
if dctype == 2 or dctype == 3 or dctype == 5 or dctype == 6 or dctype == 7 then
|
||||||
destination_list = "main"
|
destination_list = "main"
|
||||||
-- Furnace source slot
|
-- Furnace source slot
|
||||||
elseif dctype == 4 then
|
elseif dctype == 4 then
|
||||||
destination_list = "src"
|
destination_list = "src"
|
||||||
end
|
end
|
||||||
|
@ -409,7 +408,7 @@ end
|
||||||
-- Returns true if item (itemstring or ItemStack) can be used as a furnace fuel.
|
-- Returns true if item (itemstring or ItemStack) can be used as a furnace fuel.
|
||||||
-- Returns false otherwise
|
-- Returns false otherwise
|
||||||
function mcl_util.is_fuel(item)
|
function mcl_util.is_fuel(item)
|
||||||
return minetest.get_craft_result({method="fuel", width=1, items={item}}).time ~= 0
|
return minetest.get_craft_result({method = "fuel", width = 1, items = {item}}).time ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns a on_place function for plants
|
-- Returns a on_place function for plants
|
||||||
|
@ -456,7 +455,7 @@ function mcl_util.generate_on_place_plant_function(condition)
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
if idef.sounds and idef.sounds.place then
|
if idef.sounds and idef.sounds.place then
|
||||||
minetest.sound_play(idef.sounds.place, {pos=pointed_thing.above, gain=1}, true)
|
minetest.sound_play(idef.sounds.place, {pos = pointed_thing.above, gain = 1}, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
itemstack = new_itemstack
|
itemstack = new_itemstack
|
||||||
|
@ -643,78 +642,80 @@ end
|
||||||
|
|
||||||
local function roundN(n, d)
|
local function roundN(n, d)
|
||||||
if type(n) ~= "number" then return n end
|
if type(n) ~= "number" then return n end
|
||||||
local m = 10^d
|
local m = 10 ^ d
|
||||||
return math.floor(n * m + 0.5) / m
|
return math.floor(n * m + 0.5) / m
|
||||||
end
|
end
|
||||||
|
|
||||||
local function close_enough(a,b)
|
local function close_enough(a, b)
|
||||||
local rt=true
|
local rt = true
|
||||||
if type(a) == "table" and type(b) == "table" then
|
if type(a) == "table" and type(b) == "table" then
|
||||||
for k,v in pairs(a) do
|
for k, v in pairs(a) do
|
||||||
if roundN(v,2) ~= roundN(b[k],2) then
|
if roundN(v, 2) ~= roundN(b[k], 2) then
|
||||||
rt=false
|
rt = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
rt = roundN(a,2) == roundN(b,2)
|
rt = roundN(a, 2) == roundN(b, 2)
|
||||||
end
|
end
|
||||||
return rt
|
return rt
|
||||||
end
|
end
|
||||||
|
|
||||||
local function props_changed(props,oldprops)
|
local function props_changed(props, oldprops)
|
||||||
local changed=false
|
local changed = false
|
||||||
local p={}
|
local p = {}
|
||||||
for k,v in pairs(props) do
|
for k, v in pairs(props) do
|
||||||
if not close_enough(v,oldprops[k]) then
|
if not close_enough(v, oldprops[k]) then
|
||||||
p[k]=v
|
p[k] = v
|
||||||
changed=true
|
changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return changed,p
|
return changed, p
|
||||||
end
|
end
|
||||||
|
|
||||||
--tests for roundN
|
--tests for roundN
|
||||||
local test_round1=15
|
local test_round1 = 15
|
||||||
local test_round2=15.00199999999
|
local test_round2 = 15.00199999999
|
||||||
local test_round3=15.00111111
|
local test_round3 = 15.00111111
|
||||||
local test_round4=15.00999999
|
local test_round4 = 15.00999999
|
||||||
|
|
||||||
assert(roundN(test_round1,2)==roundN(test_round1,2))
|
assert(roundN(test_round1, 2) == roundN(test_round1, 2))
|
||||||
assert(roundN(test_round1,2)==roundN(test_round2,2))
|
assert(roundN(test_round1, 2) == roundN(test_round2, 2))
|
||||||
assert(roundN(test_round1,2)==roundN(test_round3,2))
|
assert(roundN(test_round1, 2) == roundN(test_round3, 2))
|
||||||
assert(roundN(test_round1,2)~=roundN(test_round4,2))
|
assert(roundN(test_round1, 2) ~= roundN(test_round4, 2))
|
||||||
|
|
||||||
-- tests for close_enough
|
-- tests for close_enough
|
||||||
local test_cb = {-0.35,0,-0.35,0.35,0.8,0.35} --collisionboxes
|
local test_cb = {-0.35, 0, -0.35, 0.35, 0.8, 0.35} --collisionboxes
|
||||||
local test_cb_close = {-0.351213,0,-0.35,0.35,0.8,0.351212}
|
local test_cb_close = {-0.351213, 0, -0.35, 0.35, 0.8, 0.351212}
|
||||||
local test_cb_diff = {-0.35,0,-1.35,0.35,0.8,0.35}
|
local test_cb_diff = {-0.35, 0, -1.35, 0.35, 0.8, 0.35}
|
||||||
|
|
||||||
local test_eh = 1.65 --eye height
|
local test_eh = 1.65 --eye height
|
||||||
local test_eh_close = 1.65123123
|
local test_eh_close = 1.65123123
|
||||||
local test_eh_diff = 1.35
|
local test_eh_diff = 1.35
|
||||||
|
|
||||||
local test_nt = { r = 225, b = 225, a = 225, g = 225 } --nametag
|
local test_nt = {r = 225, b = 225, a = 225, g = 225} --nametag
|
||||||
local test_nt_diff = { r = 225, b = 225, a = 0, g = 225 }
|
local test_nt_diff = {r = 225, b = 225, a = 0, g = 225}
|
||||||
|
|
||||||
assert(close_enough(test_cb,test_cb_close))
|
assert(close_enough(test_cb, test_cb_close))
|
||||||
assert(not close_enough(test_cb,test_cb_diff))
|
assert(not close_enough(test_cb, test_cb_diff))
|
||||||
assert(close_enough(test_eh,test_eh_close))
|
assert(close_enough(test_eh, test_eh_close))
|
||||||
assert(not close_enough(test_eh,test_eh_diff))
|
assert(not close_enough(test_eh, test_eh_diff))
|
||||||
assert(not close_enough(test_nt,test_nt_diff)) --no floats involved here
|
assert(not close_enough(test_nt, test_nt_diff)) --no floats involved here
|
||||||
|
|
||||||
--tests for properties_changed
|
--tests for properties_changed
|
||||||
local test_properties_set1={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
local test_properties_set1 = {collisionbox = {-0.35, 0, -0.35, 0.35, 0.8, 0.35}, eye_height = 0.65,
|
||||||
local test_properties_set2={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
|
nametag_color = {r = 225, b = 225, a = 225, g = 225}}
|
||||||
|
local test_properties_set2 = {collisionbox = {-0.35, 0, -0.35, 0.35, 0.8, 0.35}, eye_height = 1.35,
|
||||||
|
nametag_color = {r = 225, b = 225, a = 225, g = 225}}
|
||||||
|
|
||||||
local test_p1,_=props_changed(test_properties_set1,test_properties_set1)
|
local test_p1, _ = props_changed(test_properties_set1, test_properties_set1)
|
||||||
local test_p2,_=props_changed(test_properties_set1,test_properties_set2)
|
local test_p2, _ = props_changed(test_properties_set1, test_properties_set2)
|
||||||
|
|
||||||
assert(not test_p1)
|
assert(not test_p1)
|
||||||
assert(test_p2)
|
assert(test_p2)
|
||||||
|
|
||||||
function mcl_util.set_properties(obj,props)
|
function mcl_util.set_properties(obj, props)
|
||||||
local changed,p=props_changed(props,obj:get_properties())
|
local changed, p = props_changed(props, obj:get_properties())
|
||||||
if changed then
|
if changed then
|
||||||
obj:set_properties(p)
|
obj:set_properties(p)
|
||||||
end
|
end
|
||||||
|
@ -728,3 +729,285 @@ function mcl_util.set_bone_position(obj, bone, pos, rot)
|
||||||
obj:set_bone_position(bone, pos or current_pos, rot or current_rot)
|
obj:set_bone_position(bone, pos or current_pos, rot or current_rot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Return a function to use in `on_place`.
|
||||||
|
---
|
||||||
|
---Allow to bypass the `buildable_to` node field in a `on_place` callback.
|
||||||
|
---
|
||||||
|
---You have to make sure that the nodes you return true for have `buildable_to = true`.
|
||||||
|
---@param func fun(node_name: string): boolean Return `true` if node must not replace the buildable_to node which have `node_name`
|
||||||
|
---@return fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: pointed_thing, param2: integer): ItemStack?
|
||||||
|
function mcl_util.bypass_buildable_to(func)
|
||||||
|
--------------------------
|
||||||
|
-- MINETEST CODE: UTILS --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
local function copy_pointed_thing(pointed_thing)
|
||||||
|
return {
|
||||||
|
type = pointed_thing.type,
|
||||||
|
above = pointed_thing.above and vector.copy(pointed_thing.above),
|
||||||
|
under = pointed_thing.under and vector.copy(pointed_thing.under),
|
||||||
|
ref = pointed_thing.ref,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function user_name(user)
|
||||||
|
return user and user:get_player_name() or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a logging function. For empty names, does not log.
|
||||||
|
local function make_log(name)
|
||||||
|
return name ~= "" and minetest.log or function() end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check_attached_node(p, n, group_rating)
|
||||||
|
local def = core.registered_nodes[n.name]
|
||||||
|
local d = vector.zero()
|
||||||
|
if group_rating == 3 then
|
||||||
|
-- always attach to floor
|
||||||
|
d.y = -1
|
||||||
|
elseif group_rating == 4 then
|
||||||
|
-- always attach to ceiling
|
||||||
|
d.y = 1
|
||||||
|
elseif group_rating == 2 then
|
||||||
|
-- attach to facedir or 4dir direction
|
||||||
|
if (def.paramtype2 == "facedir" or
|
||||||
|
def.paramtype2 == "colorfacedir") then
|
||||||
|
-- Attach to whatever facedir is "mounted to".
|
||||||
|
-- For facedir, this is where tile no. 5 point at.
|
||||||
|
|
||||||
|
-- The fallback vector here is in case 'facedir to dir' is nil due
|
||||||
|
-- to voxelmanip placing a wallmounted node without resetting a
|
||||||
|
-- pre-existing param2 value that is out-of-range for facedir.
|
||||||
|
-- The fallback vector corresponds to param2 = 0.
|
||||||
|
d = core.facedir_to_dir(n.param2) or vector.new(0, 0, 1)
|
||||||
|
elseif (def.paramtype2 == "4dir" or
|
||||||
|
def.paramtype2 == "color4dir") then
|
||||||
|
-- Similar to facedir handling
|
||||||
|
d = core.fourdir_to_dir(n.param2) or vector.new(0, 0, 1)
|
||||||
|
end
|
||||||
|
elseif def.paramtype2 == "wallmounted" or
|
||||||
|
def.paramtype2 == "colorwallmounted" then
|
||||||
|
-- Attach to whatever this node is "mounted to".
|
||||||
|
-- This where tile no. 2 points at.
|
||||||
|
|
||||||
|
-- The fallback vector here is used for the same reason as
|
||||||
|
-- for facedir nodes.
|
||||||
|
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
|
||||||
|
else
|
||||||
|
d.y = -1
|
||||||
|
end
|
||||||
|
local p2 = vector.add(p, d)
|
||||||
|
local nn = core.get_node(p2).name
|
||||||
|
local def2 = core.registered_nodes[nn]
|
||||||
|
if def2 and not def2.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(itemstack, placer, pointed_thing, param2)
|
||||||
|
-------------------
|
||||||
|
-- MINETEST CODE --
|
||||||
|
-------------------
|
||||||
|
local def = itemstack:get_definition()
|
||||||
|
if def.type ~= "node" or pointed_thing.type ~= "node" then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local under = pointed_thing.under
|
||||||
|
local oldnode_under = minetest.get_node_or_nil(under)
|
||||||
|
local above = pointed_thing.above
|
||||||
|
local oldnode_above = minetest.get_node_or_nil(above)
|
||||||
|
local playername = user_name(placer)
|
||||||
|
local log = make_log(playername)
|
||||||
|
|
||||||
|
if not oldnode_under or not oldnode_above then
|
||||||
|
log("info", playername .. " tried to place"
|
||||||
|
.. " node in unloaded position " .. minetest.pos_to_string(above))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local olddef_under = minetest.registered_nodes[oldnode_under.name]
|
||||||
|
olddef_under = olddef_under or minetest.nodedef_default
|
||||||
|
local olddef_above = minetest.registered_nodes[oldnode_above.name]
|
||||||
|
olddef_above = olddef_above or minetest.nodedef_default
|
||||||
|
|
||||||
|
if not olddef_above.buildable_to and not olddef_under.buildable_to then
|
||||||
|
log("info", playername .. " tried to place"
|
||||||
|
.. " node in invalid position " .. minetest.pos_to_string(above)
|
||||||
|
.. ", replacing " .. oldnode_above.name)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
-- CUSTOMIZED CODE --
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
-- Place above pointed node
|
||||||
|
local place_to = vector.copy(above)
|
||||||
|
|
||||||
|
-- If node under is buildable_to, check for callback result and place into it instead
|
||||||
|
if olddef_under.buildable_to and not func(oldnode_under.name) then
|
||||||
|
log("info", "node under is buildable to")
|
||||||
|
place_to = vector.copy(under)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
-- MINETEST CODE --
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
if minetest.is_protected(place_to, playername) then
|
||||||
|
log("action", playername
|
||||||
|
.. " tried to place " .. def.name
|
||||||
|
.. " at protected position "
|
||||||
|
.. minetest.pos_to_string(place_to))
|
||||||
|
minetest.record_protection_violation(place_to, playername)
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldnode = minetest.get_node(place_to)
|
||||||
|
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
|
||||||
|
|
||||||
|
-- Calculate direction for wall mounted stuff like torches and signs
|
||||||
|
if def.place_param2 ~= nil then
|
||||||
|
newnode.param2 = def.place_param2
|
||||||
|
elseif (def.paramtype2 == "wallmounted" or
|
||||||
|
def.paramtype2 == "colorwallmounted") and not param2 then
|
||||||
|
local dir = vector.subtract(under, above)
|
||||||
|
newnode.param2 = minetest.dir_to_wallmounted(dir)
|
||||||
|
-- Calculate the direction for furnaces and chests and stuff
|
||||||
|
elseif (def.paramtype2 == "facedir" or
|
||||||
|
def.paramtype2 == "colorfacedir" or
|
||||||
|
def.paramtype2 == "4dir" or
|
||||||
|
def.paramtype2 == "color4dir") and not param2 then
|
||||||
|
local placer_pos = placer and placer:get_pos()
|
||||||
|
if placer_pos then
|
||||||
|
local dir = vector.subtract(above, placer_pos)
|
||||||
|
newnode.param2 = minetest.dir_to_facedir(dir)
|
||||||
|
log("info", "facedir: " .. newnode.param2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local metatable = itemstack:get_meta():to_table().fields
|
||||||
|
|
||||||
|
-- Transfer color information
|
||||||
|
if metatable.palette_index and not def.place_param2 then
|
||||||
|
local color_divisor = nil
|
||||||
|
if def.paramtype2 == "color" then
|
||||||
|
color_divisor = 1
|
||||||
|
elseif def.paramtype2 == "colorwallmounted" then
|
||||||
|
color_divisor = 8
|
||||||
|
elseif def.paramtype2 == "colorfacedir" then
|
||||||
|
color_divisor = 32
|
||||||
|
elseif def.paramtype2 == "color4dir" then
|
||||||
|
color_divisor = 4
|
||||||
|
elseif def.paramtype2 == "colordegrotate" then
|
||||||
|
color_divisor = 32
|
||||||
|
end
|
||||||
|
if color_divisor then
|
||||||
|
local color = math.floor(metatable.palette_index / color_divisor)
|
||||||
|
local other = newnode.param2 % color_divisor
|
||||||
|
newnode.param2 = color * color_divisor + other
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if the node is attached and if it can be placed there
|
||||||
|
local an = minetest.get_item_group(def.name, "attached_node")
|
||||||
|
if an ~= 0 and
|
||||||
|
not check_attached_node(place_to, newnode, an) then
|
||||||
|
log("action", "attached node " .. def.name ..
|
||||||
|
" cannot be placed at " .. minetest.pos_to_string(place_to))
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
log("action", playername .. " places node "
|
||||||
|
.. def.name .. " at " .. minetest.pos_to_string(place_to))
|
||||||
|
|
||||||
|
-- Add node and update
|
||||||
|
minetest.add_node(place_to, newnode)
|
||||||
|
|
||||||
|
-- Play sound if it was done by a player
|
||||||
|
if playername ~= "" and def.sounds and def.sounds.place then
|
||||||
|
minetest.sound_play(def.sounds.place, {
|
||||||
|
pos = place_to,
|
||||||
|
exclude_player = playername,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local take_item = true
|
||||||
|
|
||||||
|
-- Run callback
|
||||||
|
if def.after_place_node then
|
||||||
|
-- Deepcopy place_to and pointed_thing because callback can modify it
|
||||||
|
local place_to_copy = vector.copy(place_to)
|
||||||
|
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||||
|
if def.after_place_node(place_to_copy, placer, itemstack,
|
||||||
|
pointed_thing_copy) then
|
||||||
|
take_item = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run script hook
|
||||||
|
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||||
|
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
||||||
|
local place_to_copy = vector.copy(place_to)
|
||||||
|
local newnode_copy = {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2}
|
||||||
|
local oldnode_copy = {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2}
|
||||||
|
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||||
|
if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
|
||||||
|
take_item = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if take_item then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[Check for a protection violation in a given area.
|
||||||
|
--
|
||||||
|
-- Applies is_protected() to a 3D lattice of points in the defined volume. The points are spaced
|
||||||
|
-- evenly throughout the volume and have a spacing similar to, but no larger than, "interval".
|
||||||
|
--
|
||||||
|
-- @param pos1 A position table of the area volume's first edge.
|
||||||
|
-- @param pos2 A position table of the area volume's second edge.
|
||||||
|
-- @param player The player performing the action.
|
||||||
|
-- @param interval Optional. Max spacing between checked points at the volume.
|
||||||
|
-- Default: Same as minetest.is_area_protected.
|
||||||
|
--
|
||||||
|
-- @return true on protection violation detection. false otherwise.
|
||||||
|
--
|
||||||
|
-- @notes *All corners and edges of the defined volume are checked.
|
||||||
|
]]
|
||||||
|
function mcl_util.check_area_protection(pos1, pos2, player, interval)
|
||||||
|
local name = player and player:get_player_name() or ""
|
||||||
|
|
||||||
|
local protected_pos = minetest.is_area_protected(pos1, pos2, name, interval)
|
||||||
|
if protected_pos then
|
||||||
|
minetest.record_protection_violation(protected_pos, name)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[Check for a protection violation on a single position.
|
||||||
|
--
|
||||||
|
-- @param position A position table to check for protection violation.
|
||||||
|
-- @param player The player performing the action.
|
||||||
|
--
|
||||||
|
-- @return true on protection violation detection. false otherwise.
|
||||||
|
]]
|
||||||
|
function mcl_util.check_position_protection(position, player)
|
||||||
|
local name = player and player:get_player_name() or ""
|
||||||
|
|
||||||
|
if minetest.is_protected(position, name) then
|
||||||
|
minetest.record_protection_violation(position, name)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
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=黒曜石のボート
|
|
@ -7,31 +7,24 @@ local pool = {}
|
||||||
local tick = false
|
local tick = false
|
||||||
|
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities",false)
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities", false)
|
||||||
local function mcl_log (message)
|
local function mcl_log(message)
|
||||||
if LOGGING_ON then
|
if LOGGING_ON then
|
||||||
mcl_util.mcl_log (message, "[Item Entities]", true)
|
mcl_util.mcl_log(message, "[Item Entities]", true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
local name
|
pool[player:get_player_name()] = 0
|
||||||
name = player:get_player_name()
|
|
||||||
pool[name] = 0
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
minetest.register_on_leaveplayer(function(player)
|
||||||
local name
|
pool[player:get_player_name()] = nil
|
||||||
name = player:get_player_name()
|
|
||||||
pool[name] = nil
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
local has_awards = minetest.get_modpath("awards")
|
local has_awards = minetest.get_modpath("awards")
|
||||||
|
|
||||||
local mcl_item_entity = {}
|
mcl_item_entity = {}
|
||||||
|
|
||||||
--basic settings
|
--basic settings
|
||||||
local item_drop_settings = {} --settings table
|
local item_drop_settings = {} --settings table
|
||||||
|
@ -46,22 +39,29 @@ item_drop_settings.random_item_velocity = true --this sets random item velocity
|
||||||
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
|
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
|
||||||
-- drop_single_item is disabled by default because it is annoying to throw away items from the intentory screen
|
-- drop_single_item is disabled by default because it is annoying to throw away items from the intentory screen
|
||||||
|
|
||||||
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
||||||
|
|
||||||
local function get_gravity()
|
local function get_gravity()
|
||||||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||||
end
|
end
|
||||||
|
|
||||||
local registered_pickup_achievement = {}
|
mcl_item_entity.registered_pickup_achievement = {}
|
||||||
|
|
||||||
--TODO: remove limitation of 1 award per itemname
|
---Register an achievement that will be unlocked on pickup.
|
||||||
|
---
|
||||||
|
---TODO: remove limitation of 1 award per itemname
|
||||||
|
---@param itemname string
|
||||||
|
---@param award string
|
||||||
function mcl_item_entity.register_pickup_achievement(itemname, award)
|
function mcl_item_entity.register_pickup_achievement(itemname, award)
|
||||||
if not has_awards then
|
if not has_awards then
|
||||||
minetest.log("warning", "[mcl_item_entity] Trying to register pickup achievement ["..award.."] for ["..itemname.."] while awards missing")
|
minetest.log("warning",
|
||||||
elseif registered_pickup_achievement[itemname] then
|
"[mcl_item_entity] Trying to register pickup achievement [" .. award .. "] for [" ..
|
||||||
minetest.log("error", "[mcl_item_entity] Trying to register already existing pickup achievement ["..award.."] for ["..itemname.."]")
|
itemname .. "] while awards missing")
|
||||||
|
elseif mcl_item_entity.registered_pickup_achievement[itemname] then
|
||||||
|
minetest.log("error",
|
||||||
|
"[mcl_item_entity] Trying to register already existing pickup achievement [" .. award .. "] for [" .. itemname .. "]")
|
||||||
else
|
else
|
||||||
registered_pickup_achievement[itemname] = award
|
mcl_item_entity.registered_pickup_achievement[itemname] = award
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,11 +74,13 @@ mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hi
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_end:dragon_egg", "mcl:PickUpDragonEgg")
|
mcl_item_entity.register_pickup_achievement("mcl_end:dragon_egg", "mcl:PickUpDragonEgg")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_armor:elytra", "mcl:skysTheLimit")
|
mcl_item_entity.register_pickup_achievement("mcl_armor:elytra", "mcl:skysTheLimit")
|
||||||
|
|
||||||
|
---@param object ObjectRef
|
||||||
|
---@param player ObjectRef
|
||||||
local function check_pickup_achievements(object, player)
|
local function check_pickup_achievements(object, player)
|
||||||
if has_awards then
|
if has_awards then
|
||||||
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
||||||
local playername = player:get_player_name()
|
local playername = player:get_player_name()
|
||||||
for name,award in pairs(registered_pickup_achievement) do
|
for name, award in pairs(mcl_item_entity.registered_pickup_achievement) do
|
||||||
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
|
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
|
||||||
awards.unlock(playername, award)
|
awards.unlock(playername, award)
|
||||||
end
|
end
|
||||||
|
@ -86,16 +88,23 @@ local function check_pickup_achievements(object, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param object ObjectRef
|
||||||
|
---@param luaentity Luaentity
|
||||||
|
---@param ignore_check? boolean
|
||||||
local function enable_physics(object, luaentity, ignore_check)
|
local function enable_physics(object, luaentity, ignore_check)
|
||||||
if luaentity.physical_state == false or ignore_check == true then
|
if luaentity.physical_state == false or ignore_check == true then
|
||||||
luaentity.physical_state = true
|
luaentity.physical_state = true
|
||||||
object:set_properties({
|
object:set_properties({
|
||||||
physical = true
|
physical = true
|
||||||
})
|
})
|
||||||
object:set_acceleration({x=0,y=-get_gravity(),z=0})
|
object:set_acceleration(vector.new(0, -get_gravity(), 0))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param object ObjectRef
|
||||||
|
---@param luaentity Luaentity
|
||||||
|
---@param ignore_check? boolean
|
||||||
|
---@param reset_movement? boolean
|
||||||
local function disable_physics(object, luaentity, ignore_check, reset_movement)
|
local function disable_physics(object, luaentity, ignore_check, reset_movement)
|
||||||
if luaentity.physical_state == true or ignore_check == true then
|
if luaentity.physical_state == true or ignore_check == true then
|
||||||
luaentity.physical_state = false
|
luaentity.physical_state = false
|
||||||
|
@ -103,17 +112,16 @@ local function disable_physics(object, luaentity, ignore_check, reset_movement)
|
||||||
physical = false
|
physical = false
|
||||||
})
|
})
|
||||||
if reset_movement ~= false then
|
if reset_movement ~= false then
|
||||||
object:set_velocity({x=0,y=0,z=0})
|
object:set_velocity(vector.zero())
|
||||||
object:set_acceleration({x=0,y=0,z=0})
|
object:set_acceleration(vector.zero())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
minetest.register_globalstep(function(_)
|
||||||
minetest.register_globalstep(function(dtime)
|
|
||||||
tick = not tick
|
tick = not tick
|
||||||
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
|
||||||
|
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
|
@ -125,7 +133,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
pos = pos,
|
pos = pos,
|
||||||
gain = 0.3,
|
gain = 0.3,
|
||||||
max_hear_distance = 16,
|
max_hear_distance = 16,
|
||||||
pitch = math.random(70,110)/100
|
pitch = math.random(70, 110) / 100
|
||||||
})
|
})
|
||||||
if pool[name] > 6 then
|
if pool[name] > 6 then
|
||||||
pool[name] = 6
|
pool[name] = 6
|
||||||
|
@ -135,15 +143,18 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local inv = player:get_inventory()
|
local inv = player:get_inventory()
|
||||||
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
|
local checkpos = vector.offset(pos, 0, item_drop_settings.player_collect_height, 0)
|
||||||
|
|
||||||
--magnet and collection
|
--magnet and collection
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
for _, object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
||||||
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and
|
||||||
|
object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer
|
||||||
|
and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
||||||
|
|
||||||
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
if object:get_luaentity()._magnet_timer >= 0 and
|
||||||
|
object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and
|
||||||
|
inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
||||||
|
|
||||||
-- Collection
|
-- Collection
|
||||||
if not object:get_luaentity()._removed then
|
if not object:get_luaentity()._removed then
|
||||||
|
@ -158,8 +169,8 @@ minetest.register_globalstep(function(dtime)
|
||||||
object:get_luaentity().target = checkpos
|
object:get_luaentity().target = checkpos
|
||||||
object:get_luaentity()._removed = true
|
object:get_luaentity()._removed = true
|
||||||
|
|
||||||
object:set_velocity({x=0,y=0,z=0})
|
object:set_velocity(vector.zero())
|
||||||
object:set_acceleration({x=0,y=0,z=0})
|
object:set_acceleration(vector.zero())
|
||||||
|
|
||||||
object:move_to(checkpos)
|
object:move_to(checkpos)
|
||||||
|
|
||||||
|
@ -179,7 +190,6 @@ minetest.register_globalstep(function(dtime)
|
||||||
local entity = object:get_luaentity()
|
local entity = object:get_luaentity()
|
||||||
entity.collector = player:get_player_name()
|
entity.collector = player:get_player_name()
|
||||||
entity.collected = true
|
entity.collected = true
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -194,6 +204,11 @@ end)
|
||||||
|
|
||||||
local tmp_id = 0
|
local tmp_id = 0
|
||||||
|
|
||||||
|
---@param drop string|drop_definition
|
||||||
|
---@param toolname string
|
||||||
|
---@param param2 integer
|
||||||
|
---@param paramtype2 paramtype2
|
||||||
|
---@return string[]
|
||||||
local function get_drops(drop, toolname, param2, paramtype2)
|
local function get_drops(drop, toolname, param2, paramtype2)
|
||||||
tmp_id = tmp_id + 1
|
tmp_id = tmp_id + 1
|
||||||
local tmp_node_name = "mcl_item_entity:" .. tmp_id
|
local tmp_node_name = "mcl_item_entity:" .. tmp_id
|
||||||
|
@ -202,7 +217,7 @@ local function get_drops(drop, toolname, param2, paramtype2)
|
||||||
drop = drop,
|
drop = drop,
|
||||||
paramtype2 = paramtype2
|
paramtype2 = paramtype2
|
||||||
}
|
}
|
||||||
local drops = minetest.get_node_drops({name = tmp_node_name, param2 = param2}, toolname)
|
local drops = minetest.get_node_drops({ name = tmp_node_name, param2 = param2 }, toolname)
|
||||||
minetest.registered_nodes[tmp_node_name] = nil
|
minetest.registered_nodes[tmp_node_name] = nil
|
||||||
return drops
|
return drops
|
||||||
end
|
end
|
||||||
|
@ -237,10 +252,17 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
-- NOTE: This function override allows digger to be nil.
|
-- NOTE: This function override allows digger to be nil.
|
||||||
-- This means there is no digger. This is a special case which allows this function to be called
|
-- This means there is no digger. This is a special case which allows this function to be called
|
||||||
-- by hand. Creative Mode is intentionally ignored in this case.
|
-- by hand. Creative Mode is intentionally ignored in this case.
|
||||||
|
if digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name()) then
|
||||||
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
local inv = digger:get_inventory()
|
||||||
|
if inv then
|
||||||
|
for _, item in ipairs(drops) do
|
||||||
|
if not inv:contains_item("main", item, true) then
|
||||||
|
inv:add_item("main", item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
elseif not doTileDrops then return end
|
||||||
|
|
||||||
-- Check if node will yield its useful drop by the digger's tool
|
-- Check if node will yield its useful drop by the digger's tool
|
||||||
local dug_node = minetest.get_node(pos)
|
local dug_node = minetest.get_node(pos)
|
||||||
|
@ -248,9 +270,9 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
local tool
|
local tool
|
||||||
if digger then
|
if digger then
|
||||||
tool = digger:get_wielded_item()
|
tool = digger:get_wielded_item()
|
||||||
tooldef = minetest.registered_tools[tool:get_name()]
|
tooldef = minetest.registered_items[tool:get_name()]
|
||||||
|
|
||||||
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name()) then
|
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name(), digger) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -265,7 +287,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
* table: Drop every itemstring in this table when dug by shears _mcl_silk_touch_drop
|
* table: Drop every itemstring in this table when dug by shears _mcl_silk_touch_drop
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local enchantments = tool and mcl_enchanting.get_enchantments(tool, "silk_touch")
|
local enchantments = tool and mcl_enchanting.get_enchantments(tool)
|
||||||
|
|
||||||
local silk_touch_drop = false
|
local silk_touch_drop = false
|
||||||
local nodedef = minetest.registered_nodes[dug_node.name]
|
local nodedef = minetest.registered_nodes[dug_node.name]
|
||||||
|
@ -294,7 +316,8 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1)
|
local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1)
|
||||||
local chance = fortune_drop.chance or fortune_drop.get_chance and fortune_drop.get_chance(fortune_level)
|
local chance = fortune_drop.chance or fortune_drop.get_chance and fortune_drop.get_chance(fortune_level)
|
||||||
if not chance or math.random() < chance then
|
if not chance or math.random() < chance then
|
||||||
drops = discrete_uniform_distribution(fortune_drop.multiply and drops or fortune_drop.items, min_count, max_count, fortune_drop.cap)
|
drops = discrete_uniform_distribution(fortune_drop.multiply and drops or fortune_drop.items, min_count, max_count,
|
||||||
|
fortune_drop.cap)
|
||||||
elseif fortune_drop.override then
|
elseif fortune_drop.override then
|
||||||
drops = {}
|
drops = {}
|
||||||
end
|
end
|
||||||
|
@ -306,13 +329,13 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
|
|
||||||
if digger and mcl_experience.throw_xp and not silk_touch_drop then
|
if digger and mcl_experience.throw_xp and not silk_touch_drop then
|
||||||
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
|
local experience_amount = minetest.get_item_group(dug_node.name, "xp")
|
||||||
if experience_amount > 0 then
|
if experience_amount > 0 then
|
||||||
mcl_experience.throw_xp(pos, experience_amount)
|
mcl_experience.throw_xp(pos, experience_amount)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,item in ipairs(drops) do
|
for _, item in ipairs(drops) do
|
||||||
local count
|
local count
|
||||||
if type(item) == "string" then
|
if type(item) == "string" then
|
||||||
count = ItemStack(item):get_count()
|
count = ItemStack(item):get_count()
|
||||||
|
@ -321,7 +344,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
local drop_item = ItemStack(item)
|
local drop_item = ItemStack(item)
|
||||||
drop_item:set_count(1)
|
drop_item:set_count(1)
|
||||||
for i=1,count do
|
for i = 1, count do
|
||||||
local dpos = table.copy(pos)
|
local dpos = table.copy(pos)
|
||||||
-- Apply offset for plantlike_rooted nodes because of their special shape
|
-- Apply offset for plantlike_rooted nodes because of their special shape
|
||||||
if nodedef and nodedef.drawtype == "plantlike_rooted" and nodedef.walkable then
|
if nodedef and nodedef.drawtype == "plantlike_rooted" and nodedef.walkable then
|
||||||
|
@ -348,7 +371,7 @@ end
|
||||||
function minetest.item_drop(itemstack, dropper, pos)
|
function minetest.item_drop(itemstack, dropper, pos)
|
||||||
if dropper and dropper:is_player() then
|
if dropper and dropper:is_player() then
|
||||||
local v = dropper:get_look_dir()
|
local v = dropper:get_look_dir()
|
||||||
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
|
local p = vector.offset(pos, 0, 1.2, 0)
|
||||||
local cs = itemstack:get_count()
|
local cs = itemstack:get_count()
|
||||||
if dropper:get_player_control().sneak then
|
if dropper:get_player_control().sneak then
|
||||||
cs = 1
|
cs = 1
|
||||||
|
@ -356,9 +379,9 @@ function minetest.item_drop(itemstack, dropper, pos)
|
||||||
local item = itemstack:take_item(cs)
|
local item = itemstack:take_item(cs)
|
||||||
local obj = minetest.add_item(p, item)
|
local obj = minetest.add_item(p, item)
|
||||||
if obj then
|
if obj then
|
||||||
v.x = v.x*4
|
v.x = v.x * 4
|
||||||
v.y = v.y*4 + 2
|
v.y = v.y * 4 + 2
|
||||||
v.z = v.z*4
|
v.z = v.z * 4
|
||||||
obj:set_velocity(v)
|
obj:set_velocity(v)
|
||||||
-- Force collection delay
|
-- Force collection delay
|
||||||
obj:get_luaentity()._insta_collect = false
|
obj:get_luaentity()._insta_collect = false
|
||||||
|
@ -376,16 +399,16 @@ end
|
||||||
|
|
||||||
local function cxcz(o, cw, one, zero)
|
local function cxcz(o, cw, one, zero)
|
||||||
if cw < 0 then
|
if cw < 0 then
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
table.insert(o, { [one] = 1, y = 0, [zero] = 0 })
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
table.insert(o, { [one] = -1, y = 0, [zero] = 0 })
|
||||||
else
|
else
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
table.insert(o, { [one] = -1, y = 0, [zero] = 0 })
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
table.insert(o, { [one] = 1, y = 0, [zero] = 0 })
|
||||||
end
|
end
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hopper_take_item (self, pos)
|
local function hopper_take_item(self, pos)
|
||||||
--mcl_log("self.itemstring: ".. self.itemstring)
|
--mcl_log("self.itemstring: ".. self.itemstring)
|
||||||
--mcl_log("self.itemstring: ".. minetest.pos_to_string(pos))
|
--mcl_log("self.itemstring: ".. minetest.pos_to_string(pos))
|
||||||
|
|
||||||
|
@ -394,17 +417,17 @@ local function hopper_take_item (self, pos)
|
||||||
if objs and self.itemstring then
|
if objs and self.itemstring then
|
||||||
--mcl_log("there is an itemstring. Number of objs: ".. #objs)
|
--mcl_log("there is an itemstring. Number of objs: ".. #objs)
|
||||||
|
|
||||||
for k,v in pairs(objs) do
|
for k, v in pairs(objs) do
|
||||||
local ent = v:get_luaentity()
|
local ent = v:get_luaentity()
|
||||||
|
|
||||||
-- Don't forget actual hoppers
|
-- Don't forget actual hoppers
|
||||||
if ent and ent.name == "mcl_minecarts:hopper_minecart" then
|
if ent and ent.name == "mcl_minecarts:hopper_minecart" then
|
||||||
local taken_items = false
|
local taken_items = false
|
||||||
|
|
||||||
mcl_log("ent.name: ".. tostring(ent.name))
|
mcl_log("ent.name: " .. tostring(ent.name))
|
||||||
mcl_log("ent pos: ".. tostring(ent.object:get_pos()))
|
mcl_log("ent pos: " .. tostring(ent.object:get_pos()))
|
||||||
|
|
||||||
local inv = mcl_entity_invs.load_inv(ent,5)
|
local inv = mcl_entity_invs.load_inv(ent, 5)
|
||||||
|
|
||||||
if not inv then
|
if not inv then
|
||||||
mcl_log("No inv")
|
mcl_log("No inv")
|
||||||
|
@ -428,7 +451,7 @@ local function hopper_take_item (self, pos)
|
||||||
local items_remaining = current_itemstack:get_count()
|
local items_remaining = current_itemstack:get_count()
|
||||||
|
|
||||||
-- This will take part of a floating item stack if no slot can hold the full amount
|
-- 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
|
for i = 1, ent._inv_size, 1 do
|
||||||
local stack = inv:get_stack("main", i)
|
local stack = inv:get_stack("main", i)
|
||||||
|
|
||||||
mcl_log("i: " .. tostring(i))
|
mcl_log("i: " .. tostring(i))
|
||||||
|
@ -500,13 +523,13 @@ minetest.register_entity(":__builtin:item", {
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
physical = true,
|
physical = true,
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
|
collisionbox = { -0.3, -0.3, -0.3, 0.3, 0.3, 0.3 },
|
||||||
pointable = false,
|
pointable = false,
|
||||||
visual = "wielditem",
|
visual = "wielditem",
|
||||||
visual_size = {x = 0.4, y = 0.4},
|
visual_size = { x = 0.4, y = 0.4 },
|
||||||
textures = {""},
|
textures = { "" },
|
||||||
spritediv = {x = 1, y = 1},
|
spritediv = { x = 1, y = 1 },
|
||||||
initial_sprite_basepos = {x = 0, y = 0},
|
initial_sprite_basepos = { x = 0, y = 0 },
|
||||||
is_visible = false,
|
is_visible = false,
|
||||||
infotext = "",
|
infotext = "",
|
||||||
},
|
},
|
||||||
|
@ -544,11 +567,11 @@ minetest.register_entity(":__builtin:item", {
|
||||||
if vel and vel.x == 0 and vel.z == 0 and self.random_velocity > 0 then
|
if vel and vel.x == 0 and vel.z == 0 and self.random_velocity > 0 then
|
||||||
local v = self.random_velocity
|
local v = self.random_velocity
|
||||||
local x = math.random(5, 10) / 10 * v
|
local x = math.random(5, 10) / 10 * v
|
||||||
if math.random(0,10) < 5 then x = -x end
|
if math.random(0, 10) < 5 then x = -x end
|
||||||
local z = math.random(5, 10) / 10 * v
|
local z = math.random(5, 10) / 10 * v
|
||||||
if math.random(0,10) < 5 then z = -z end
|
if math.random(0, 10) < 5 then z = -z end
|
||||||
local y = math.random(2,4)
|
local y = math.random(2, 4)
|
||||||
self.object:set_velocity({x=x, y=y, z=z})
|
self.object:set_velocity(vector.new(x, y, z))
|
||||||
end
|
end
|
||||||
self.random_velocity = 0
|
self.random_velocity = 0
|
||||||
end,
|
end,
|
||||||
|
@ -576,7 +599,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local max_count = stack:get_stack_max()
|
local max_count = stack:get_stack_max()
|
||||||
if count > max_count then
|
if count > max_count then
|
||||||
count = max_count
|
count = max_count
|
||||||
self.itemstring = stack:get_name().." "..max_count
|
self.itemstring = stack:get_name() .. " " .. max_count
|
||||||
end
|
end
|
||||||
local itemtable = stack:to_table()
|
local itemtable = stack:to_table()
|
||||||
local itemname = nil
|
local itemname = nil
|
||||||
|
@ -597,9 +620,9 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local prop = {
|
local prop = {
|
||||||
is_visible = true,
|
is_visible = true,
|
||||||
visual = "wielditem",
|
visual = "wielditem",
|
||||||
textures = {itemname},
|
textures = { itemname },
|
||||||
visual_size = {x = s, y = s},
|
visual_size = { x = s, y = s },
|
||||||
collisionbox = {-c, -c, -c, c, c, c},
|
collisionbox = { -c, -c, -c, c, c, c },
|
||||||
automatic_rotate = math.pi * 0.5,
|
automatic_rotate = math.pi * 0.5,
|
||||||
infotext = description,
|
infotext = description,
|
||||||
glow = glow,
|
glow = glow,
|
||||||
|
@ -695,9 +718,9 @@ minetest.register_entity(":__builtin:item", {
|
||||||
self._forcestart = nil
|
self._forcestart = nil
|
||||||
self._forcetimer = 0
|
self._forcetimer = 0
|
||||||
|
|
||||||
self.object:set_armor_groups({immortal = 1})
|
self.object:set_armor_groups({ immortal = 1 })
|
||||||
-- self.object:set_velocity({x = 0, y = 2, z = 0})
|
-- self.object:set_velocity(vector.new(0, 2, 0))
|
||||||
self.object:set_acceleration({x = 0, y = -get_gravity(), z = 0})
|
self.object:set_acceleration(vector.new(0, -get_gravity(), 0))
|
||||||
self:set_item(self.itemstring)
|
self:set_item(self.itemstring)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -710,9 +733,9 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local stack = ItemStack(entity.itemstring)
|
local stack = ItemStack(entity.itemstring)
|
||||||
local name = stack:get_name()
|
local name = stack:get_name()
|
||||||
if own_stack:get_name() ~= name or
|
if own_stack:get_name() ~= name or
|
||||||
own_stack:get_meta() ~= stack:get_meta() or
|
own_stack:get_meta() ~= stack:get_meta() or
|
||||||
own_stack:get_wear() ~= stack:get_wear() or
|
own_stack:get_wear() ~= stack:get_wear() or
|
||||||
own_stack:get_free_space() == 0 then
|
own_stack:get_free_space() == 0 then
|
||||||
-- Can not merge different or full stack
|
-- Can not merge different or full stack
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -745,8 +768,8 @@ minetest.register_entity(":__builtin:item", {
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
physical = false
|
physical = false
|
||||||
})
|
})
|
||||||
self.object:set_velocity({x=0,y=0,z=0})
|
self.object:set_velocity(vector.zero())
|
||||||
self.object:set_acceleration({x=0,y=0,z=0})
|
self.object:set_acceleration(vector.zero())
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.age = self.age + dtime
|
self.age = self.age + dtime
|
||||||
|
@ -761,21 +784,22 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- Delete corrupted item entities. The itemstring MUST be non-empty on its first step,
|
-- Delete corrupted item entities. The itemstring MUST be non-empty on its first step,
|
||||||
-- otherwise there might have some data corruption.
|
-- otherwise there might have some data corruption.
|
||||||
if self.itemstring == "" then
|
if self.itemstring == "" then
|
||||||
minetest.log("warning", "Item entity with empty itemstring found at "..minetest.pos_to_string(self.object:get_pos()).. "! Deleting it now.")
|
minetest.log("warning",
|
||||||
|
"Item entity with empty itemstring found at " .. minetest.pos_to_string(self.object:get_pos()) ..
|
||||||
|
"! Deleting it now.")
|
||||||
self._removed = true
|
self._removed = true
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
return
|
return
|
||||||
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 has taken item, it has gone, and no operations should be conducted on this item
|
||||||
if hopper_take_item(self, p) then
|
if hopper_take_item(self, p) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = minetest.get_node(p)
|
||||||
local in_unloaded = (node == nil)
|
local in_unloaded = node.name == "ignore"
|
||||||
|
|
||||||
if in_unloaded then
|
if in_unloaded then
|
||||||
-- Don't infinetly fall into unloaded map
|
-- Don't infinetly fall into unloaded map
|
||||||
|
@ -785,27 +809,27 @@ minetest.register_entity(":__builtin:item", {
|
||||||
|
|
||||||
if self.is_clock then
|
if self.is_clock then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
textures = { "mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame) }
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local nn = node.name
|
local nn = node.name
|
||||||
local is_in_water = (minetest.get_item_group(nn, "liquid") ~= 0)
|
local is_in_water = (minetest.get_item_group(nn, "liquid") ~= 0)
|
||||||
local nn_above = minetest.get_node({x=p.x, y=p.y+0.1, z=p.z}).name
|
local nn_above = minetest.get_node(vector.offset(p, 0, 0.1, 0)).name
|
||||||
-- make sure it's more or less stationary and is at water level
|
-- make sure it's more or less stationary and is at water level
|
||||||
local sleep_threshold = 0.3
|
local sleep_threshold = 0.3
|
||||||
local is_floating = false
|
local is_floating = false
|
||||||
local is_stationary = math.abs(self.object:get_velocity().x) < sleep_threshold
|
local is_stationary = math.abs(self.object:get_velocity().x) < sleep_threshold
|
||||||
and math.abs(self.object:get_velocity().y) < sleep_threshold
|
and math.abs(self.object:get_velocity().y) < sleep_threshold
|
||||||
and math.abs(self.object:get_velocity().z) < sleep_threshold
|
and math.abs(self.object:get_velocity().z) < sleep_threshold
|
||||||
if is_in_water and is_stationary then
|
if is_in_water and is_stationary then
|
||||||
is_floating = (is_in_water
|
is_floating = (is_in_water
|
||||||
and (minetest.get_item_group(nn_above, "liquid") == 0))
|
and (minetest.get_item_group(nn_above, "liquid") == 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_floating and self.physical_state == true then
|
if is_floating and self.physical_state == true then
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
self.object:set_velocity(vector.zero())
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration(vector.zero())
|
||||||
disable_physics(self.object, self)
|
disable_physics(self.object, self)
|
||||||
end
|
end
|
||||||
-- If no collector was found for a long enough time, declare the magnet as disabled
|
-- If no collector was found for a long enough time, declare the magnet as disabled
|
||||||
|
@ -825,7 +849,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
|
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
|
||||||
if self.age > 2 and minetest.get_item_group(self.itemstring, "fire_immune") == 0 then
|
if self.age > 2 and minetest.get_item_group(self.itemstring, "fire_immune") == 0 then
|
||||||
if dg ~= 2 then
|
if dg ~= 2 then
|
||||||
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5})
|
minetest.sound_play("builtin_item_lava", { pos = self.object:get_pos(), gain = 0.5 })
|
||||||
end
|
end
|
||||||
self._removed = true
|
self._removed = true
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
|
@ -865,7 +889,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check which one of the 4 sides is free
|
-- Check which one of the 4 sides is free
|
||||||
for o=1, #order do
|
for o = 1, #order do
|
||||||
local nn = minetest.get_node(vector.add(p, order[o])).name
|
local nn = minetest.get_node(vector.add(p, order[o])).name
|
||||||
local def = minetest.registered_nodes[nn]
|
local def = minetest.registered_nodes[nn]
|
||||||
if def and def.walkable == false and nn ~= "ignore" then
|
if def and def.walkable == false and nn ~= "ignore" then
|
||||||
|
@ -875,7 +899,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
-- If none of the 4 sides is free, shoot upwards
|
-- If none of the 4 sides is free, shoot upwards
|
||||||
if shootdir == nil then
|
if shootdir == nil then
|
||||||
shootdir = { x=0, y=1, z=0 }
|
shootdir = vector.new(0, 1, 0)
|
||||||
local nn = minetest.get_node(vector.add(p, shootdir)).name
|
local nn = minetest.get_node(vector.add(p, shootdir)).name
|
||||||
if nn == "ignore" then
|
if nn == "ignore" then
|
||||||
-- Do not push into ignore
|
-- Do not push into ignore
|
||||||
|
@ -885,7 +909,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
|
|
||||||
-- Set new item moving speed accordingly
|
-- Set new item moving speed accordingly
|
||||||
local newv = vector.multiply(shootdir, 3)
|
local newv = vector.multiply(shootdir, 3)
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration(vector.zero())
|
||||||
self.object:set_velocity(newv)
|
self.object:set_velocity(newv)
|
||||||
disable_physics(self.object, self, false, false)
|
disable_physics(self.object, self, false, false)
|
||||||
|
|
||||||
|
@ -907,10 +931,10 @@ minetest.register_entity(":__builtin:item", {
|
||||||
if self._forcetimer > 0 then
|
if self._forcetimer > 0 then
|
||||||
local cbox = self.object:get_properties().collisionbox
|
local cbox = self.object:get_properties().collisionbox
|
||||||
local ok = false
|
local ok = false
|
||||||
if self._force.x > 0 and (p.x > (self._forcestart.x + 0.5 + (cbox[4] - cbox[1])/2)) then ok = true
|
if self._force.x > 0 and (p.x > (self._forcestart.x + 0.5 + (cbox[4] - cbox[1]) / 2)) then ok = true
|
||||||
elseif self._force.x < 0 and (p.x < (self._forcestart.x + 0.5 - (cbox[4] - cbox[1])/2)) then ok = true
|
elseif self._force.x < 0 and (p.x < (self._forcestart.x + 0.5 - (cbox[4] - cbox[1]) / 2)) then ok = true
|
||||||
elseif self._force.z > 0 and (p.z > (self._forcestart.z + 0.5 + (cbox[6] - cbox[3])/2)) then ok = true
|
elseif self._force.z > 0 and (p.z > (self._forcestart.z + 0.5 + (cbox[6] - cbox[3]) / 2)) then ok = true
|
||||||
elseif self._force.z < 0 and (p.z < (self._forcestart.z + 0.5 - (cbox[6] - cbox[3])/2)) then ok = true end
|
elseif self._force.z < 0 and (p.z < (self._forcestart.z + 0.5 - (cbox[6] - cbox[3]) / 2)) then ok = true end
|
||||||
-- Item was successfully forced out. No more pushing
|
-- Item was successfully forced out. No more pushing
|
||||||
if ok then
|
if ok then
|
||||||
self._forcetimer = -1
|
self._forcetimer = -1
|
||||||
|
@ -941,7 +965,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- Set new item moving speed into the direciton of the liquid
|
-- Set new item moving speed into the direciton of the liquid
|
||||||
local newv = vector.multiply(vec, f)
|
local newv = vector.multiply(vec, f)
|
||||||
-- Swap to acceleration instead of a static speed to better mimic MC mechanics.
|
-- Swap to acceleration instead of a static speed to better mimic MC mechanics.
|
||||||
self.object:set_acceleration({x = newv.x, y = -0.22, z = newv.z})
|
self.object:set_acceleration(vector.new(newv.x, -0.22, newv.z))
|
||||||
|
|
||||||
self.physical_state = true
|
self.physical_state = true
|
||||||
self._flowing = true
|
self._flowing = true
|
||||||
|
@ -954,9 +978,10 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local cur_vec = self.object:get_velocity()
|
local cur_vec = self.object:get_velocity()
|
||||||
-- apply some acceleration in the opposite direction so it doesn't slide forever
|
-- apply some acceleration in the opposite direction so it doesn't slide forever
|
||||||
local vec = {
|
local vec = {
|
||||||
x = 0 -cur_vec.x*0.9,
|
x = 0 - cur_vec.x * 0.9,
|
||||||
y = 3 -cur_vec.y*0.9,
|
y = 3 - cur_vec.y * 0.9,
|
||||||
z = 0 -cur_vec.z*0.9}
|
z = 0 - cur_vec.z * 0.9
|
||||||
|
}
|
||||||
self.object:set_acceleration(vec)
|
self.object:set_acceleration(vec)
|
||||||
-- slow down the item in water
|
-- slow down the item in water
|
||||||
local vel = self.object:get_velocity()
|
local vel = self.object:get_velocity()
|
||||||
|
@ -980,20 +1005,20 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If node is not registered or node is walkably solid and resting on nodebox
|
-- If node is not registered or node is walkably solid and resting on nodebox
|
||||||
local nn = minetest.get_node({x=p.x, y=p.y-0.5, z=p.z}).name
|
local nn = minetest.get_node(vector.offset(p, 0, -0.5, 0)).name
|
||||||
local def = minetest.registered_nodes[nn]
|
local def = minetest.registered_nodes[nn]
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
local is_on_floor = def and (def.walkable
|
local is_on_floor = def and (def.walkable
|
||||||
and not def.groups.slippery and v.y == 0)
|
and not def.groups.slippery and v.y == 0)
|
||||||
|
|
||||||
if not minetest.registered_nodes[nn]
|
if not minetest.registered_nodes[nn]
|
||||||
or is_floating or is_on_floor then
|
or is_floating or is_on_floor then
|
||||||
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
|
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
|
||||||
-- Merge with close entities of the same item
|
-- Merge with close entities of the same item
|
||||||
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
|
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
|
||||||
local obj = object:get_luaentity()
|
local obj = object:get_luaentity()
|
||||||
if obj and obj.name == "__builtin:item"
|
if obj and obj.name == "__builtin:item"
|
||||||
and obj.physical_state == false then
|
and obj.physical_state == false then
|
||||||
if self:try_merge_with(own_stack, object, obj) then
|
if self:try_merge_with(own_stack, object, obj) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -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,349 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local HORNY_TIME = 30*20
|
||||||
|
local HORNY_AGAIN_TIME = 30*20 -- was 300 or 15*20
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
else
|
||||||
|
-- horny animal can mate for HORNY_TIME seconds,
|
||||||
|
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
||||||
|
if self.horny == true then
|
||||||
|
self.hornytimer = self.hornytimer + 1
|
||||||
|
|
||||||
|
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
||||||
|
self.hornytimer = 0
|
||||||
|
self.horny = false
|
||||||
|
end
|
||||||
|
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,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,411 @@
|
||||||
|
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
|
||||||
|
-- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down.
|
||||||
|
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
|
||||||
|
|
||||||
|
local function who_are_you_looking_at (self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
local stop_look_at_player_chance = math.random(833/self.curiosity)
|
||||||
|
-- was 10000 - div by 12 for avg entities as outside loop
|
||||||
|
|
||||||
|
local stop_look_at_player = stop_look_at_player_chance == 1
|
||||||
|
|
||||||
|
if self.attack or self.following then
|
||||||
|
self._locked_object = self.attack or self.following
|
||||||
|
elseif self._locked_object then
|
||||||
|
if stop_look_at_player then
|
||||||
|
--minetest.log("Stop look: ".. self.name)
|
||||||
|
self._locked_object = nil
|
||||||
|
end
|
||||||
|
elseif not self._locked_object then
|
||||||
|
if math.random(1, 30) then
|
||||||
|
--minetest.log("Change look check: ".. self.name)
|
||||||
|
local look_at_player_chance = math.random(20/self.curiosity)
|
||||||
|
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
|
||||||
|
-- then div by 20 as less freq lookup
|
||||||
|
|
||||||
|
local look_at_player = look_at_player_chance == 1
|
||||||
|
|
||||||
|
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 8)) do
|
||||||
|
if obj:is_player() and vector.distance(pos,obj:get_pos()) < 4 then
|
||||||
|
--minetest.log("Change look to player: ".. self.name)
|
||||||
|
self._locked_object = obj
|
||||||
|
break
|
||||||
|
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then
|
||||||
|
if look_at_player then
|
||||||
|
--minetest.log("Change look to mob: ".. self.name)
|
||||||
|
self._locked_object = obj
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:check_head_swivel(dtime)
|
||||||
|
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
|
||||||
|
|
||||||
|
who_are_you_looking_at (self)
|
||||||
|
|
||||||
|
local final_rotation = vector.new(0,0,0)
|
||||||
|
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -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,444 @@
|
||||||
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
|
local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
|
||||||
|
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
|
||||||
|
|
||||||
|
local PATHFINDING = "gowp"
|
||||||
|
|
||||||
|
local one_down = vector.new(0,-1,0)
|
||||||
|
local one_up = vector.new(0,1,0)
|
||||||
|
|
||||||
|
local plane_adjacents = {
|
||||||
|
vector.new(1,0,0),
|
||||||
|
vector.new(-1,0,0),
|
||||||
|
vector.new(0,0,1),
|
||||||
|
vector.new(0,0,-1),
|
||||||
|
}
|
||||||
|
|
||||||
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
|
||||||
|
|
||||||
|
local LOG_MODULE = "[Mobs Pathfinding]"
|
||||||
|
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("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
||||||
|
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 = {}
|
||||||
|
|
||||||
|
-- TODO Just pass in door position and the index before is open, the index after is close
|
||||||
|
local current_door_index = -1
|
||||||
|
|
||||||
|
for i, cur_pos in pairs(wp_in) do
|
||||||
|
local action = nil
|
||||||
|
|
||||||
|
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", 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 = "close", 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")
|
||||||
|
action = {type = "door", action = "open", target = cur_door_pos}
|
||||||
|
cur_pos_to_add = vector.add(cur_pos, one_down)
|
||||||
|
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
|
||||||
|
|
||||||
|
function mob_class:ready_to_path()
|
||||||
|
mcl_log("Check ready to path")
|
||||||
|
if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then
|
||||||
|
mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 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, cur_door_pos, t)
|
||||||
|
if t then
|
||||||
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
|
||||||
|
else
|
||||||
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p))
|
||||||
|
end
|
||||||
|
|
||||||
|
local enriched_path = nil
|
||||||
|
local wp, prospective_wp
|
||||||
|
|
||||||
|
local pos_closest_to_door = nil
|
||||||
|
local other_side_of_door = nil
|
||||||
|
|
||||||
|
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)
|
||||||
|
other_side_of_door = vector.add(cur_door_pos,-v)
|
||||||
|
|
||||||
|
local n = minetest.get_node(pos_closest_to_door)
|
||||||
|
|
||||||
|
if n.name == "air" then
|
||||||
|
mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
|
||||||
|
prospective_wp = minetest.find_path(p,pos_closest_to_door,150,1,4)
|
||||||
|
|
||||||
|
if prospective_wp then
|
||||||
|
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||||
|
|
||||||
|
table.insert(prospective_wp, cur_door_pos)
|
||||||
|
|
||||||
|
if t then
|
||||||
|
mcl_log("We have t, lets go from door to target")
|
||||||
|
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
|
||||||
|
append_paths (prospective_wp, wp_otherside_door_to_target)
|
||||||
|
|
||||||
|
wp = prospective_wp
|
||||||
|
mcl_log("We have a path from outside door to target")
|
||||||
|
else
|
||||||
|
mcl_log("We cannot path from outside door to target")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("No t, just add other side of door")
|
||||||
|
table.insert(prospective_wp, other_side_of_door)
|
||||||
|
wp = prospective_wp
|
||||||
|
end
|
||||||
|
|
||||||
|
if wp then
|
||||||
|
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("Cannot path to this air block next to door.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("No door found")
|
||||||
|
end
|
||||||
|
|
||||||
|
if wp and not enriched_path then
|
||||||
|
mcl_log("Wp but not enriched")
|
||||||
|
enriched_path = generate_enriched_path(wp)
|
||||||
|
end
|
||||||
|
return enriched_path
|
||||||
|
end
|
||||||
|
|
||||||
|
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 not self:ready_to_path() then return end
|
||||||
|
|
||||||
|
self.order = nil
|
||||||
|
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
local t = vector.offset(target,0,1,0)
|
||||||
|
|
||||||
|
--Check direct route
|
||||||
|
local wp = minetest.find_path(p,t,150,1,4)
|
||||||
|
|
||||||
|
if not wp then
|
||||||
|
mcl_log("### No direct path. Path through door closest to target.")
|
||||||
|
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
|
||||||
|
wp = calculate_path_through_door(p, door_near_target, t)
|
||||||
|
|
||||||
|
if not wp then
|
||||||
|
mcl_log("### No path though door closest to target. Try door closest to origin.")
|
||||||
|
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
|
||||||
|
wp = calculate_path_through_door(p, door_closest, t)
|
||||||
|
|
||||||
|
-- Path through 2 doors
|
||||||
|
if not wp then
|
||||||
|
mcl_log("### Still not wp. Need to path through 2 doors.")
|
||||||
|
local path_through_closest_door = calculate_path_through_door(p, door_closest)
|
||||||
|
|
||||||
|
if path_through_closest_door and #path_through_closest_door > 0 then
|
||||||
|
mcl_log("We have path through first door")
|
||||||
|
mcl_log("Number of pos in path through door: " .. tostring(#path_through_closest_door))
|
||||||
|
|
||||||
|
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
||||||
|
if pos_after_door_entry then
|
||||||
|
local pos_after_door = vector.add(pos_after_door_entry["pos"], one_up)
|
||||||
|
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
|
||||||
|
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
|
||||||
|
if path_after_door and #path_after_door > 1 then
|
||||||
|
mcl_log("We have path after first door")
|
||||||
|
table.remove(path_after_door, 1) -- Remove duplicate
|
||||||
|
wp = path_through_closest_door
|
||||||
|
append_paths (wp, path_after_door)
|
||||||
|
else
|
||||||
|
mcl_log("Path after door is not good")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("No pos after door")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("Path through closest door empty or null")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("ok, we have a path through 1 door")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
wp = generate_enriched_path(wp)
|
||||||
|
mcl_log("We have a direct route")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not wp then
|
||||||
|
mcl_log("Could not calculate path")
|
||||||
|
self._pf_last_failed = os.time()
|
||||||
|
-- If cannot path, don't immediately try again
|
||||||
|
end
|
||||||
|
|
||||||
|
if wp and #wp > 0 then
|
||||||
|
--output_table(wp)
|
||||||
|
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
|
||||||
|
|
||||||
|
function mob_class:check_gowp(dtime)
|
||||||
|
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 < 1.8 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
|
||||||
|
elseif not self.current_target then
|
||||||
|
mcl_log("Not close enough to targ: ".. tostring(distance_to_targ))
|
||||||
|
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 are at current_target or have no current target. 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 and not close enough. 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 >= PATHFINDING_FAIL_THRESHOLD 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")
|
||||||
|
|
||||||
|
if self.waypoints then
|
||||||
|
mcl_log("WP: " .. tostring(self.waypoints))
|
||||||
|
mcl_log("WP num: " .. tostring(#self.waypoints))
|
||||||
|
else
|
||||||
|
mcl_log("No wp set")
|
||||||
|
end
|
||||||
|
if self.current_target then
|
||||||
|
mcl_log("Current target: " .. tostring(self.current_target))
|
||||||
|
else
|
||||||
|
mcl_log("No current target")
|
||||||
|
end
|
||||||
|
|
||||||
|
local final_wp = minetest.find_path(p,self._target,150,1,4)
|
||||||
|
if final_wp then
|
||||||
|
mcl_log("We can 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"]))
|
||||||
|
mcl_log("target is: ".. minetest.pos_to_string(self._target))
|
||||||
|
self.current_target = nil
|
||||||
|
end
|
||||||
|
return
|
||||||
|
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,
|
||||||
|
@ -166,6 +169,8 @@ local list_of_all_biomes = {
|
||||||
"MushroomIslandShore",
|
"MushroomIslandShore",
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
|
"BambooJungleM_shore",
|
||||||
|
"BambooJungle_shore",
|
||||||
"MangroveSwamp_shore",
|
"MangroveSwamp_shore",
|
||||||
|
|
||||||
-- dimension biome:
|
-- dimension biome:
|
||||||
|
@ -213,6 +218,10 @@ local list_of_all_biomes = {
|
||||||
"JungleEdge",
|
"JungleEdge",
|
||||||
"SavannaM",
|
"SavannaM",
|
||||||
"MangroveSwamp",
|
"MangroveSwamp",
|
||||||
|
"BambooJungle",
|
||||||
|
"BambooJungleEdge",
|
||||||
|
"BambooJungleEdgeM",
|
||||||
|
"BambooJungleM",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- count how many mobs are in an area
|
-- count how many mobs are in an area
|
||||||
|
@ -241,6 +250,24 @@ local function count_mobs_total(mob_type)
|
||||||
return num
|
return num
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function count_mobs_all()
|
||||||
|
local mobs_found = {}
|
||||||
|
local num = 0
|
||||||
|
for _,entity in pairs(minetest.luaentities) do
|
||||||
|
if entity.is_mob then
|
||||||
|
local mob_type = entity.type -- animal / monster / npc
|
||||||
|
local mob_name = entity.name
|
||||||
|
if mobs_found[mob_name] then
|
||||||
|
mobs_found[mob_name] = mobs_found[mob_name] + 1
|
||||||
|
else
|
||||||
|
mobs_found[mob_name] = 1
|
||||||
|
end
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return mobs_found, num
|
||||||
|
end
|
||||||
|
|
||||||
local function count_mobs_total_cap(mob_type)
|
local function count_mobs_total_cap(mob_type)
|
||||||
local num = 0
|
local num = 0
|
||||||
for _,l in pairs(minetest.luaentities) do
|
for _,l in pairs(minetest.luaentities) do
|
||||||
|
@ -399,11 +426,9 @@ local two_pi = 2 * math.pi
|
||||||
local function get_next_mob_spawn_pos(pos)
|
local function get_next_mob_spawn_pos(pos)
|
||||||
local distance = math_random(25, 32)
|
local distance = math_random(25, 32)
|
||||||
local angle = math_random() * two_pi
|
local angle = math_random() * two_pi
|
||||||
return {
|
local xoff = math_round(distance * math_cos(angle))
|
||||||
x = math_round(pos.x + distance * math_cos(angle)),
|
local yoff = math_round(distance * math_sin(angle))
|
||||||
y = pos.y,
|
return vector.offset(pos, xoff, 0, yoff)
|
||||||
z = math_round(pos.z + distance * math_sin(angle))
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function decypher_limits(posy)
|
local function decypher_limits(posy)
|
||||||
|
@ -556,7 +581,7 @@ local S = minetest.get_translator("mcl_mobs")
|
||||||
|
|
||||||
minetest.register_chatcommand("spawn_mob",{
|
minetest.register_chatcommand("spawn_mob",{
|
||||||
privs = { debug = true },
|
privs = { debug = true },
|
||||||
description=S("spawn_mob is a chatcommand that allows you to type in the name of a mob without 'typing mobs_mc:' all the time like so; 'spawn_mob spider'. however, there is more you can do with this special command, currently you can edit any number, boolian, and string variable you choose with this format: spawn_mob 'any_mob:var<mobs_variable=variable_value>:'. any_mob being your mob of choice, mobs_variable being the variable, and variable value being the value of the chosen variable. and example of this format: \n spawn_mob skeleton:var<passive=true>:\n this would spawn a skeleton that wouldn't attack you. REMEMBER-THIS> when changing a number value always prefix it with 'NUM', example: \n spawn_mob skeleton:var<jump_height=NUM10>:\n this setting the skelly's jump height to 10. if you want to make multiple changes to a mob, you can, example: \n spawn_mob skeleton:var<passive=true>::var<jump_height=NUM10>::var<fly_in=air>::var<fly=true>:\n etc."),
|
description=S("spawn_mob is a chatcommand that allows you to type in the name of a mob without 'typing mobs_mc:' all the time like so; 'spawn_mob spider'. however, there is more you can do with this special command, currently you can edit any number, boolean, and string variable you choose with this format: spawn_mob 'any_mob:var<mobs_variable=variable_value>:'. any_mob being your mob of choice, mobs_variable being the variable, and variable value being the value of the chosen variable. and example of this format: \n spawn_mob skeleton:var<passive=true>:\n this would spawn a skeleton that wouldn't attack you. REMEMBER-THIS> when changing a number value always prefix it with 'NUM', example: \n spawn_mob skeleton:var<jump_height=NUM10>:\n this setting the skelly's jump height to 10. if you want to make multiple changes to a mob, you can, example: \n spawn_mob skeleton:var<passive=true>::var<jump_height=NUM10>::var<fly_in=air>::var<fly=true>:\n etc."),
|
||||||
func = function(n,param)
|
func = function(n,param)
|
||||||
local pos = minetest.get_player_by_name(n):get_pos()
|
local pos = minetest.get_player_by_name(n):get_pos()
|
||||||
|
|
||||||
|
@ -576,15 +601,14 @@ minetest.register_chatcommand("spawn_mob",{
|
||||||
|
|
||||||
local mob = mcl_mobs.spawn(pos,mobname)
|
local mob = mcl_mobs.spawn(pos,mobname)
|
||||||
|
|
||||||
for c=1, #modifiers do
|
if mob then
|
||||||
modifs = modifiers[c]
|
for c=1, #modifiers do
|
||||||
|
modifs = modifiers[c]
|
||||||
|
|
||||||
local mod1 = string.find(modifs, ":")
|
local mod1 = string.find(modifs, ":")
|
||||||
local mod_start = string.find(modifs, "<")
|
local mod_start = string.find(modifs, "<")
|
||||||
local mod_vals = string.find(modifs, "=")
|
local mod_vals = string.find(modifs, "=")
|
||||||
local mod_end = string.find(modifs, ">")
|
local mod_end = string.find(modifs, ">")
|
||||||
local mod_end = string.find(modifs, ">")
|
|
||||||
if mob then
|
|
||||||
local mob_entity = mob:get_luaentity()
|
local mob_entity = mob:get_luaentity()
|
||||||
if string.sub(modifs, mod1+1, mod1+3) == "var" then
|
if string.sub(modifs, mod1+1, mod1+3) == "var" then
|
||||||
if mod1 and mod_start and mod_vals and mod_end then
|
if mod1 and mod_start and mod_vals and mod_end then
|
||||||
|
@ -615,14 +639,12 @@ minetest.register_chatcommand("spawn_mob",{
|
||||||
minetest.log("warning", n.." couldn't modify "..mobname.." at "..minetest.pos_to_string(pos).. ", missing modification type")
|
minetest.log("warning", n.." couldn't modify "..mobname.." at "..minetest.pos_to_string(pos).. ", missing modification type")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if mob then
|
|
||||||
return true, mobname.." spawned at "..minetest.pos_to_string(pos),
|
|
||||||
minetest.log("action", n.." spawned "..mobname.." at "..minetest.pos_to_string(pos))
|
minetest.log("action", n.." spawned "..mobname.." at "..minetest.pos_to_string(pos))
|
||||||
|
return true, mobname.." spawned at "..minetest.pos_to_string(pos)
|
||||||
|
else
|
||||||
|
return false, "Couldn't spawn "..mobname
|
||||||
end
|
end
|
||||||
return false, "Couldn't spawn "..mobname
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -630,6 +652,9 @@ if mobs_spawn then
|
||||||
|
|
||||||
local perlin_noise
|
local perlin_noise
|
||||||
|
|
||||||
|
-- Get pos to spawn, x and z are randomised, y is range
|
||||||
|
|
||||||
|
|
||||||
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
||||||
--create a disconnected clone of the spawn dictionary
|
--create a disconnected clone of the spawn dictionary
|
||||||
--prevents memory leak
|
--prevents memory leak
|
||||||
|
@ -661,34 +686,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(spawning_position) then
|
local object
|
||||||
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
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
|
||||||
|
@ -722,6 +749,32 @@ if mobs_spawn then
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mob_class:check_despawn(pos, dtime)
|
||||||
|
self.lifetimer = self.lifetimer - dtime
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
@ -731,5 +784,18 @@ minetest.register_chatcommand("mobstats",{
|
||||||
minetest.chat_send_player(n,"total mobs:"..count_mobs_total())
|
minetest.chat_send_player(n,"total mobs:"..count_mobs_total())
|
||||||
minetest.chat_send_player(n,"spawning attempts since server start:"..dbg_spawn_attempts)
|
minetest.chat_send_player(n,"spawning attempts since server start:"..dbg_spawn_attempts)
|
||||||
minetest.chat_send_player(n,"successful spawns since server start:"..dbg_spawn_succ)
|
minetest.chat_send_player(n,"successful spawns since server start:"..dbg_spawn_succ)
|
||||||
|
|
||||||
|
|
||||||
|
local mob_counts, total_mobs = count_mobs_all()
|
||||||
|
if (total_mobs) then
|
||||||
|
minetest.log("action", "Total mobs found: " .. total_mobs)
|
||||||
|
end
|
||||||
|
if mob_counts then
|
||||||
|
for k, v1 in pairs(mob_counts) do
|
||||||
|
minetest.log("action", "k: " .. tostring(k))
|
||||||
|
minetest.log("action", "v1: " .. tostring(v1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|