Compare commits
997 Commits
chat-comma
...
master
Author | SHA1 | Date |
---|---|---|
|
46394e7e1f | |
|
b0aceae73d | |
|
9200c3fe44 | |
|
903d1777be | |
|
6ca5799565 | |
|
30528b0a2c | |
|
9919011aca | |
|
c757e98b4f | |
|
e2b22a62ce | |
|
a9804879e2 | |
|
d2a03ec0b9 | |
|
7ef6613f09 | |
|
d27109d807 | |
|
79e1e2fa8f | |
|
fdaaf0ec49 | |
|
add97d39a6 | |
|
bcb6251d20 | |
|
1483c45cc9 | |
|
642b9db035 | |
|
d7e59f6a35 | |
|
d4da855570 | |
|
30dd505985 | |
|
ae8068cca6 | |
|
bd14918543 | |
|
66c1b4a95f | |
|
96c4fb60d8 | |
|
5515e2baa1 | |
|
be2e4614d0 | |
|
7ed964756e | |
|
0e5ad8637d | |
|
0cc0a7e01d | |
|
490e40d042 | |
|
f233fb5d30 | |
|
9e4e7f0695 | |
|
2a08f31435 | |
|
740f7583ef | |
|
0564121183 | |
|
bca5033fb2 | |
|
03d22852ee | |
|
10a5f8c4bf | |
|
68810a2a74 | |
|
0b6467d679 | |
|
8979230c42 | |
|
fafa47d643 | |
|
f61143758e | |
|
fa22ec4dd0 | |
|
017bf705e9 | |
|
a7bc460fae | |
|
e82d21040c | |
|
0491b814dd | |
|
2f05388541 | |
|
976f522b9d | |
|
51dffc6c53 | |
|
30ce6f8a77 | |
|
a34ae040c8 | |
|
a410a7fabe | |
|
ea46c8741b | |
|
5b52deaa8a | |
|
7bbc1e9951 | |
|
0dd780ee2a | |
|
1b259f928b | |
|
d0d60804a3 | |
|
d1d11f9740 | |
|
4926c0560d | |
|
23ca11c8e1 | |
|
88e084cbca | |
|
be86b603f8 | |
|
f4803a35c4 | |
|
ce4c0ed4c1 | |
|
4a3a8841cd | |
|
649f481b51 | |
|
b937b38b1c | |
|
f9e7f58492 | |
|
a77e79d985 | |
|
24ca8252a9 | |
|
c6e9d763d6 | |
|
61d0dc8182 | |
|
46d1dd42d4 | |
|
39f66eb4a0 | |
|
18dd1cabd0 | |
|
e70161501f | |
|
bb6fe65aa5 | |
|
cdf6533e0a | |
|
d690797011 | |
|
6000c29171 | |
|
faff9316e0 | |
|
7c1777c53a | |
|
c2f0f0297b | |
|
6473494cbc | |
|
4d93e13f80 | |
|
47340386e2 | |
|
25f467c8ab | |
|
c17fc81d1f | |
|
8f9f5cc936 | |
|
4af5d8e5e8 | |
|
643ded06da | |
|
970988cb39 | |
|
34f329a9d5 | |
|
a4e73886d5 | |
|
148575a05b | |
|
0baffeced3 | |
|
2607d40f1f | |
|
db696d0e2b | |
|
f09923ded9 | |
|
0b9cbdb20a | |
|
c52fda154b | |
|
28b7304211 | |
|
1518eb81cb | |
|
4e0bb0d120 | |
|
5b37f56005 | |
|
aaee3b6de7 | |
|
6025a3d7d4 | |
|
bbdd8f55eb | |
|
6fd8ff8865 | |
|
11e364b3ec | |
|
4db9952a84 | |
|
fb2a501a9c | |
|
938911e7e3 | |
|
c1934c4f3a | |
|
61dccfb9e5 | |
|
90796ec7b4 | |
|
d30e014233 | |
|
19689dd857 | |
|
b78e7b3e4d | |
|
835076ea4b | |
|
797da20fa7 | |
|
c315d155e1 | |
|
0c567c7921 | |
|
64ebdd0f18 | |
|
1bd972bff7 | |
|
ea0f52763c | |
|
70425e9f30 | |
|
a0789e72f0 | |
|
e341b2a6fe | |
|
4e8e6fbb51 | |
|
efbe3c93b4 | |
|
0369465630 | |
|
30f7c638f3 | |
|
eccba76732 | |
|
f3d16d264c | |
|
da5e703675 | |
|
6466061796 | |
|
a80438d58e | |
|
47fbb0c176 | |
|
fba30eccd6 | |
|
6e7827902c | |
|
a877c615a5 | |
|
7f43ba6e36 | |
|
5edf27ac88 | |
|
c998788435 | |
|
7707c3132c | |
|
756d28e2c6 | |
|
3a422e3afc | |
|
362de4c920 | |
|
6580bcab5a | |
|
a6def5e9bb | |
|
6d22262c53 | |
|
7489010152 | |
|
d224236388 | |
|
27f35fe422 | |
|
4b1606eaee | |
|
4776487f83 | |
|
664244d25c | |
|
b9999195ec | |
|
b18e077ba3 | |
|
f3693138c8 | |
|
6e94550a12 | |
|
d85f143fde | |
|
dafe860e56 | |
|
832e634e3b | |
|
5c55ddff1e | |
|
a28bfebb9b | |
|
e5c5a78553 | |
|
1c458a2e72 | |
|
94e1d82b45 | |
|
9ccf8de606 | |
|
19b5a5aac7 | |
|
6803d22dbd | |
|
0584d16569 | |
|
03829dd518 | |
|
72ea9069bd | |
|
9188467a6a | |
|
fe91d7f3e0 | |
|
463fe2af5f | |
|
e4af02ea52 | |
|
8e3f9d2169 | |
|
a410d17306 | |
|
e9437e9e1e | |
|
e40ed91674 | |
|
16c73c1cb6 | |
|
03be736566 | |
|
8697b38d60 | |
|
e83438e42c | |
|
71f448537c | |
|
df1d8077e6 | |
|
89e90b13eb | |
|
b0cf07a020 | |
|
e36a8c5acf | |
|
b2af00db22 | |
|
e8134345d4 | |
|
e0c7e938de | |
|
3e54acce9d | |
|
47fff66a41 | |
|
ac8383dd4b | |
|
c6003398b5 | |
|
f10d579d9c | |
|
fed43586f2 | |
|
18a83fa5d7 | |
|
0e15accada | |
|
4194a76db0 | |
|
27e4bd6d09 | |
|
256de2bc36 | |
|
693a5317ef | |
|
95c4d6472b | |
|
1c192f4fbb | |
|
c22c2063ad | |
|
410b7c7844 | |
|
9f9799d96e | |
|
cd84c472dc | |
|
0bc9ab9233 | |
|
89ff666a6a | |
|
1a5339e907 | |
|
aa930dc2b8 | |
|
79463738f6 | |
|
79d8593d34 | |
|
dc8436fdf9 | |
|
f41cea71fd | |
|
5fedd914fb | |
|
a8d09338a9 | |
|
872e3e74d3 | |
|
bf62eb33fa | |
|
0da7b3fbda | |
|
3669321828 | |
|
ca086109bf | |
|
6c6d40eb9d | |
|
2b322a451f | |
|
f0af15fcd8 | |
|
af8e88f44c | |
|
2d1a43396f | |
|
df4b8e64cc | |
|
40898d3e9d | |
|
58a292a4f3 | |
|
fe62189019 | |
|
4802b610c3 | |
|
cb55c36863 | |
|
f8dcf05670 | |
|
7e0bb036f4 | |
|
664c238137 | |
|
efab5a1cf5 | |
|
d76a7daf2d | |
|
5bb57a81ad | |
|
df0c1f1dd1 | |
|
5c563d6ffd | |
|
4aabd7d9e7 | |
|
88f253bf66 | |
|
289ba826ba | |
|
4846076c8f | |
|
5c5c405ccf | |
|
65d33b935a | |
|
c05e57efb1 | |
|
e44e9eaf62 | |
|
09a68443cd | |
|
75b425ffd7 | |
|
0a9ea7e46a | |
|
a44d9643ae | |
|
a47630035d | |
|
667ef5cad5 | |
|
fef23d0b6f | |
|
a0d52010bf | |
|
44063bca93 | |
|
e58bb6d859 | |
|
31d3ea8a87 | |
|
13536baed8 | |
|
6069bb4a38 | |
|
c31c852a6e | |
|
b5f7ae5458 | |
|
9254bab971 | |
|
999b82c94a | |
|
a83dd8548f | |
|
559cf85c94 | |
|
96e8e6a86f | |
|
801d9a2571 | |
|
f2a4d6bd56 | |
|
48166625d4 | |
|
a2e0cf83bc | |
|
b364faa7c7 | |
|
60fcafeee4 | |
|
9d9e213012 | |
|
49bde37a5e | |
|
dc17cc91a3 | |
|
873a1e73dc | |
|
8fff20eec9 | |
|
ec6086d8e6 | |
|
6d7aafe0d4 | |
|
91adfcbb1d | |
|
8958aa7b12 | |
|
cf5703d528 | |
|
88e59d3592 | |
|
cd08df175c | |
|
ca277b6769 | |
|
b0127fc1c3 | |
|
d26b1b1402 | |
|
549bdeb6e9 | |
|
ecfb615c89 | |
|
960b653979 | |
|
5ceb48fcb1 | |
|
f894153b8a | |
|
848f1489e8 | |
|
a0d3f51745 | |
|
46d48ccf2f | |
|
f46c4ebad7 | |
|
b7e6db537b | |
|
357474e32f | |
|
b6eb0ab66c | |
|
d2f7d31360 | |
|
8e931e92f5 | |
|
cbfec5c5c8 | |
|
509568b4b0 | |
|
6e1758400e | |
|
c558e30ea5 | |
|
fb01e61946 | |
|
8445a9ddec | |
|
3b2a25a6cb | |
|
8f584be235 | |
|
94cbb2b802 | |
|
1471521709 | |
|
2f22ce79cc | |
|
ae2c5ede30 | |
|
f4a30959ce | |
|
b22e4ae99d | |
|
27f4c210c7 | |
|
091622f855 | |
|
5cc4fe955f | |
|
4afdea56db | |
|
0a474ee578 | |
|
d45e1c07bd | |
|
21992dc265 | |
|
a9ceeabc4b | |
|
16d79c38ce | |
|
7ff476b9b9 | |
|
c6b662ce7a | |
|
586c18b00f | |
|
61b0220951 | |
|
ef1c06e3d1 | |
|
a9ae6aa9c8 | |
|
b03cd1fc8e | |
|
cf3a2a42bf | |
|
ff8176e252 | |
|
a62d3ff2ee | |
|
8bae5dd874 | |
|
2361c1c797 | |
|
3b3922b2d6 | |
|
4987636913 | |
|
c542ff998e | |
|
f28a5b90fc | |
|
7a130ff2b9 | |
|
73927cb0cb | |
|
2624343c30 | |
|
30e2e0d70a | |
|
eea9fb7670 | |
|
64d3a28578 | |
|
822071c66b | |
|
eafb1a2797 | |
|
40f4287ff2 | |
|
f70dbb8cfa | |
|
e11dc4b58e | |
|
e81da3c4ff | |
|
a896d7f5e4 | |
|
3577585d74 | |
|
c350b5158b | |
|
ee2fa60cae | |
|
0795862c5c | |
|
99ccd9ea4c | |
|
4324e97db0 | |
|
7ae4fb6ae7 | |
|
2603c4768b | |
|
c642f9e2f7 | |
|
1e0ffed26b | |
|
ee21a24fb6 | |
|
580f6f9629 | |
|
2fac67898e | |
|
28bfb3f141 | |
|
f3322fd7cb | |
|
881f0b0a71 | |
|
e5bdf230d5 | |
|
0c21a51c45 | |
|
9706c56964 | |
|
7ae777a01d | |
|
d391d50eb8 | |
|
92c25ef691 | |
|
7464b8ff48 | |
|
a21b14e707 | |
|
dfd9737d18 | |
|
198d3bff53 | |
|
522553fcd0 | |
|
753a8ce488 | |
|
f9dbfd8eb5 | |
|
0132d0569f | |
|
a0d3e92b70 | |
|
cbe8583cef | |
|
6715aecd63 | |
|
b06bc276dc | |
|
b9fd1ac227 | |
|
7e64470f70 | |
|
06984fa486 | |
|
4bd49f7eb0 | |
|
a4fc38ffcc | |
|
c8102838cb | |
|
6b53dda79b | |
|
640b0dc485 | |
|
7804de4230 | |
|
22bda9c770 | |
|
21bdf914bf | |
|
75e263debc | |
|
a65db15b5c | |
|
95627f4304 | |
|
cd33d406b2 | |
|
ec542a142a | |
|
91ae3208bd | |
|
0d619ec6a8 | |
|
8ad953ab17 | |
|
362965f8fb | |
|
1de0009cc6 | |
|
7da32dcc36 | |
|
1929df492b | |
|
69f85a1aa6 | |
|
c260ef8c6e | |
|
5f82e76862 | |
|
3bedc81068 | |
|
b8782d0af1 | |
|
f1c6c3b285 | |
|
f8ee9c79c2 | |
|
00063cde73 | |
|
0119793d7a | |
|
138a3cd154 | |
|
c57ead6985 | |
|
772f48ed58 | |
|
079cedaa34 | |
|
1b0d6a2c2d | |
|
656891ec61 | |
|
90bab9026b | |
|
5b43303873 | |
|
211b078d3a | |
|
bb77443892 | |
|
5fc3256930 | |
|
ee9577b625 | |
|
fd387a65af | |
|
190d5cad33 | |
|
4c9eeed499 | |
|
425c7505ec | |
|
1ad1bbef13 | |
|
5ef0c5bcb7 | |
|
4a396fde2c | |
|
a4c21de153 | |
|
a27833a0e1 | |
|
d83c29f0c4 | |
|
2873c7f7ce | |
|
e797a4ec9b | |
|
6105a9ef22 | |
|
37b1191c0a | |
|
dcceafad41 | |
|
f421e07d1c | |
|
49044ac15e | |
|
9e074af07f | |
|
0ea72ed1f1 | |
|
47b6a8d7f8 | |
|
388ab6d8df | |
|
981f3d5d74 | |
|
3f3abbbee1 | |
|
42d3830c48 | |
|
8592670f9b | |
|
d3a237384d | |
|
79cf98c8e3 | |
|
00f4cfa5b5 | |
|
0cc11d0bbb | |
|
f61102279f | |
|
f5a91f7c24 | |
|
540508638c | |
|
03c9c0b830 | |
|
36f3c26139 | |
|
0d13e1ffdd | |
|
be9fff4fe2 | |
|
ad16fe7072 | |
|
dd3b5a98ae | |
|
fddaacdeae | |
|
8286fd2a4d | |
|
f76b66eec6 | |
|
aeb8fa5b88 | |
|
ba38f787e2 | |
|
66f132a645 | |
|
8b11e2fec8 | |
|
9b9ec13967 | |
|
d5a2e5e514 | |
|
dddaed42b6 | |
|
973a8201ad | |
|
91ac70cf28 | |
|
b6dd8d5c44 | |
|
335405f131 | |
|
470f7b70bc | |
|
b0a3cffad0 | |
|
9f41c6fc64 | |
|
36dc18e9da | |
|
5f8d79b37a | |
|
21b7647731 | |
|
4fd0bf2c3f | |
|
5975b20cff | |
|
58510b00ee | |
|
c19c05443e | |
|
ce9258b764 | |
|
8536e2c033 | |
|
8baea02f24 | |
|
52b1473657 | |
|
3fbcacac3b | |
|
d43b2149e6 | |
|
b814fe47f9 | |
|
962ae359af | |
|
ddc157acbf | |
|
4fd0ea8a88 | |
|
e6f72e0c60 | |
|
dc10448c6e | |
|
43f418c6c0 | |
|
21fdf492e2 | |
|
c580c1ccad | |
|
e75a2e3a67 | |
|
a9c49853aa | |
|
4bc473bc29 | |
|
8f6a97eefe | |
|
be5eb621fd | |
|
203c7e2c84 | |
|
892f4b96c9 | |
|
b7bf566190 | |
|
fdfb586b16 | |
|
ac459d3914 | |
|
621a5a53a8 | |
|
60736d04b8 | |
|
df1eeba6b6 | |
|
8ac5d32416 | |
|
402a1d0088 | |
|
b8488ce55e | |
|
f9a86947e5 | |
|
30e83088d4 | |
|
b3c0e7c1bd | |
|
bbde3b551f | |
|
56310a3624 | |
|
fcc2087b97 | |
|
a1acf6d3c1 | |
|
82396bd994 | |
|
45ab5c8e25 | |
|
b58e61b988 | |
|
a200252ab8 | |
|
0cac8f3f44 | |
|
9edcc60ba2 | |
|
0113d290b7 | |
|
e91ee174a4 | |
|
c1a717238b | |
|
a71a0e9161 | |
|
1870a89af0 | |
|
1b3763654e | |
|
84a800f22b | |
|
17202115fa | |
|
5d65c8a3aa | |
|
e9f38c6b90 | |
|
fb60bd0253 | |
|
a9bf923df9 | |
|
067affcabb | |
|
4d13eddcd6 | |
|
f7052943ec | |
|
04436ea5f7 | |
|
cb82e3c9c5 | |
|
4992a05c2b | |
|
9ae1119438 | |
|
9e095980c8 | |
|
be9fb5be47 | |
|
f4c302e582 | |
|
bcea5a35ef | |
|
86d1e0ac12 | |
|
866c097d56 | |
|
d5ec2ccbd8 | |
|
0ad03acf46 | |
|
3097df731d | |
|
ad4e86deca | |
|
052bb540ba | |
|
074e8c8389 | |
|
7e132866bf | |
|
11d700b785 | |
|
9fa51dc6b9 | |
|
6ffb7f525a | |
|
1601437510 | |
|
81ee51b0c2 | |
|
45201481c1 | |
|
f054eb1ce6 | |
|
f3c540af5d | |
|
a3d3055e8a | |
|
925e482362 | |
|
96be84161f | |
|
7b78d500fd | |
|
358ea695e1 | |
|
f6b8133381 | |
|
96aec38947 | |
|
d221e5ab0a | |
|
31b56e77ee | |
|
4bb27a0d79 | |
|
37350e81c9 | |
|
4b327bcf99 | |
|
35a2a2b912 | |
|
f53ff8418f | |
|
1bd647507b | |
|
f5b2a5f5c1 | |
|
be0cf5788d | |
|
52ebc3343f | |
|
a9817fea4b | |
|
70db02306f | |
|
0ff4780066 | |
|
f7280dc4f0 | |
|
75d9450002 | |
|
cb2bc52cbb | |
|
0d29688b13 | |
|
78ce97aaf6 | |
|
3b3d67a99c | |
|
7513bc600a | |
|
f96258ca5c | |
|
33c6e5cdad | |
|
2c5eca3999 | |
|
2c434495c1 | |
|
f0ec64528c | |
|
cbb014ed38 | |
|
74e3c6555d | |
|
ecc2010fe5 | |
|
5bb1657bd8 | |
|
f063d14caf | |
|
02da1abcf9 | |
|
0d287001f7 | |
|
f528b31d48 | |
|
a77f99a1ec | |
|
45965c0799 | |
|
1eaf662833 | |
|
63d4e57a8d | |
|
ecea8f1662 | |
|
ef08977112 | |
|
1f87874870 | |
|
9a3ae17564 | |
|
bd74dbe321 | |
|
ca9cd8cbe0 | |
|
d9a670dcb8 | |
|
f22b3fd457 | |
|
1079ab74d4 | |
|
e6b61a1551 | |
|
f1229c5401 | |
|
2228b30658 | |
|
e11185638f | |
|
1ed6aeca5e | |
|
5d3e74c844 | |
|
c6e80b5882 | |
|
1c05461e67 | |
|
ee749c874b | |
|
8cccbd7caa | |
|
ae1212be7f | |
|
7e3dd715af | |
|
55177c03ec | |
|
b74078a06c | |
|
e19d39f065 | |
|
397a05b651 | |
|
5b3a156c09 | |
|
604af04849 | |
|
0e8c002665 | |
|
77597f9bac | |
|
65846cb6d8 | |
|
97e50714fe | |
|
9666076cb4 | |
|
7f1cb7fc54 | |
|
87b5a7b7ad | |
|
d74e1fd162 | |
|
0e52e09cc8 | |
|
ffd28c259d | |
|
ab4b6d214e | |
|
f8b9f16799 | |
|
404097dcc0 | |
|
db78c19880 | |
|
7be749a122 | |
|
fed1410b7f | |
|
8b64022bfd | |
|
71ff38add8 | |
|
6550e3e8e2 | |
|
87e41cc9a9 | |
|
ec08032b62 | |
|
199488cc74 | |
|
33c0aa23c5 | |
|
d29c71c69a | |
|
6fac49550e | |
|
cf46f0d8b8 | |
|
e0c94ccb8a | |
|
a6ac6f5c76 | |
|
32c03dc27e | |
|
3195df3864 | |
|
f1b7476825 | |
|
7a00e8acf7 | |
|
13a0fa2314 | |
|
61c6d6e276 | |
|
528f5e6b86 | |
|
d0f4dd4309 | |
|
61e4db6bcf | |
|
ae83e44149 | |
|
819d59dbe7 | |
|
173f67870e | |
|
0a2fcdc4e8 | |
|
79aac4c494 | |
|
a41aace9d7 | |
|
9f015f2c58 | |
|
72e53a82d7 | |
|
0867d6e406 | |
|
347cc7ee7c | |
|
6dc6fc8ec3 | |
|
b43f59332a | |
|
3b1d71382f | |
|
05be20fcb8 | |
|
5df1df8e21 | |
|
fed4596cb4 | |
|
a77a916cc2 | |
|
8a59d7b9c6 | |
|
f6d34db76e | |
|
8df9fe76d8 | |
|
a2983e1fda | |
|
e710795eed | |
|
4d2210c4e5 | |
|
749f1a4a62 | |
|
e5a44dcb75 | |
|
dd68b79782 | |
|
aac9d6a105 | |
|
dada1e9815 | |
|
8a78b1f8ef | |
|
edc89898bb | |
|
fede04eaa6 | |
|
57662b319c | |
|
3e3f53efbd | |
|
97e69e04aa | |
|
cab2167520 | |
|
b2407e407a | |
|
aeaec68c1b | |
|
302175691a | |
|
228759e49b | |
|
eeaf4e09c6 | |
|
29001086c3 | |
|
1f07f5ba17 | |
|
095128b6b3 | |
|
6aecae6eea | |
|
bcf5eef010 | |
|
3917bb51a2 | |
|
dd0e381e39 | |
|
939229cb21 | |
|
58d67aace6 | |
|
4975a516f5 | |
|
c0ce511ad0 | |
|
ddb7e8388a | |
|
9eba2add3f | |
|
b93d3cf93c | |
|
a038908acd | |
|
a613da65c5 | |
|
642351a1d2 | |
|
18903d5a09 | |
|
c6a07b8007 | |
|
67780f2159 | |
|
7e9eb927f1 | |
|
ae7ea8edef | |
|
0cf58ce98d | |
|
0ebf6b8f92 | |
|
0a8b6a63cb | |
|
73cd482e55 | |
|
414adff8fb | |
|
7f3e5bda7e | |
|
a03e7f6f3a | |
|
62ed39bd76 | |
|
a836ac9879 | |
|
827cd2d7b1 | |
|
78355c5c57 | |
|
50b6f03977 | |
|
3669ccf907 | |
|
818f33ff74 | |
|
038066f0c5 | |
|
c46c07f836 | |
|
62adbb9625 | |
|
f9c2d710e2 | |
|
222104b3cb | |
|
9f7b371bfc | |
|
8b23388d63 | |
|
acbdc937b4 | |
|
74a3b2654f | |
|
dccb71e2fb | |
|
1e7ba2b605 | |
|
fe5f61c747 | |
|
1e5ae0bbac | |
|
c7d91baadf | |
|
9c3fcae9e1 | |
|
4c25091430 | |
|
ca74f23094 | |
|
7a5ce1c717 | |
|
e8c15867ad | |
|
20a4e038f5 | |
|
c80b1c5140 | |
|
7efc177188 | |
|
d8d4cb5def | |
|
52a49e640e | |
|
69485f8505 | |
|
0c5ca70187 | |
|
ec1ef1affa | |
|
c03717139b | |
|
44a25de12f | |
|
aa533edda2 | |
|
c9b4ddb923 | |
|
53b0ad7347 | |
|
d9195cc520 | |
|
49f6ccaa4a | |
|
3668b2dee6 | |
|
a2c3eb95bb | |
|
d08a226a51 | |
|
9bc2f2d866 | |
|
2827542002 | |
|
3ad5b30ea5 | |
|
1cf53caa7a | |
|
d1198e8d74 | |
|
4f0bb444fe | |
|
f0d7715080 | |
|
5d9c3cd85b | |
|
956dab78ce | |
|
fc9928c0b6 | |
|
573b1dc44b | |
|
dd69dcfd9f | |
|
76271199c6 | |
|
11b66e83a2 | |
|
4aa6421faf | |
|
f967aa3d51 | |
|
a7e102426a | |
|
bcd058feb1 | |
|
baebe3c2d2 | |
|
205572cc69 | |
|
f8b54db392 | |
|
bbd115fde0 | |
|
7c7af7d664 | |
|
f79e12c8ae | |
|
5c928575f8 | |
|
8e37b51cac | |
|
6724a8d0ed | |
|
06699c2cc4 | |
|
6bbea11fb8 | |
|
c7c47c1ca7 | |
|
d952423dda | |
|
4d515e95c6 | |
|
ca75a893db | |
|
6a55136517 | |
|
02f393998f | |
|
22a2fdbf5f | |
|
5f076d372a | |
|
bd3c08d367 | |
|
10f81dbf0c | |
|
01c79c5a18 | |
|
095f78b785 | |
|
457f61a532 | |
|
1a58b0c510 | |
|
3dd490ed6b | |
|
5d5ed91519 | |
|
2d6e0241cd | |
|
1df916bd7b | |
|
044e9d22ad | |
|
31dfeae445 | |
|
cd04f6d7ce | |
|
60e74fc41d | |
|
a602f64ea5 | |
|
9feaec95d9 | |
|
0ca6c06198 | |
|
b497778192 | |
|
b1f7294578 | |
|
f0fab4e708 | |
|
0c5096db13 | |
|
3fbeab7c85 | |
|
d865d95639 | |
|
a8760c0375 | |
|
54f81b7740 | |
|
bbffda3635 | |
|
2575e20e7e | |
|
5fe30d7287 | |
|
bffd032a8b | |
|
e4c8ba795a | |
|
2a06be02b3 | |
|
8931ffb7d3 | |
|
727d7be6ab | |
|
ec6d68322a | |
|
18cdd13c06 | |
|
9c75cd1a79 | |
|
a3af1cdf6e | |
|
4e37cc114c | |
|
5d9bb7cacd | |
|
49e7def70a | |
|
ad3defdfb3 | |
|
c41bd3932c | |
|
875bb3db84 | |
|
76af838f4c | |
|
46b1762872 | |
|
a069406f40 | |
|
97f8c92d95 | |
|
963adfb4e2 | |
|
6d0abb2973 | |
|
e74838136d | |
|
d0ddd38d4e | |
|
ce0148d9a8 | |
|
36391d8583 | |
|
5b93ee5e40 | |
|
8d22102c66 | |
|
a93562a60c | |
|
399ed85717 | |
|
67c1bc973a | |
|
1c50f9aed6 | |
|
311c4dc3fb | |
|
94e7bbfb80 | |
|
7a14d1d593 | |
|
bc2a79cddf | |
|
52d7ffa78f | |
|
f768972469 | |
|
9c68528b8b | |
|
1597e093b5 | |
|
f15a28109c | |
|
81d0ce5ea7 | |
|
f498d3ae02 | |
|
c6de8c0669 | |
|
e334a71361 | |
|
67fff50fc4 | |
|
e96eb79a3f | |
|
de3f236bf2 | |
|
940621b676 | |
|
9519c8ebaa | |
|
28719264df | |
|
4ac115cf90 | |
|
1f4ac3d8e0 | |
|
cd0d2d2296 | |
|
3077498a63 | |
|
84764389dc | |
|
3a88ff7cb3 | |
|
78d387e2df | |
|
0de2d3d707 | |
|
8934c65564 | |
|
421ab9f660 | |
|
422385ad02 | |
|
20bd91a14e | |
|
5ddd1d0193 | |
|
037832a177 | |
|
424685d3d3 | |
|
51711385a1 | |
|
9472f86025 | |
|
5028728fbc | |
|
6f770bc962 | |
|
6887948718 | |
|
f540bc2bb1 | |
|
5fb7913355 | |
|
d3a3253563 | |
|
c3868084af | |
|
6fb94fb411 | |
|
883bd0f437 | |
|
42d2dfed19 | |
|
33f22f4eed | |
|
cec4011de2 | |
|
38d788998d | |
|
9e41d5874a | |
|
e0feba115d | |
|
1be74d21d3 | |
|
b914e088cc | |
|
32e33d3b21 | |
|
7d674e6e72 | |
|
ec2e5f7430 | |
|
7a4d009648 | |
|
8f9f4f6093 | |
|
38741754e0 | |
|
f1ce4f0df6 | |
|
a598d94afd | |
|
d553aa5e8c | |
|
23e52975fd | |
|
baed298ce6 | |
|
6c5c4b0080 | |
|
b14b08dc30 | |
|
2b6f31fe3b | |
|
321caa0632 | |
|
6f3bd95106 | |
|
a3b46be64a | |
|
8e4ba0d3e1 | |
|
a7c8bfc0d7 | |
|
504a0e5d2c | |
|
99c20421f4 | |
|
cd8e695ca5 | |
|
2bf2766688 | |
|
7b4eb28160 | |
|
2221f89696 | |
|
50b209a945 | |
|
034a29edc0 | |
|
958fee0a73 | |
|
d50e63d935 | |
|
1fd869b5fb | |
|
9f7f1f2c89 | |
|
ee28bda893 | |
|
51f121d516 | |
|
1e17989ef2 |
|
@ -0,0 +1,2 @@
|
||||||
|
# Text Editor TMP Files
|
||||||
|
*.swp
|
|
@ -0,0 +1,55 @@
|
||||||
|
unused_args = false
|
||||||
|
allow_defined_top = true
|
||||||
|
max_line_length = false
|
||||||
|
redefined = false
|
||||||
|
|
||||||
|
globals = {
|
||||||
|
"minetest", "core",
|
||||||
|
}
|
||||||
|
|
||||||
|
read_globals = {
|
||||||
|
"DIR_DELIM",
|
||||||
|
"dump", "dump2",
|
||||||
|
"vector",
|
||||||
|
"VoxelManip", "VoxelArea",
|
||||||
|
"PseudoRandom", "PcgRandom", "PerlinNoise", "PerlinNoiseMap",
|
||||||
|
"ItemStack",
|
||||||
|
"Settings",
|
||||||
|
"unpack",
|
||||||
|
|
||||||
|
table = {
|
||||||
|
fields = {
|
||||||
|
"copy",
|
||||||
|
"indexof",
|
||||||
|
"insert_all",
|
||||||
|
"key_value_swap",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
string = {
|
||||||
|
fields = {
|
||||||
|
"split",
|
||||||
|
"trim",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
math = {
|
||||||
|
fields = {
|
||||||
|
"hypot",
|
||||||
|
"sign",
|
||||||
|
"factorial"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
------
|
||||||
|
--MODS
|
||||||
|
------
|
||||||
|
|
||||||
|
--GENERAL
|
||||||
|
"default",
|
||||||
|
|
||||||
|
--ENTITIES
|
||||||
|
"cmi",
|
||||||
|
|
||||||
|
--HUD
|
||||||
|
"sfinv", "sfinv_buttons", "unified_inventory", "cmsg", "inventory_plus",
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
eliasfleckenstein@web.de.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
452
CONTRIBUTING.md
|
@ -1,96 +1,414 @@
|
||||||
# Contributing to MineClone 2
|
# Contributing to MineClone2
|
||||||
So you want to MineClone 2?
|
So you want to contribute to MineClone2?
|
||||||
Wow, thank you! :-)
|
Wow, thank you! :-)
|
||||||
|
|
||||||
But first, some things to note:
|
MineClone2 is maintained by Nicu and Fleckenstein. If you have any
|
||||||
|
problems or questions, contact us (See Links section below).
|
||||||
|
|
||||||
MineClone 2's development target is to make a free software clone of Minecraft,
|
You can help with MineClone2's development in many different ways,
|
||||||
***version 1.12***, ***PC edition***, *** + Optifine features supported by the Minetest Engine ***.
|
whether you're a programmer or not.
|
||||||
|
|
||||||
MineClone 2 is maintained by two persons. Namely, kay27 and EliasFleckenstein. You can find us
|
## MineClone2's development target is to...
|
||||||
in the Minetest forums (forums.minetest.net), in IRC in the #minetest
|
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||||
channel on irc.freenode.net. And finally, you can send e-mails to
|
based on the Minetest engine with polished features, usable in both
|
||||||
<eliasfleckenstein@web.de> or <kay27@bk.ru>.
|
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||||
|
Edition 1.12.2** features are already implemented and polishing existing
|
||||||
|
features are prioritized over new feature requests.
|
||||||
|
- With lessened priority yet strictly, implement features targetting
|
||||||
|
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||||
|
by the Minetest Engine). This means features in parity with the listed
|
||||||
|
Minecraft experiences are prioritized over those that don't fulfill this
|
||||||
|
scope.
|
||||||
|
- Optionally, create a performant experience that will run relatively
|
||||||
|
well on really low spec computers. Unfortunately, due to Minecraft's
|
||||||
|
mechanisms and Minetest engine's limitations along with a very small
|
||||||
|
playerbase on low spec computers, optimizations are hard to investigate.
|
||||||
|
|
||||||
There is **no** guarantee we will accept anything from anybody.
|
## Links
|
||||||
|
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
||||||
|
* [Discord](https://discord.gg/xE4z8EEpDC)
|
||||||
|
* [YouTube](https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A)
|
||||||
|
* [IRC](https://web.libera.chat/#mineclone2)
|
||||||
|
* [Matrix](https://app.element.io/#/room/#mc2:matrix.org)
|
||||||
|
* [Reddit](https://www.reddit.com/r/MineClone2/)
|
||||||
|
* [Minetest forums](https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
||||||
|
* [ContentDB](https://content.minetest.net/packages/wuzzy/mineclone2/)
|
||||||
|
* [OpenCollective](https://opencollective.com/mineclone2)
|
||||||
|
|
||||||
By sending us patches or asking us to include your changes in this game,
|
## Using git
|
||||||
you agree that they fall under the terms of the LGPLv2.1, which basically
|
MineClone2 is developed using the version control system
|
||||||
means they will become part of a free software.
|
[git](https://git-scm.com/). If you want to contribute code to the
|
||||||
|
project, it is **highly recommended** that you learn the git basics.
|
||||||
|
For non-programmers and people who do not plan to contribute code to
|
||||||
|
MineClone2, git is not required. However, git is a tool that will be
|
||||||
|
referenced frequently because of its usefulness. As such, it is valuable
|
||||||
|
in learning how git works and its terminology. It can also help you
|
||||||
|
keeping your game updated, and easily test pull requests.
|
||||||
|
|
||||||
## The suggested workflow
|
## How you can help as a non-programmer
|
||||||
We don't **dictate** your workflow, but in order to work with us in an efficient
|
|
||||||
way, you can follow these suggestions:
|
|
||||||
|
|
||||||
For small and medium changes:
|
As someone who does not know how to write programs in Lua or does not
|
||||||
|
know how to use the Minetest API, you can still help us out a lot. For
|
||||||
|
example, by opening an issue in the
|
||||||
|
[Issue tracker](https://git.minetest.land/MineClone2/MineClone2/issues),
|
||||||
|
you can report a bug or request a feature.
|
||||||
|
|
||||||
* Fork the repository
|
### Rules about both bugs and feature requests
|
||||||
|
* Stay polite towards the developers and anyone else involved in the
|
||||||
|
discussion.
|
||||||
|
* Choose a descriptive title (e.g. not just "crash", "bug" or "question"
|
||||||
|
).
|
||||||
|
* Please write in plain, understandable English. It will be easier to
|
||||||
|
communicate.
|
||||||
|
* Please start the issue title with a capital letter.
|
||||||
|
* Always check the currently opened issues before creating a new one.
|
||||||
|
Don't report bugs that have already been reported or request features
|
||||||
|
that already have been requested.
|
||||||
|
* If you know about Minetest's inner workings, please think about
|
||||||
|
whether the bug / the feature that you are reporting / requesting is
|
||||||
|
actually an issue with Minetest itself, and if it is, head to the
|
||||||
|
[Minetest issue tracker](https://github.com/minetest/minetest/issues)
|
||||||
|
instead.
|
||||||
|
* If you need any help regarding creating a Mesehub account or opening
|
||||||
|
an issue, feel free to ask on the Discord / Matrix server or the IRC
|
||||||
|
channel.
|
||||||
|
|
||||||
|
### Reporting bugs
|
||||||
|
* A bug is an unintended behavior or, in the worst case, a crash.
|
||||||
|
However, it is not a bug if you believe something is missing in the
|
||||||
|
game. In this case, please read "Requesting features"
|
||||||
|
* If you report a crash, always include the error message. If you play
|
||||||
|
in singleplayer, post a screenshot of the message that Minetest showed
|
||||||
|
when the crash happened (or copy the message into your issue). If you
|
||||||
|
are a server admin, you can find error messages in the log file of the
|
||||||
|
server.
|
||||||
|
* Tell us which MineClone2 and Minetest versions you are using.
|
||||||
|
* Tell us how to reproduce the problem: What you were doing to trigger
|
||||||
|
the bug, e.g. before the crash happened or what causes the faulty
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
### Requesting features
|
||||||
|
* Ensure the requested feature fulfills our development targets and
|
||||||
|
goals.
|
||||||
|
* Begging or excessive attention seeking does not help us in the
|
||||||
|
slightest, and may very well disrupt MineClone2 development. It's better
|
||||||
|
to put that energy into helping or researching the feature in question.
|
||||||
|
After all, we're just volunteers working on our spare time.
|
||||||
|
* Ensure the requested feature has not been implemented in MineClone2
|
||||||
|
latest or development versions.
|
||||||
|
|
||||||
|
### Testing code
|
||||||
|
If you want to help us with speeding up MineClone2 development and
|
||||||
|
making the game more stable, a great way to do that is by testing out
|
||||||
|
new features from contributors. For most new things that get into the
|
||||||
|
game, a pull request is created. A pull request is essentially a
|
||||||
|
programmer saying "Look, I modified the game, please apply my changes
|
||||||
|
to the upstream version of the game". However, every programmer makes
|
||||||
|
mistakes sometimes, some of which are hard to spot. You can help by
|
||||||
|
downloading this modified version of the game and trying it out - then
|
||||||
|
tell us if the code works as expected without any issues. Ideally, you
|
||||||
|
would report issues will pull requests similar to when you were
|
||||||
|
reporting bugs that are the mainline (See Reporting bugs section). You
|
||||||
|
can find currently open pull requests here:
|
||||||
|
<https://git.minetest.land/MineClone2/MineClone2/pulls>. Note that pull
|
||||||
|
requests that start with a `WIP:` are not done yet, and therefore might
|
||||||
|
not work, so it's not very useful to try them out yet.
|
||||||
|
|
||||||
|
### Contributing assets
|
||||||
|
Due to license problems, MineClone2 unfortunately cannot use
|
||||||
|
Minecraft's assets, therefore we are always looking for asset
|
||||||
|
contributions. To contribute assets, it can be useful to learn git
|
||||||
|
basics and read the section for Programmers of this document, however
|
||||||
|
this is not required. It's also a good idea to join the Discord server
|
||||||
|
(or alternatively IRC or Matrix).
|
||||||
|
|
||||||
|
#### Textures
|
||||||
|
For textures we use the Pixel Perfection texture pack. This is mostly
|
||||||
|
enough; however in some cases - e.g. for newer Minecraft features, it's
|
||||||
|
useful to have texture artists around. If you want to make such
|
||||||
|
contributions, join our Discord server. Demands for textures will be
|
||||||
|
communicated there.
|
||||||
|
|
||||||
|
#### Sounds
|
||||||
|
MineClone2 currently does not have a consistent way to handle sounds.
|
||||||
|
The sounds in the game come from different sources, like the SnowZone
|
||||||
|
resource pack or minetest_game. Unfortunately, MineClone2 does not play
|
||||||
|
a sound in every situation you would get one in Minecraft. Any help with
|
||||||
|
sounds is greatly appreciated, however if you add new sounds you should
|
||||||
|
probably work together with a programmer, to write the code to actually
|
||||||
|
play these sounds in game.
|
||||||
|
|
||||||
|
#### 3D Models
|
||||||
|
Most of the 3D Models in MineClone2 come from
|
||||||
|
[22i's repository](https://github.com/22i/minecraft-voxel-blender-models).
|
||||||
|
Similar to the textures, we need people that can make 3D Models with
|
||||||
|
Blender on demand. Many of the models have to be patched, some new
|
||||||
|
animations have to be added etc.
|
||||||
|
|
||||||
|
#### Crediting
|
||||||
|
Asset contributions will be credited in their own respective sections in
|
||||||
|
CREDITS.md. If you have commited the results yourself, you will also be
|
||||||
|
credited in the Contributors section.
|
||||||
|
|
||||||
|
### Contributing Translations
|
||||||
|
|
||||||
|
#### Workflow
|
||||||
|
To add/update support for your language to MineClone2, you should take
|
||||||
|
the steps documented in the section for Programmers, add/update the
|
||||||
|
translation files of the mods that you want to update. You can add
|
||||||
|
support for all mods, just some of them or only one mod; you can update
|
||||||
|
the translation file entirely or only partly; basically any effort is
|
||||||
|
valued. If your changes are small, you can also send them to developers
|
||||||
|
via E-Mail, Discord, IRC or Matrix - they will credit you appropriately.
|
||||||
|
|
||||||
|
#### Things to note
|
||||||
|
You can use the script at `tools/check_translate_files.py` to compare
|
||||||
|
the translation files for the language you are working on with the
|
||||||
|
template files, to see what is missing and what is out of date with
|
||||||
|
the template file. However, template files are often incomplete and/or
|
||||||
|
out of date, sometimes they don't match the code. You can update the
|
||||||
|
translation files if that is required, you can also modify the code in
|
||||||
|
your translation PR if it's related to translation. You can also work on
|
||||||
|
multiple languages at the same time in one PR.
|
||||||
|
|
||||||
|
#### Crediting
|
||||||
|
Translation contributions will be credited in their own in CREDITS.md.
|
||||||
|
If you have commited the results yourself, you will also be credited in
|
||||||
|
the Contributors section.
|
||||||
|
|
||||||
|
### Profiling
|
||||||
|
If you own a server, a great way to help us improve MineClone2's code
|
||||||
|
is by giving us profiler results. Profiler results give us detailed
|
||||||
|
information about the game's performance and let us know places to
|
||||||
|
investigate optimization issues. This way we can make the game faster.
|
||||||
|
|
||||||
|
#### Using Minetest's profiler
|
||||||
|
Minetest has a built in profiler. Simply set `profiler.load = true` in
|
||||||
|
your configuration file and restart the server. After running the server
|
||||||
|
for some time, just run `/profiler save` in chat - then you will find a
|
||||||
|
file in the world directory containing the results. Open a new issue and
|
||||||
|
upload the file. You can name the issue "<Server name> profiler
|
||||||
|
results".
|
||||||
|
|
||||||
|
### Let us know your opinion
|
||||||
|
It is always encouraged to actively contribute to issue discussions on
|
||||||
|
MeseHub, let us know what you think about a topic and help us make
|
||||||
|
decisions. Also, note that a lot of discussion takes place on the
|
||||||
|
Discord server, so it's definitely worth checking it out.
|
||||||
|
|
||||||
|
### Funding
|
||||||
|
You can help pay for our infrastructure (Mesehub) by donating to our
|
||||||
|
OpenCollective link (See Links section).
|
||||||
|
|
||||||
|
### Crediting
|
||||||
|
If you opened or have contributed to an issue, you receive the
|
||||||
|
`Community` role on our Discord (after asking for it).
|
||||||
|
OpenCollective Funders are credited in their own section in
|
||||||
|
`CREDITS.md` and receive a special role "Funder" on our discord (unless
|
||||||
|
they have made their donation Incognito).
|
||||||
|
|
||||||
|
## How you can help as a programmer
|
||||||
|
(Almost) all the MineClone2 development is done using pull requests.
|
||||||
|
|
||||||
|
### Recommended workflow
|
||||||
|
* Fork the repository (in case you have not already)
|
||||||
* Do your change in a new branch
|
* Do your change in a new branch
|
||||||
* Upload the repository somewhere where it can be accessed from the Internet and
|
* Create a pull request to get your changes merged into master
|
||||||
notify us
|
* Keep your pull request up to date by regularly merging upstream. It is
|
||||||
|
imperative that conflicts are resolved prior to merging the pull
|
||||||
|
request.
|
||||||
|
* After the pull request got merged, you can delete the branch
|
||||||
|
|
||||||
For small changes, sending us a patch is also good.
|
### Discuss first
|
||||||
|
If you feel like a problem needs to fixed or you want to make a new
|
||||||
|
feature, you could start writing the code right away and notifying us
|
||||||
|
when you're done, but it never hurts to discuss things first. If there
|
||||||
|
is no issue on the topic, open one. If there is an issue, tell us that
|
||||||
|
you'd like to take care of it, to avoid duplicate work.
|
||||||
|
|
||||||
For big changes: Same as above, but consider notifying us first to avoid
|
### Don't hesitate to ask for help
|
||||||
duplicate work and possible tears of rejection. ;-)
|
We appreciate any contributing effort to MineClone2. If you are a
|
||||||
|
relatively new programmer, you can reach us on Discord, Matrix or IRC
|
||||||
|
for questions about git, Lua, Minetest API, MineClone2 codebase or
|
||||||
|
anything related to MineClone2. We can help you avoid writing code that
|
||||||
|
would be deemed inadequate, or help you become familiar with MineClone2
|
||||||
|
better, or assist you use development tools.
|
||||||
|
|
||||||
For trusted people, we might give them direct commit access to this
|
### Maintain your own code, even if already got merged
|
||||||
repository. In this case, you obviously don't need to fork, but you still
|
Sometimes, your code may cause crashes or bugs - we try to avoid such
|
||||||
need to show your contributions align with the project goals. We still
|
scenarios by testing every time before merging it, but if your merged
|
||||||
reserve the right to revert everything that we don't like.
|
work causes problems, we ask you fix the issues as soon as possible.
|
||||||
For bigger changes, we strongly recommend to use feature branches and
|
|
||||||
discuss with me first.
|
|
||||||
|
|
||||||
Contributors will be credited in `README.md`.
|
### Changing Gameplay
|
||||||
|
Pull Requests that change gameplay have to be properly researched and
|
||||||
|
need to state their sources. These PRs also need Fleckenstein's approval
|
||||||
|
before they are merged.
|
||||||
|
You can use these sources:
|
||||||
|
|
||||||
## Quality remarks
|
* Minecraft code (Name the source file and line, however DONT post any
|
||||||
Again: There is ***no*** guarantee we will accept anything from anybody.
|
proprietary code). You can use
|
||||||
But we will gladly take in code from others when we feel it saves us work
|
[MCP](https://minecraft.fandom.com/wiki/Programs_and_editors/Mod_Coder_Pack)
|
||||||
in the long run.
|
to decompile Minecraft or look at
|
||||||
|
[Minestorm](https://github.com/Minestom/Minestom) code.
|
||||||
|
* Testing things inside of Minecraft (Attach screenshots / video footage
|
||||||
|
of the results)
|
||||||
|
* [Official Minecraft Wiki](https://minecraft.fandom.com/wiki/Minecraft_Wiki)
|
||||||
|
(Include a link to the specific page you used)
|
||||||
|
|
||||||
### Inclusion criteria
|
### Stick to our guidelines
|
||||||
Depending on what you add, the chances for inclusion vary:
|
|
||||||
|
|
||||||
### High chance for inclusion
|
#### Git Guidelines
|
||||||
* Gameplay features in Minecraft which are missing in MineClone 2
|
* We use merge rather than rebase or squash merge
|
||||||
|
* We don't use git submodules.
|
||||||
|
* Your commit names should be relatively descriptive, e.g. when saying
|
||||||
|
"Fix #issueid", the commit message should also contain the title of the
|
||||||
|
issue.
|
||||||
|
* Try to keep your commits as atomic as possible (advise, but completely
|
||||||
|
optional)
|
||||||
|
|
||||||
### Medium chance for inclusion (discuss first)
|
#### Code Guidelines
|
||||||
* Features which don't a impact on gameplay
|
* Each mod must provide `mod.conf`.
|
||||||
* GUI improvement
|
* Mod names are snake case, and newly added mods start with `mcl_`, e.g.
|
||||||
* Features from pocket or console edition
|
`mcl_core`, `mcl_farming`, `mcl_monster_eggs`. Keep in mind Minetest
|
||||||
|
does not support capital letters in mod names.
|
||||||
|
* To export functions, store them inside a global table named like the
|
||||||
|
mod, e.g.
|
||||||
|
|
||||||
### Low chance for inclusion (discuss/optimize first)
|
```lua
|
||||||
* Overhaul of architecture / mod structure
|
mcl_example = {}
|
||||||
* Mass-itemstring changes all over the place
|
|
||||||
* Added files have a unusual high file size
|
|
||||||
* Indentation looks like crazy
|
|
||||||
* Single commits which add several unrelated things
|
|
||||||
* Gameplay features which don't exist in Minecraft
|
|
||||||
|
|
||||||
### Instant rejection
|
function mcl_example.do_something()
|
||||||
* Proprietary **anything**
|
-- ...
|
||||||
* Code contains `minetest.env` anywhere
|
end
|
||||||
|
|
||||||
## Coding style guide
|
```
|
||||||
* Indentations should reflect the code flow
|
|
||||||
* Use tabs, not spaces for indentation (tab size = 8)
|
|
||||||
* Never use `minetest.env`
|
|
||||||
|
|
||||||
## Reporting bugs
|
* Public functions should not use self references but rather just access
|
||||||
Report all bugs and missing Minecraft features here:
|
the table directly, e.g.
|
||||||
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
```lua
|
||||||
|
-- bad
|
||||||
|
function mcl_example:do_something()
|
||||||
|
end
|
||||||
|
|
||||||
## Direct discussion
|
-- good
|
||||||
We have an IRC channel! Join us on #mineclone2 in freenode.net.
|
function mcl_example.do_something()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
<ircs://irc.freenode.net:6697/#mineclone2>
|
* Use modern Minetest API, e.g. no usage of `minetest.env`
|
||||||
|
* Tabs should be used for indent, spaces for alignment, e.g.
|
||||||
|
|
||||||
## Creating releases
|
```lua
|
||||||
|
|
||||||
|
-- use tabs for indent
|
||||||
|
|
||||||
|
for i = 1, 10 do
|
||||||
|
if i % 3 == 0 then
|
||||||
|
print(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- use tabs for indent and spaces to align things
|
||||||
|
|
||||||
|
some_table = {
|
||||||
|
{"a string", 5},
|
||||||
|
{"a very much longer string", 10},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Use double quotes for strings, e.g. `"asdf"` rather than `'asdf'`
|
||||||
|
* Use snake_case rather than CamelCase, e.g. `my_function` rather than
|
||||||
|
`MyFunction`
|
||||||
|
* Don't declare functions as an assignment, e.g.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- bad
|
||||||
|
local some_local_func = function()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
|
my_mod.some_func = function()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
|
-- good
|
||||||
|
local function some_local_func()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
|
function my_mod.some_func()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Developer status
|
||||||
|
Active and trusted contributors are often granted write access to the
|
||||||
|
MineClone2 repository.
|
||||||
|
|
||||||
|
#### Developer responsibilities
|
||||||
|
- You should not push things directly to
|
||||||
|
MineClone2 master - rather, do your work on a branch on your private
|
||||||
|
repository, then create a pull request. This way other people can review
|
||||||
|
your changes and make sure they work before they get merged.
|
||||||
|
- Merge PRs only when they have recieved the necessary feedback and have
|
||||||
|
been tested by at least two different people (including the author of
|
||||||
|
the pull request), to avoid crashes or the introduction of new bugs.
|
||||||
|
- You may also be assigned to issues or pull
|
||||||
|
requests as a developer. In this case it is your responsibility to fix
|
||||||
|
the issue / review and merge the pull request when it is ready. You can
|
||||||
|
also unassign yourself from the issue / PR if you have no time or don't
|
||||||
|
want to take care of it for some other reason. After all, everyone is a
|
||||||
|
volunteer and we can't expect you to do work that you are not interested
|
||||||
|
in. **The important thing is that you make sure to inform us if you
|
||||||
|
won't take care of something that has been assigned to you.**
|
||||||
|
- Please assign yourself to something that you want to work on to avoid
|
||||||
|
duplicate work.
|
||||||
|
- As a developer, it should be easy to reach you about your work. You
|
||||||
|
should be in at least one of the public MineClone2 discussion rooms -
|
||||||
|
preferrably Discord, but if you really don't like Discord, Matrix
|
||||||
|
or IRC are fine too.
|
||||||
|
|
||||||
|
### Maintainer status
|
||||||
|
Maintainers carry the main responsibility for the project.
|
||||||
|
|
||||||
|
#### Maintainer responsibilities
|
||||||
|
- Making sure issues are addressed and pull requests are reviewed and
|
||||||
|
merged, by assigning either themselves or Developers to issues / PRs
|
||||||
|
- Making releases
|
||||||
|
- Making sure guidelines are kept
|
||||||
|
- Making project decisions based on community feedback
|
||||||
|
- Granting/revoking developer access
|
||||||
|
- Enforcing the code of conduct (See CODE_OF_CONDUCT.md)
|
||||||
|
- Moderating official community spaces (See Links section)
|
||||||
|
- Resolving conflicts and problems within the community
|
||||||
|
|
||||||
|
#### Current maintainers
|
||||||
|
* Fleckenstein - responsible for gameplay review, publishing releases,
|
||||||
|
technical guidelines and issue/PR delegation
|
||||||
|
* Nicu - responsible for community related issues
|
||||||
|
|
||||||
|
#### Release process
|
||||||
|
* Run `tools/generate_ingame_credits.lua` to update the ingame credits
|
||||||
|
from `CREDITS.md` and commit the result (if anything changed)
|
||||||
* Launch MineClone2 to make sure it still runs
|
* Launch MineClone2 to make sure it still runs
|
||||||
* Update the version number in README.md
|
* Update the version number in README.md
|
||||||
* Use `git tag <version number>` to tag the latest commit with the version number
|
* Use `git tag <version number>` to tag the latest commit with the
|
||||||
* Push to repo (don't forget `--tags`!)
|
version number
|
||||||
* Update ContentDB (https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
* Push to repository (don't forget `--tags`!)
|
||||||
* Update first post in forum thread (https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
* Update ContentDB
|
||||||
|
(https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
||||||
|
* Update first post in forum thread
|
||||||
|
(https://forum.minetest.net/viewtopic.php?f=50&t=16407)
|
||||||
* Post release announcement and changelog in forums
|
* Post release announcement and changelog in forums
|
||||||
|
|
||||||
|
### Licensing
|
||||||
|
By asking us to include your changes in this game, you agree that they
|
||||||
|
fall under the terms of the GPLv3, which basically means they will
|
||||||
|
become part of a free/libre software.
|
||||||
|
|
||||||
|
### Crediting
|
||||||
|
Contributors, Developers and Maintainers will be credited in
|
||||||
|
`CREDITS.md`. If you make your first time contribution, please add
|
||||||
|
yourself to this file. There are also Discord roles for Contributors,
|
||||||
|
Developers and Maintainers.
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
# Credits
|
||||||
|
|
||||||
|
## Creator of MineClone
|
||||||
|
* davedevils
|
||||||
|
|
||||||
|
## Creator of MineClone2
|
||||||
|
* Wuzzy
|
||||||
|
|
||||||
|
## Maintainers
|
||||||
|
* Fleckenstein
|
||||||
|
* Nicu
|
||||||
|
* kay27
|
||||||
|
|
||||||
|
## Developers
|
||||||
|
* bzoss
|
||||||
|
* AFCMS
|
||||||
|
* epCode
|
||||||
|
* ryvnf
|
||||||
|
* iliekprogrammar
|
||||||
|
* MysticTempest
|
||||||
|
* Rootyjr
|
||||||
|
* aligator
|
||||||
|
* Code-Sploit
|
||||||
|
* NO11
|
||||||
|
* cora
|
||||||
|
* jordan4ibanez
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
* Laurent Rocher
|
||||||
|
* HimbeerserverDE
|
||||||
|
* TechDudie
|
||||||
|
* Alexander Minges
|
||||||
|
* ArTee3
|
||||||
|
* ZeDique la Ruleta
|
||||||
|
* pitchum
|
||||||
|
* wuniversales
|
||||||
|
* Bu-Gee
|
||||||
|
* David McMackins II
|
||||||
|
* Nicholas Niro
|
||||||
|
* Wouters Dorian
|
||||||
|
* Blue Blancmange
|
||||||
|
* Jared Moody
|
||||||
|
* Li0n
|
||||||
|
* Midgard
|
||||||
|
* Saku Laesvuori
|
||||||
|
* Yukitty
|
||||||
|
* ZedekThePD
|
||||||
|
* aldum
|
||||||
|
* dBeans
|
||||||
|
* nickolas360
|
||||||
|
* yutyo
|
||||||
|
* Tianyang Zhang
|
||||||
|
* j45
|
||||||
|
* Marcin Serwin
|
||||||
|
* erlehmann
|
||||||
|
* E
|
||||||
|
* Benjamin Schötz
|
||||||
|
* Doloment
|
||||||
|
* Sydney Gems
|
||||||
|
* talamh
|
||||||
|
* Emily2255
|
||||||
|
* Emojigit
|
||||||
|
* FinishedFragment
|
||||||
|
* sfan5
|
||||||
|
* Blue Blancmange
|
||||||
|
* Jared Moody
|
||||||
|
* SmallJoker
|
||||||
|
* Sven792
|
||||||
|
* aldum
|
||||||
|
* Dieter44
|
||||||
|
|
||||||
|
## MineClone5
|
||||||
|
* kay27
|
||||||
|
* Debiankaios
|
||||||
|
* epCode
|
||||||
|
* NO11
|
||||||
|
* j45
|
||||||
|
|
||||||
|
## Original Mod Authors
|
||||||
|
* Wuzzy
|
||||||
|
* Fleckenstein
|
||||||
|
* BlockMen
|
||||||
|
* TenPlus1
|
||||||
|
* PilzAdam
|
||||||
|
* ryvnf
|
||||||
|
* stujones11
|
||||||
|
* Arcelmi
|
||||||
|
* celeron55
|
||||||
|
* maikerumine
|
||||||
|
* GunshipPenguin
|
||||||
|
* Qwertymine3
|
||||||
|
* Rochambeau
|
||||||
|
* rubenwardy
|
||||||
|
* stu
|
||||||
|
* 4aiman
|
||||||
|
* Kahrl
|
||||||
|
* Krock
|
||||||
|
* UgnilJoZ
|
||||||
|
* lordfingle
|
||||||
|
* 22i
|
||||||
|
* bzoss
|
||||||
|
* kilbith
|
||||||
|
* xeranas
|
||||||
|
* kddekadenz
|
||||||
|
* sofar
|
||||||
|
* 4Evergreen4
|
||||||
|
* jordan4ibanez
|
||||||
|
* paramat
|
||||||
|
|
||||||
|
## 3D Models
|
||||||
|
* 22i
|
||||||
|
* tobyplowy
|
||||||
|
* epCode
|
||||||
|
|
||||||
|
## Textures
|
||||||
|
* XSSheep
|
||||||
|
* Wuzzy
|
||||||
|
* kingoscargames
|
||||||
|
* leorockway
|
||||||
|
* xMrVizzy
|
||||||
|
* yutyo
|
||||||
|
* NO11
|
||||||
|
* kay27
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
* Wuzzy
|
||||||
|
* Rocher Laurent
|
||||||
|
* wuniversales
|
||||||
|
* kay27
|
||||||
|
* pitchum
|
||||||
|
* todoporlalibertad
|
||||||
|
* Marcin Serwin
|
||||||
|
|
||||||
|
## Funders
|
||||||
|
* 40W
|
||||||
|
|
||||||
|
## Special thanks
|
||||||
|
* celeron55 for creating Minetest
|
||||||
|
* Jordach for the jukebox music compilation from Big Freaking Dig
|
||||||
|
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
||||||
|
* Notch and Jeb for being the major forces behind Minecraft
|
|
@ -149,7 +149,7 @@ These groups are used mostly for informational purposes
|
||||||
* `trapdoor=2`: Open trapdoor
|
* `trapdoor=2`: Open trapdoor
|
||||||
* `glass=1`: Glass (full cubes only)
|
* `glass=1`: Glass (full cubes only)
|
||||||
* `rail=1`: Rail
|
* `rail=1`: Rail
|
||||||
* `music_record`: Music Disc (rating is track ID)
|
* `music_record`: Item is Music Disc
|
||||||
* `tnt=1`: Block is TNT
|
* `tnt=1`: Block is TNT
|
||||||
* `boat=1`: Boat
|
* `boat=1`: Boat
|
||||||
* `minecart=1`: Minecart
|
* `minecart=1`: Minecart
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Legal information
|
||||||
|
This is a fan game, not developed or endorsed by Mojang AB.
|
||||||
|
|
||||||
|
Copying is an act of love. Please copy and share! <3
|
||||||
|
Here's the detailed legalese for those who need it:
|
||||||
|
|
||||||
|
## License of source code
|
||||||
|
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
||||||
|
is an imitation of Minecraft.
|
||||||
|
|
||||||
|
MineClone 2 is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License (in the LICENSE.txt file) for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
In the mods you might find in the read-me or license
|
||||||
|
text files a different license. This counts as dual-licensing.
|
||||||
|
You can choose which license applies to you: Either the
|
||||||
|
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
||||||
|
|
||||||
|
MineClone 2 is a direct continuation of the discontinued MineClone
|
||||||
|
project by davedevils.
|
||||||
|
|
||||||
|
Mod credits:
|
||||||
|
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
||||||
|
For mods that do not have such a file, the license is the source code license
|
||||||
|
of MineClone 2 and the author is Wuzzy.
|
||||||
|
|
||||||
|
## License of media (textures and sounds)
|
||||||
|
No non-free licenses are used anywhere.
|
||||||
|
|
||||||
|
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
||||||
|
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
||||||
|
from scratch.
|
||||||
|
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
||||||
|
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
||||||
|
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||||
|
|
||||||
|
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||||
|
|
||||||
|
All other files, unless mentioned otherwise, fall under:
|
||||||
|
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||||
|
http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
|
||||||
|
See README.txt in each mod directory for detailed information about other authors.
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# Missing features in Minetest to recreate Minecraft features
|
|
||||||
|
|
||||||
A side goal of the MineClone 2 project is to find any shortcomings of Minetest which make it impossible to recreate a Minecraft feature exactly.
|
|
||||||
This file lists some of the missing features in Minetest which MineClone 2 would require.
|
|
||||||
|
|
||||||
## No workaround possible
|
|
||||||
For these features, no easy Lua workaround could be found.
|
|
||||||
|
|
||||||
### Lua API
|
|
||||||
#### Tools/wielded item
|
|
||||||
- “Lock” hotbar for a brief time after using an item, making it impossible to switch item or to attach/mine/build until the delay is over (For eating with delay)
|
|
||||||
- Tool charging: Holding down the mouse and releasing it, applying a “power level” (For bow and arrows, more charge = higher arrow range) ([issue 5212](https://github.com/minetest/minetest/issues/5212))
|
|
||||||
- [Dual Wielding](http://minecraft.gamepedia.com/Dual_wield)
|
|
||||||
- Eating/drinking animation ([issue 2811](https://github.com/minetest/minetest/issues/2811))
|
|
||||||
|
|
||||||
#### Nodes
|
|
||||||
- Light level 15 for nodes (not sunlight)
|
|
||||||
- Nodes makes light level drop by 2 or or more per node ([issue 5209](https://github.com/minetest/minetest/issues/5209))
|
|
||||||
|
|
||||||
## Interface
|
|
||||||
- Inventory: Hold down right mouse button while holding an item stack to drop items into the slots as you move the mouse. Makes crafting MUCH faster
|
|
||||||
- Sneak+Leftclick on crafting output crafts as many items as possible and immediately puts it into the player inventory ([issue 5211](https://github.com/minetest/minetest/issues/5211))
|
|
||||||
- Sneak+click puts items in different inventories depending on the item type (maybe group-based)? Required for sneak-clicking to armor slots
|
|
||||||
|
|
||||||
## Workaround theoretically possible
|
|
||||||
For these features, a workaround (or hack ;-)) by using Lua is theoretically possible. But engine support would be clearly better, more performant, more reliable, etc.
|
|
||||||
|
|
||||||
### Lua API
|
|
||||||
#### Nodes
|
|
||||||
- Change walking speed on block (soul sand)
|
|
||||||
- Change jumping height on block (soul sand),
|
|
||||||
- Change object movement speed *through* a block, but for non-liquids (for cobweb)
|
|
||||||
- Add `on_walk_over` event
|
|
||||||
- Set frequency in which players lose breath. 2 seconds are hardcoded in Minetest, in Minecraft it's 1 second
|
|
||||||
- Set damage frequency of `damage_per_second`. In Minecraft many things damage players every half-second rather than every second
|
|
||||||
- Possible to damage players directly when they are with the head inside. This allows to add Minecraft-like suffocation
|
|
||||||
- Sneak+click on inventory slot should be able to put items into additional “fallback inventories” if the first inventory is full. Useful for large chests
|
|
||||||
|
|
||||||
#### Nice-to-haye
|
|
||||||
- Utility function to rotate pillar-like nodes, requiring only 3 possible orientations (X, Y, Z). Basically this is `minetest.rotate_node` but with less orientations; the purpur pillar would mess up if a mirrored rotation would be possible. This is already implemented in MCL2, See `mcl_util` for more infos
|
|
216
README.md
|
@ -1,8 +1,8 @@
|
||||||
# MineClone 2
|
# MineClone2
|
||||||
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.71.0
|
Version: 0.72.0 (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
|
||||||
|
@ -65,41 +65,51 @@ map builders. They can not be obtained in-game or in the creative inventory.
|
||||||
Use the `/giveme` chat command to obtain them. See the in-game help for
|
Use the `/giveme` chat command to obtain them. See the in-game help for
|
||||||
an explanation.
|
an explanation.
|
||||||
|
|
||||||
#### Incomplete items
|
|
||||||
These items do not work yet, but you can get them with `/giveme` for testing:
|
|
||||||
|
|
||||||
* Minecart with Chest: `mcl_minecarts:chest_minecart`
|
|
||||||
* Minecart with Furnace: `mcl_minecarts:furnace_minecart`
|
|
||||||
* Minecart with Hopper: `mcl_minecarts:hopper_minecart`
|
|
||||||
* Minecart with Command Block: `mcl_minecarts:command_block_minecart`
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This game requires [Minetest](http://minetest.net) to run (version 5.0.0 or
|
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
||||||
later). So you need to install Minetest first. Only stable versions of Minetest
|
later). So you need to install Minetest first. Only stable versions of Minetest
|
||||||
are officially supported.
|
are officially supported.
|
||||||
There is no support for running MineClone 2 in development versions of Minetest.
|
There is no support for running MineClone2 in development versions of Minetest.
|
||||||
|
|
||||||
To install MineClone 2 (if you haven't already), move this directory into the
|
To install MineClone2 (if you haven't already), move this directory into the
|
||||||
“games” directory of your Minetest data directory. Consult the help of
|
“games” directory of your Minetest data directory. Consult the help of
|
||||||
Minetest to learn more.
|
Minetest to learn more.
|
||||||
|
|
||||||
## Project description
|
## Useful links
|
||||||
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
The MineClone2 repository is hosted at Mesehub. To contribute or report issues, head there.
|
||||||
|
|
||||||
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
* Mesehub: <https://git.minetest.land/MineClone2/MineClone2>
|
||||||
* MineClone2 also includes Optifine features supported by the Minetest
|
* Discord: <https://discord.gg/xE4z8EEpDC>
|
||||||
* Features of later Minecraft versions might sneak in, but they have a low priority
|
* YouTube <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
|
||||||
* In general, Minecraft is aimed to be cloned as good as Minetest currently permits (no hacks)
|
* IRC: <https://web.libera.chat/#mineclone2>
|
||||||
* Cloning the gameplay has highest priority
|
* Matrix: <https://app.element.io/#/room/#mc2:matrix.org>
|
||||||
* MineClone 2 will use different graphics and sounds, but with a similar style
|
* Reddit: <https://www.reddit.com/r/MineClone2/>
|
||||||
* Cloning the interface has no priority. It will only be roughly imitated
|
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
|
||||||
* Limitations found in Minetest will be written down and reported in the course of development
|
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
|
||||||
|
* OpenCollective: <https://opencollective.com/mineclone2>
|
||||||
|
|
||||||
|
## Target
|
||||||
|
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||||
|
based on the Minetest engine with polished features, usable in both
|
||||||
|
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||||
|
Edition 1.12.2** features are already implemented and polishing existing
|
||||||
|
features are prioritized over new feature requests.
|
||||||
|
- With lessened priority yet strictly, implement features targetting
|
||||||
|
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||||
|
by the Minetest Engine). This means features in parity with the listed
|
||||||
|
Minecraft experiences are prioritized over those that don't fulfill this
|
||||||
|
scope.
|
||||||
|
- Optionally, create a performant experience that will run relatively
|
||||||
|
well on really low spec computers. Unfortunately, due to Minecraft's
|
||||||
|
mechanisms and Minetest engine's limitations along with a very small
|
||||||
|
playerbase on low spec computers, optimizations are hard to investigate.
|
||||||
|
|
||||||
## Completion status
|
## Completion status
|
||||||
This game is currently in **alpha** stage.
|
This game is currently in **beta** stage.
|
||||||
It is playable, but unfinished, many bugs are to be expected.
|
It is playable, but not yet feature-complete.
|
||||||
Backwards-compability is *not* guaranteed, updating your world might cause small and
|
Backwards-compability is not entirely guaranteed, updating your world might cause small bugs.
|
||||||
big bugs (such as “missing node” errors or even crashes).
|
If you want to use the git version of MineClone2 in production, consider using the production branch.
|
||||||
|
It is updated weekly and contains relatively stable code for servers.
|
||||||
|
|
||||||
The following main features are available:
|
The following main features are available:
|
||||||
|
|
||||||
|
@ -128,7 +138,7 @@ The following main features are available:
|
||||||
* Clock
|
* Clock
|
||||||
* Compass
|
* Compass
|
||||||
* Sponge
|
* Sponge
|
||||||
* Slime block (does not interact with redstone)
|
* Slime block
|
||||||
* Small plants and saplings
|
* Small plants and saplings
|
||||||
* Dyes
|
* Dyes
|
||||||
* Banners
|
* Banners
|
||||||
|
@ -140,19 +150,19 @@ The following main features are available:
|
||||||
* Creative inventory
|
* Creative inventory
|
||||||
* Farming
|
* Farming
|
||||||
* Writable books
|
* Writable books
|
||||||
* A few server commands
|
* Commands
|
||||||
|
* Villages
|
||||||
|
* The End
|
||||||
* And more!
|
* And more!
|
||||||
|
|
||||||
The following features are incomplete:
|
The following features are incomplete:
|
||||||
|
|
||||||
* Generated structures (especially villages)
|
|
||||||
* Some monsters and animals
|
* Some monsters and animals
|
||||||
* Redstone-related things
|
* Redstone-related things
|
||||||
* The End
|
|
||||||
* Special minecarts
|
* Special minecarts
|
||||||
* A couple of non-trivial blocks and items
|
* A couple of non-trivial blocks and items
|
||||||
|
|
||||||
Bonus features (not found in Minecraft 1.11):
|
Bonus features (not found in Minecraft 1.12):
|
||||||
|
|
||||||
* Built-in crafting guide which shows you crafting and smelting recipes
|
* Built-in crafting guide which shows you crafting and smelting recipes
|
||||||
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
* In-game help system containing extensive help about gameplay basics, blocks, items and more
|
||||||
|
@ -177,148 +187,14 @@ Technical differences from Minecraft:
|
||||||
* Different textures (Pixel Perfection)
|
* Different textures (Pixel Perfection)
|
||||||
* Different sounds (various sources)
|
* Different sounds (various sources)
|
||||||
* Different engine (Minetest)
|
* Different engine (Minetest)
|
||||||
|
* Different easter eggs
|
||||||
|
|
||||||
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
|
… and finally, MineClone2 is free software (“free” as in “freedom”)!
|
||||||
|
|
||||||
## Reporting bugs
|
|
||||||
Please report all bugs and missing Minecraft features here:
|
|
||||||
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
|
||||||
|
|
||||||
## Chating with the community
|
|
||||||
Join our discord server at:
|
|
||||||
|
|
||||||
<https://discord.gg/84GKcxczG3>
|
|
||||||
|
|
||||||
## Other readme files
|
## Other readme files
|
||||||
|
|
||||||
* `LICENSE.txt`: The GPLv3 license text
|
* `LICENSE.txt`: The GPLv3 license text
|
||||||
* `CONTRIBUTING.md`: Information for those who want to contribute
|
* `CONTRIBUTING.md`: Information for those who want to contribute
|
||||||
* `MISSING_ENGINE_FEATURES.md`: List of missing features in Minetest which MineClone 2 would need for improvement
|
|
||||||
* `API.md`: For Minetest modders who want to mod this game
|
* `API.md`: For Minetest modders who want to mod this game
|
||||||
|
* `LEGAL.md`: Legal information
|
||||||
## Credits
|
* `CREDITS.md`: List of everyone who contributed
|
||||||
There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game.
|
|
||||||
|
|
||||||
### Coding
|
|
||||||
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired)
|
|
||||||
* davedevils: Creator of MineClone on which MineClone 2 is based on
|
|
||||||
* [ex-bart](https://github.com/ex-bart): Redstone comparators
|
|
||||||
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes
|
|
||||||
* [aligator](https://github.com/aligator): Improvement of doors
|
|
||||||
* [ryvnf](https://github.com/ryvnf): Explosion mechanics
|
|
||||||
* MysticTempest: Bugfixes
|
|
||||||
* [bzoss](https://github.com/bzoss): Status effects, potions, brewing stand
|
|
||||||
* kay27 <kay27@bk.ru>: Experience system, bugfixes, optimizations (Current maintainer)
|
|
||||||
* [EliasFleckenstein03](https://github.com/EliasFleckenstein03): End crystals, enchanting, burning mobs / players, animated chests, bugfixes (Current maintainer)
|
|
||||||
* epCode: Better player animations, new logo
|
|
||||||
* 2mac: Fix bug with powered rail
|
|
||||||
* Lots of other people: TO BE WRITTEN (see mod directories for details)
|
|
||||||
|
|
||||||
#### Mod credits (summary)
|
|
||||||
|
|
||||||
* `controls`: Arcelmi
|
|
||||||
* `flowlib`: Qwertymine13
|
|
||||||
* `walkover`: lordfingle
|
|
||||||
* `drippingwater`: kddekadenz
|
|
||||||
* `mobs_mc`: maikerumine, 22i and others
|
|
||||||
* `awards`: rubenwardy
|
|
||||||
* `screwdriver`: RealBadAngel, Maciej Kastakin, Minetest contributors
|
|
||||||
* `xpanes`: Minetest contributors
|
|
||||||
* `mesecons` mods: Jeija and contributors
|
|
||||||
* `wieldview`: Stuart Jones
|
|
||||||
* `mcl_meshhand`: Based on `newhand` by jordan4ibanez
|
|
||||||
* `mcl_mobs`: Based on Mobs Redo [`mobs`] by TenPlus1 and contributors
|
|
||||||
* Most other mods: Wuzzy
|
|
||||||
|
|
||||||
Detailed credits for each mod can be found in the individual mod directories.
|
|
||||||
|
|
||||||
### Graphics
|
|
||||||
* [XSSheep](http://www.minecraftforum.net/members/XSSheep): Main author; creator of the Pixel Perfection resource pack of Minecraft 1.11
|
|
||||||
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main menu imagery and various edits and additions of texture pack
|
|
||||||
* [kingoscargames](https://github.com/kingoscargames): Various edits and additions of existing textures
|
|
||||||
* [leorockway](https://github.com/leorockway): Some edits of mob textures
|
|
||||||
* [xMrVizzy](https://minecraft.curseforge.com/members/xMrVizzy): Glazed terracotta (textures are subject to be replaced later)
|
|
||||||
* yutyo <tanakinci2002@gmail.com>: MineClone 2 logo
|
|
||||||
* Other authors: GUI images
|
|
||||||
|
|
||||||
### Translations
|
|
||||||
* Wuzzy: German
|
|
||||||
* Rocher Laurent <rocherl@club-internet.fr>: French
|
|
||||||
* wuniversales: Spanish
|
|
||||||
* kay27 <kay27@bk.ru>: Russian
|
|
||||||
|
|
||||||
### Models
|
|
||||||
* [22i](https://github.com/22i): Creator of all models
|
|
||||||
* [tobyplowy](https://github.com/tobyplowy): UV-mapping fixes to said models
|
|
||||||
|
|
||||||
### Sounds and music
|
|
||||||
Various sources. See the respective mod directories for details.
|
|
||||||
|
|
||||||
### Special thanks
|
|
||||||
|
|
||||||
* davedevils for starting MineClone, the original version of this game
|
|
||||||
* Wuzzy for starting and maintaining MineClone2 for several years
|
|
||||||
* celeron55 for creating Minetest
|
|
||||||
* Minetest's modding community for providing a huge selection of mods, some of which ended up in MineClone 2
|
|
||||||
* Jordach for the jukebox music compilation from Big Freaking Dig
|
|
||||||
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
|
|
||||||
* Notch and Jeb for being the major forces behind Minecraft
|
|
||||||
* XSSheep for creating the Pixel Perfection resource pack
|
|
||||||
* [22i](https://github.com/22i) for providing great models and support
|
|
||||||
* [maikerumine](http://github.com/maikerumine) for kicking off mobs and biomes
|
|
||||||
|
|
||||||
## Info for programmers
|
|
||||||
You find interesting and useful infos in `API.md`.
|
|
||||||
|
|
||||||
## Legal information
|
|
||||||
This is a fan game, not developed or endorsed by Mojang AB.
|
|
||||||
|
|
||||||
Copying is an act of love. Please copy and share! <3
|
|
||||||
Here's the detailed legalese for those who need it:
|
|
||||||
|
|
||||||
### License of source code
|
|
||||||
MineClone 2 (by kay27, EliasFleckenstein, Wuzzy, davedevils and countless others)
|
|
||||||
is an imitation of Minecraft.
|
|
||||||
|
|
||||||
MineClone 2 is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License (in the LICENSE.txt file) for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
In the mods you might find in the read-me or license
|
|
||||||
text files a different license. This counts as dual-licensing.
|
|
||||||
You can choose which license applies to you: Either the
|
|
||||||
license of MineClone 2 (GNU GPLv3) or the mod's license.
|
|
||||||
|
|
||||||
MineClone 2 is a direct continuation of the discontinued MineClone
|
|
||||||
project by davedevils.
|
|
||||||
|
|
||||||
Mod credits:
|
|
||||||
See `README.txt` or `README.md` in each mod directory for information about other authors.
|
|
||||||
For mods that do not have such a file, the license is the source code license
|
|
||||||
of MineClone 2 and the author is Wuzzy.
|
|
||||||
|
|
||||||
### License of media (textures and sounds)
|
|
||||||
No non-free licenses are used anywhere.
|
|
||||||
|
|
||||||
The textures, unless otherwise noted, are based on the Pixel Perfection resource pack for Minecraft 1.11,
|
|
||||||
authored by XSSheep. Most textures are verbatim copies, while some textures have been changed or redone
|
|
||||||
from scratch.
|
|
||||||
The glazed terracotta textures have been created by (MysticTempest)[https://github.com/MysticTempest].
|
|
||||||
Source: <https://www.planetminecraft.com/texture_pack/131pixel-perfection/>
|
|
||||||
License: [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)
|
|
||||||
|
|
||||||
The main menu images are release under: [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
|
||||||
|
|
||||||
All other files, unless mentioned otherwise, fall under:
|
|
||||||
Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
|
||||||
http://creativecommons.org/licenses/by-sa/3.0/
|
|
||||||
|
|
||||||
See README.txt in each mod directory for detailed information about other authors.
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ local function get_hardness_values_for_groups()
|
||||||
|
|
||||||
for _, ndef in pairs(minetest.registered_nodes) do
|
for _, ndef in pairs(minetest.registered_nodes) do
|
||||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||||
if ndef.groups[g] ~= nil then
|
if ndef.groups[g] then
|
||||||
maps[g][ndef._mcl_hardness or 0] = true
|
maps[g][ndef._mcl_hardness or 0] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -121,7 +121,7 @@ local hardness_values = get_hardness_values_for_groups()
|
||||||
-- hardness_value. Used for quick lookup.
|
-- hardness_value. Used for quick lookup.
|
||||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||||
|
|
||||||
local function compute_creativetimes(group)
|
--[[local function compute_creativetimes(group)
|
||||||
local creativetimes = {}
|
local creativetimes = {}
|
||||||
|
|
||||||
for index, hardness in pairs(hardness_values[group]) do
|
for index, hardness in pairs(hardness_values[group]) do
|
||||||
|
@ -129,7 +129,7 @@ local function compute_creativetimes(group)
|
||||||
end
|
end
|
||||||
|
|
||||||
return creativetimes
|
return creativetimes
|
||||||
end
|
end]]
|
||||||
|
|
||||||
-- Get the list of digging times for using a specific tool on a specific
|
-- Get the list of digging times for using a specific tool on a specific
|
||||||
-- diggroup.
|
-- diggroup.
|
||||||
|
@ -207,6 +207,10 @@ end
|
||||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
function mcl_autogroup.can_harvest(nodename, toolname)
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
|
|
||||||
|
if not ndef then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -239,13 +243,13 @@ function mcl_autogroup.can_harvest(nodename, toolname)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get one groupcap field for using a specific tool on a specific group.
|
-- Get one groupcap field for using a specific tool on a specific group.
|
||||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
--[[local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||||
return {
|
return {
|
||||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||||
uses = uses,
|
uses = uses,
|
||||||
maxlevel = 0,
|
maxlevel = 0,
|
||||||
}
|
}
|
||||||
end
|
end]]
|
||||||
|
|
||||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
-- Returns the tool_capabilities from a tool definition or a default set of
|
||||||
-- tool_capabilities
|
-- tool_capabilities
|
||||||
|
@ -298,7 +302,7 @@ function mcl_autogroup.get_wear(toolname, diggroup)
|
||||||
return math.ceil(65535 / uses)
|
return math.ceil(65535 / uses)
|
||||||
end
|
end
|
||||||
|
|
||||||
local overwrite = function()
|
local function overwrite()
|
||||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||||
local newgroups = table.copy(ndef.groups)
|
local newgroups = table.copy(ndef.groups)
|
||||||
if (nname ~= "ignore" and ndef.diggable) then
|
if (nname ~= "ignore" and ndef.diggable) then
|
||||||
|
@ -315,12 +319,12 @@ local overwrite = function()
|
||||||
newgroups.opaque = 1
|
newgroups.opaque = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local creative_breakable = false
|
--local creative_breakable = false
|
||||||
|
|
||||||
-- Assign groups used for digging this node depending on
|
-- Assign groups used for digging this node depending on
|
||||||
-- the registered digging groups
|
-- the registered digging groups
|
||||||
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
||||||
creative_breakable = true
|
--creative_breakable = true
|
||||||
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
||||||
if ndef.groups[g] then
|
if ndef.groups[g] then
|
||||||
if gdef.levels then
|
if gdef.levels then
|
||||||
|
|
|
@ -81,11 +81,11 @@ if v6_use_snow_biomes then
|
||||||
end
|
end
|
||||||
local v6_freq_desert = tonumber(minetest.get_mapgen_setting("mgv6_freq_desert") or 0.45)
|
local v6_freq_desert = tonumber(minetest.get_mapgen_setting("mgv6_freq_desert") or 0.45)
|
||||||
|
|
||||||
local NOISE_MAGIC_X = 1619
|
--local NOISE_MAGIC_X = 1619
|
||||||
local NOISE_MAGIC_Y = 31337
|
--local NOISE_MAGIC_Y = 31337
|
||||||
local NOISE_MAGIC_Z = 52591
|
--local NOISE_MAGIC_Z = 52591
|
||||||
local NOISE_MAGIC_SEED = 1013
|
--local NOISE_MAGIC_SEED = 1013
|
||||||
local noise2d = function(x, y, seed)
|
local function noise2d(x, y, seed)
|
||||||
-- TODO: implement noise2d function for biome blend
|
-- TODO: implement noise2d function for biome blend
|
||||||
return 0
|
return 0
|
||||||
--[[
|
--[[
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
local get_connected_players = minetest.get_connected_players
|
local get_connected_players = minetest.get_connected_players
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
|
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
controls = {}
|
controls = {}
|
||||||
controls.players = {}
|
controls.players = {}
|
||||||
|
|
||||||
|
@ -20,15 +22,15 @@ function controls.register_on_hold(func)
|
||||||
end
|
end
|
||||||
|
|
||||||
local known_controls = {
|
local known_controls = {
|
||||||
jump=true,
|
jump = true,
|
||||||
right=true,
|
right = true,
|
||||||
left=true,
|
left = true,
|
||||||
LMB=true,
|
LMB = true,
|
||||||
RMB=true,
|
RMB = true,
|
||||||
sneak=true,
|
sneak = true,
|
||||||
aux1=true,
|
aux1 = true,
|
||||||
down=true,
|
down = true,
|
||||||
up=true,
|
up = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
@ -49,27 +51,27 @@ minetest.register_globalstep(function(dtime)
|
||||||
local player_name = player:get_player_name()
|
local player_name = player:get_player_name()
|
||||||
local player_controls = player:get_player_control()
|
local player_controls = player:get_player_control()
|
||||||
if controls.players[player_name] then
|
if controls.players[player_name] then
|
||||||
for cname, cbool in pairs(player_controls) do
|
for cname, cbool in pairs(player_controls) do
|
||||||
if known_controls[cname] == true then
|
if known_controls[cname] == true then
|
||||||
--Press a key
|
--Press a key
|
||||||
if cbool==true and controls.players[player_name][cname][1]==false then
|
if cbool == true and controls.players[player_name][cname][1] == false then
|
||||||
for _, func in pairs(controls.registered_on_press) do
|
for _, func in pairs(controls.registered_on_press) do
|
||||||
func(player, cname)
|
func(player, cname)
|
||||||
|
end
|
||||||
|
controls.players[player_name][cname] = {true, clock()}
|
||||||
|
elseif cbool == true and controls.players[player_name][cname][1] == true then
|
||||||
|
for _, func in pairs(controls.registered_on_hold) do
|
||||||
|
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||||
|
end
|
||||||
|
--Release a key
|
||||||
|
elseif cbool == false and controls.players[player_name][cname][1] == true then
|
||||||
|
for _, func in pairs(controls.registered_on_release) do
|
||||||
|
func(player, cname, clock()-controls.players[player_name][cname][2])
|
||||||
|
end
|
||||||
|
controls.players[player_name][cname] = {false}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
controls.players[player_name][cname] = {true, clock()}
|
|
||||||
elseif cbool==true and controls.players[player_name][cname][1]==true then
|
|
||||||
for _, func in pairs(controls.registered_on_hold) do
|
|
||||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
|
||||||
end
|
|
||||||
--Release a key
|
|
||||||
elseif cbool==false and controls.players[player_name][cname][1]==true then
|
|
||||||
for _, func in pairs(controls.registered_on_release) do
|
|
||||||
func(player, cname, clock()-controls.players[player_name][cname][2])
|
|
||||||
end
|
|
||||||
controls.players[player_name][cname] = {false}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,95 +1,100 @@
|
||||||
|
local math = math
|
||||||
|
|
||||||
|
local get_node = minetest.get_node
|
||||||
|
local get_item_group = minetest.get_item_group
|
||||||
|
|
||||||
|
local registered_nodes = minetest.registered_nodes
|
||||||
|
|
||||||
flowlib = {}
|
flowlib = {}
|
||||||
|
|
||||||
--sum of direction vectors must match an array index
|
--sum of direction vectors must match an array index
|
||||||
|
|
||||||
|
--(sum,root)
|
||||||
|
--(0,1), (1,1+0=1), (2,1+1=2), (3,1+2^2=5), (4,2^2+2^2=8)
|
||||||
|
|
||||||
|
local inv_roots = {
|
||||||
|
[0] = 1,
|
||||||
|
[1] = 1,
|
||||||
|
[2] = 0.70710678118655,
|
||||||
|
[4] = 0.5,
|
||||||
|
[5] = 0.44721359549996,
|
||||||
|
[8] = 0.35355339059327,
|
||||||
|
}
|
||||||
|
|
||||||
local function to_unit_vector(dir_vector)
|
local function to_unit_vector(dir_vector)
|
||||||
--(sum,root)
|
local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
|
||||||
-- (0,1), (1,1+0=1), (2,1+1=2), (3,1+2^2=5), (4,2^2+2^2=8)
|
return {x = dir_vector.x * inv_roots[sum], y = dir_vector.y, z = dir_vector.z * inv_roots[sum]}
|
||||||
local inv_roots = {[0] = 1, [1] = 1, [2] = 0.70710678118655, [4] = 0.5
|
|
||||||
, [5] = 0.44721359549996, [8] = 0.35355339059327}
|
|
||||||
local sum = dir_vector.x*dir_vector.x + dir_vector.z*dir_vector.z
|
|
||||||
return {x=dir_vector.x*inv_roots[sum],y=dir_vector.y
|
|
||||||
,z=dir_vector.z*inv_roots[sum]}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_touching = function(realpos,nodepos,radius)
|
local function is_touching(realpos,nodepos,radius)
|
||||||
local boarder = 0.5 - radius
|
local boarder = 0.5 - radius
|
||||||
return (math.abs(realpos - nodepos) > (boarder))
|
return math.abs(realpos - nodepos) > (boarder)
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_touching = is_touching
|
flowlib.is_touching = is_touching
|
||||||
|
|
||||||
local is_water = function(pos)
|
local function is_water(pos)
|
||||||
return (minetest.get_item_group(minetest.get_node(
|
return get_item_group(get_node(pos).name, "water") ~= 0
|
||||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
|
||||||
, "water") ~= 0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_water = is_water
|
flowlib.is_water = is_water
|
||||||
|
|
||||||
local node_is_water = function(node)
|
local function node_is_water(node)
|
||||||
return (minetest.get_item_group(node.name, "water") ~= 0)
|
return get_item_group(node.name, "water") ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_water = node_is_water
|
flowlib.node_is_water = node_is_water
|
||||||
|
|
||||||
local is_lava = function(pos)
|
local function is_lava(pos)
|
||||||
return (minetest.get_item_group(minetest.get_node(
|
return get_item_group(get_node(pos).name, "lava") ~= 0
|
||||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
|
||||||
, "lava") ~= 0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_lava = is_lava
|
flowlib.is_lava = is_lava
|
||||||
|
|
||||||
local node_is_lava = function(node)
|
local function node_is_lava(node)
|
||||||
return (minetest.get_item_group(node.name, "lava") ~= 0)
|
return get_item_group(node.name, "lava") ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_lava = node_is_lava
|
flowlib.node_is_lava = node_is_lava
|
||||||
|
|
||||||
|
|
||||||
local is_liquid = function(pos)
|
local function is_liquid(pos)
|
||||||
return (minetest.get_item_group(minetest.get_node(
|
return get_item_group(get_node(pos).name, "liquid") ~= 0
|
||||||
{x=pos.x,y=pos.y,z=pos.z}).name
|
|
||||||
, "liquid") ~= 0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.is_liquid = is_liquid
|
flowlib.is_liquid = is_liquid
|
||||||
|
|
||||||
local node_is_liquid = function(node)
|
local function node_is_liquid(node)
|
||||||
return (minetest.get_item_group(node.name, "liquid") ~= 0)
|
return minetest.get_item_group(node.name, "liquid") ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.node_is_liquid = node_is_liquid
|
flowlib.node_is_liquid = node_is_liquid
|
||||||
|
|
||||||
--This code is more efficient
|
--This code is more efficient
|
||||||
local function quick_flow_logic(node,pos_testing,direction)
|
local function quick_flow_logic(node, pos_testing, direction)
|
||||||
local name = node.name
|
local name = node.name
|
||||||
if not minetest.registered_nodes[name] then
|
if not registered_nodes[name] then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if minetest.registered_nodes[name].liquidtype == "source" then
|
if registered_nodes[name].liquidtype == "source" then
|
||||||
local node_testing = minetest.get_node(pos_testing)
|
local node_testing = get_node(pos_testing)
|
||||||
local param2_testing = node_testing.param2
|
if not registered_nodes[node_testing.name] then
|
||||||
if not minetest.registered_nodes[node_testing.name] then
|
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if minetest.registered_nodes[node_testing.name].liquidtype
|
if registered_nodes[node_testing.name].liquidtype ~= "flowing" then
|
||||||
~= "flowing" then
|
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
return direction
|
return direction
|
||||||
end
|
end
|
||||||
elseif minetest.registered_nodes[name].liquidtype == "flowing" then
|
elseif registered_nodes[name].liquidtype == "flowing" then
|
||||||
local node_testing = minetest.get_node(pos_testing)
|
local node_testing = get_node(pos_testing)
|
||||||
local param2_testing = node_testing.param2
|
local param2_testing = node_testing.param2
|
||||||
if not minetest.registered_nodes[node_testing.name] then
|
if not registered_nodes[node_testing.name] then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if minetest.registered_nodes[node_testing.name].liquidtype
|
if registered_nodes[node_testing.name].liquidtype == "source" then
|
||||||
== "source" then
|
|
||||||
return -direction
|
return -direction
|
||||||
elseif minetest.registered_nodes[node_testing.name].liquidtype
|
elseif registered_nodes[node_testing.name].liquidtype == "flowing" then
|
||||||
== "flowing" then
|
|
||||||
if param2_testing < node.param2 then
|
if param2_testing < node.param2 then
|
||||||
if (node.param2 - param2_testing) > 6 then
|
if (node.param2 - param2_testing) > 6 then
|
||||||
return -direction
|
return -direction
|
||||||
|
@ -108,48 +113,41 @@ local function quick_flow_logic(node,pos_testing,direction)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local quick_flow = function(pos,node)
|
local function quick_flow(pos, node)
|
||||||
local x = 0
|
|
||||||
local z = 0
|
|
||||||
|
|
||||||
if not node_is_liquid(node) then
|
if not node_is_liquid(node) then
|
||||||
return {x=0,y=0,z=0}
|
return {x = 0, y = 0, z = 0}
|
||||||
end
|
end
|
||||||
|
local x = quick_flow_logic(node,{x = pos.x-1, y = pos.y, z = pos.z},-1) + quick_flow_logic(node,{x = pos.x+1, y = pos.y, z = pos.z}, 1)
|
||||||
x = x + quick_flow_logic(node,{x=pos.x-1,y=pos.y,z=pos.z},-1)
|
local z = quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z-1},-1) + quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z+1}, 1)
|
||||||
x = x + quick_flow_logic(node,{x=pos.x+1,y=pos.y,z=pos.z}, 1)
|
return to_unit_vector({x = x, y = 0, z = z})
|
||||||
z = z + quick_flow_logic(node,{x=pos.x,y=pos.y,z=pos.z-1},-1)
|
|
||||||
z = z + quick_flow_logic(node,{x=pos.x,y=pos.y,z=pos.z+1}, 1)
|
|
||||||
|
|
||||||
return to_unit_vector({x=x,y=0,z=z})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.quick_flow = quick_flow
|
flowlib.quick_flow = quick_flow
|
||||||
|
|
||||||
|
--if not in water but touching, move centre to touching block
|
||||||
|
--x has higher precedence than z
|
||||||
|
--if pos changes with x, it affects z
|
||||||
|
|
||||||
--if not in water but touching, move centre to touching block
|
local function move_centre(pos, realpos, node, radius)
|
||||||
--x has higher precedence than z
|
if is_touching(realpos.x, pos.x, radius) then
|
||||||
--if pos changes with x, it affects z
|
if is_liquid({x = pos.x-1, y = pos.y, z = pos.z}) then
|
||||||
local move_centre = function(pos,realpos,node,radius)
|
node = get_node({x=pos.x-1, y = pos.y, z = pos.z})
|
||||||
if is_touching(realpos.x,pos.x,radius) then
|
pos = {x = pos.x-1, y = pos.y, z = pos.z}
|
||||||
if is_liquid({x=pos.x-1,y=pos.y,z=pos.z}) then
|
elseif is_liquid({x = pos.x+1, y = pos.y, z = pos.z}) then
|
||||||
node = minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z})
|
node = get_node({x = pos.x+1, y = pos.y, z = pos.z})
|
||||||
pos = {x=pos.x-1,y=pos.y,z=pos.z}
|
pos = {x = pos.x+1, y = pos.y, z = pos.z}
|
||||||
elseif is_liquid({x=pos.x+1,y=pos.y,z=pos.z}) then
|
|
||||||
node = minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z})
|
|
||||||
pos = {x=pos.x+1,y=pos.y,z=pos.z}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if is_touching(realpos.z,pos.z,radius) then
|
if is_touching(realpos.z, pos.z, radius) then
|
||||||
if is_liquid({x=pos.x,y=pos.y,z=pos.z-1}) then
|
if is_liquid({x = pos.x, y = pos.y, z = pos.z - 1}) then
|
||||||
node = minetest.get_node({x=pos.x,y=pos.y,z=pos.z-1})
|
node = get_node({x = pos.x, y = pos.y, z = pos.z - 1})
|
||||||
pos = {x=pos.x,y=pos.y,z=pos.z-1}
|
pos = {x = pos.x, y = pos.y, z = pos.z - 1}
|
||||||
elseif is_liquid({x=pos.x,y=pos.y,z=pos.z+1}) then
|
elseif is_liquid({x = pos.x, y = pos.y, z = pos.z + 1}) then
|
||||||
node = minetest.get_node({x=pos.x,y=pos.y,z=pos.z+1})
|
node = get_node({x = pos.x, y = pos.y, z = pos.z + 1})
|
||||||
pos = {x=pos.x,y=pos.y,z=pos.z+1}
|
pos = {x = pos.x, y = pos.y, z = pos.z + 1}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return pos,node
|
return pos, node
|
||||||
end
|
end
|
||||||
|
|
||||||
flowlib.move_centre = move_centre
|
flowlib.move_centre = move_centre
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local facedir_to_dir = minetest.facedir_to_dir
|
||||||
|
local get_item_group = minetest.get_item_group
|
||||||
|
local remove_node = minetest.remove_node
|
||||||
|
local get_node = minetest.get_node
|
||||||
|
|
||||||
local original_function = minetest.check_single_for_falling
|
local original_function = minetest.check_single_for_falling
|
||||||
|
|
||||||
minetest.check_single_for_falling = function(pos)
|
function minetest.check_single_for_falling(pos)
|
||||||
local ret_o = original_function(pos)
|
local ret_o = original_function(pos)
|
||||||
|
|
||||||
local ret = false
|
local ret = false
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
if minetest.get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
if get_item_group(node.name, "attached_node_facedir") ~= 0 then
|
||||||
local dir = minetest.facedir_to_dir(node.param2)
|
local dir = facedir_to_dir(node.param2)
|
||||||
if dir then
|
if dir then
|
||||||
local cpos = vector.add(pos, dir)
|
if get_item_group(get_node(vector.add(pos, dir)).name, "solid") == 0 then
|
||||||
local cnode = minetest.get_node(cpos)
|
remove_node(pos)
|
||||||
if minetest.get_item_group(cnode.name, "solid") == 0 then
|
|
||||||
minetest.remove_node(pos)
|
|
||||||
local drops = minetest.get_node_drops(node.name, "")
|
local drops = minetest.get_node_drops(node.name, "")
|
||||||
for dr=1, #drops do
|
for dr=1, #drops do
|
||||||
minetest.add_item(pos, drops[dr])
|
minetest.add_item(pos, drops[dr])
|
||||||
|
@ -20,7 +24,6 @@ minetest.check_single_for_falling = function(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ret_o or ret
|
return ret_o or ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
# API documentation of mcl_commands
|
|
||||||
|
|
||||||
The mcl_commands API allows you to register and overide complex commands.
|
|
||||||
This mod is derivated from ChatCommandBuilder by rubenwardy
|
|
||||||
|
|
||||||
Some public functions are not documented here but they are for internal use only.
|
|
||||||
|
|
||||||
## Technical differences from ChatCommandBuilder
|
|
||||||
|
|
||||||
* subcommand aditional specific privs
|
|
||||||
* new types: `json`, `color`, `nodename` an maybe more in the future
|
|
||||||
|
|
||||||
|
|
||||||
## Public Functions
|
|
||||||
|
|
||||||
### `mcl_commands.register_command("exemple", def)`
|
|
||||||
|
|
||||||
This is a function which is called when an item is dispensed by the dispenser.
|
|
||||||
These are the parameters:
|
|
||||||
|
|
||||||
```
|
|
||||||
mcl_commands.register_command("exemple", {
|
|
||||||
func = function(cmd) --function executed on registration
|
|
||||||
cmd:sub(":name:username title :params:json", { --create a new subcommand called "title" with defined patterns
|
|
||||||
func = function(name, target, json)
|
|
||||||
return a_cool_function(target, json) --function executed if the params match the patterns
|
|
||||||
end,
|
|
||||||
privs = {settime = true}, --subcommand aditional specific privs
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
description = "Controls text displayed on the screen.", --the description of the command
|
|
||||||
params = "<target> command <params>", --very basic explaination of the syntax of the command (will be semi-automatic in the future)
|
|
||||||
privs = {server = true}, --global privs
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Register a complex chatcommand. If a chat command with the same name is already registered, the program will fail.
|
|
||||||
|
|
||||||
### `mcl_commands.overide_command("exemple", def)`
|
|
||||||
|
|
||||||
Same as above but will overide existing command.
|
|
||||||
|
|
||||||
### `mcl_commands.register_chatcommand_alias(alias, cmd, [bypass])`
|
|
||||||
|
|
||||||
Register an alias called `alias` of the `cmd` command.
|
|
||||||
If the setting `mcl_builtin_commands_overide` is set to `false`, the function will silently fail.
|
|
||||||
If `bypass` is set to `true` the function will not take care of the above setting.
|
|
||||||
Will warn if trying to alias to already existing command.
|
|
||||||
|
|
||||||
### `mcl_commands.rename_chatcommand(newname, cmd, [bypass])`
|
|
||||||
|
|
||||||
Rename `cmd` command to `newname`.
|
|
||||||
If the setting `mcl_builtin_commands_overide` is set to `false`, the function will silently fail.
|
|
||||||
If `bypass` is set to `true` the function will not take care of the above setting.
|
|
||||||
Will warn if trying to rename to already existing command.
|
|
||||||
|
|
||||||
### paterns
|
|
||||||
|
|
||||||
mcl_commands adds many types for patterns
|
|
||||||
If not specified, a value will be by default with the word pattern.
|
|
||||||
|
|
||||||
* pos value must be pos
|
|
||||||
* text value must be text WARNING: this pattern must be the last pattern of the subcommand!!
|
|
||||||
* number value must be number
|
|
||||||
* int value must be integer
|
|
||||||
* word value must be word
|
|
||||||
* alpha value must be alphanumeric
|
|
||||||
* modname value must be a valid modname
|
|
||||||
* alphascore
|
|
||||||
* alphanumeric
|
|
||||||
* username: value must be a valid username
|
|
||||||
* json: value must be a json string (will be parsed automaticaly)
|
|
||||||
* color value must be a color string or a valid named color
|
|
||||||
* nodename value must be a valid (existing) node or item name
|
|
|
@ -1,374 +0,0 @@
|
||||||
local S = minetest.get_translator("mcl_commands")
|
|
||||||
local C = minetest.colorize
|
|
||||||
|
|
||||||
local mod_death_messages = minetest.get_modpath("mcl_death_messages")
|
|
||||||
|
|
||||||
local parse_json = minetest.parse_json
|
|
||||||
local get_modpath = minetest.get_modpath
|
|
||||||
|
|
||||||
mcl_commands.types = {
|
|
||||||
pos = {"%(? *(%-?[%d.]+) *,? *(%-?[%d.]+) *,? *(%-?[%d.]+) *%)?",
|
|
||||||
function(res, pointer)
|
|
||||||
local pos = {
|
|
||||||
x = tonumber(res[pointer]),
|
|
||||||
y = tonumber(res[pointer + 1]),
|
|
||||||
z = tonumber(res[pointer + 2])
|
|
||||||
}
|
|
||||||
if pos.x and pos.y and pos.z then
|
|
||||||
return nil, pos, pointer+3
|
|
||||||
else
|
|
||||||
return S("Pos is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
text = {"(.+)",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] == tostring(res[pointer]) then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Text is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
number = {"(%-?[%d.]+)}",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] == tonumber(res[pointer]) then
|
|
||||||
return nil, tonumber(res[pointer]), pointer+1
|
|
||||||
else
|
|
||||||
return S("Number is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
int = {"(%-?[%d]+)}",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] == math.floor(tonumber(res[pointer])) then
|
|
||||||
return nil, tonumber(res[pointer]), pointer+1
|
|
||||||
else
|
|
||||||
return S("Int is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
word = {"([^ ]+)",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] == tostring(res[pointer]) then
|
|
||||||
return nil, tostring(res[pointer]), pointer+1
|
|
||||||
else
|
|
||||||
return S("Word is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
quotestring = {'"+(.-)"+',
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] == tostring(res[pointer]) then
|
|
||||||
return nil, string.sub(tostring(res[pointer]), 2, #res[pointer]-1), pointer+1
|
|
||||||
else
|
|
||||||
return S("String is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
alpha = {"([A-Za-z]+)}",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Alpha is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
modname = {"([a-z0-9_]+)}",
|
|
||||||
function(res, pointer)
|
|
||||||
if get_modpath(res[pointer]) then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Modname is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
alphascore = {"([A-Za-z_]+)",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] then --What is alphascore?
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Alphascore is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
alphanumeric = {"([A-Za-z0-9]+)}",
|
|
||||||
function(res, pointer)
|
|
||||||
if res[pointer] then --What is alphanumerical?
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Alphanumerical is invalid!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
username = {"([A-Za-z0-9-_]+)",
|
|
||||||
function(res, pointer)
|
|
||||||
--if minetest.player_exists(res[pointer]) then
|
|
||||||
if res[pointer] then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Player doesn't exist.")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
target = {"([A-Za-z0-9-_]+)",
|
|
||||||
function(res, pointer)
|
|
||||||
--if minetest.player_exists(res[pointer]) then
|
|
||||||
if res[pointer] then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Player doesn't exist.")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
json = {"(.+)", --FIXME
|
|
||||||
function(res, pointer)
|
|
||||||
local parsed = parse_json(res[pointer])
|
|
||||||
if parsed then
|
|
||||||
return nil, parsed, pointer+1
|
|
||||||
else
|
|
||||||
return S("Json failed to parse!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
color = {"([^ ]+)", --FIXME
|
|
||||||
function(res, pointer)
|
|
||||||
local color = mcl_util.get_color(res[pointer])
|
|
||||||
if color then
|
|
||||||
return nil, color, pointer+1
|
|
||||||
else
|
|
||||||
return S("Color is not a valid color name or hexadecimal!")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
nodename = {"(%l+[%w_]+%:?[_%l]+[%w_]*)",
|
|
||||||
function(res, pointer)
|
|
||||||
if minetest.registered_items[res[pointer]] then
|
|
||||||
return nil, res[pointer], pointer+1
|
|
||||||
else
|
|
||||||
return S("Nodename is invalid")
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
}
|
|
||||||
|
|
||||||
function mcl_commands.register_command(name, def)
|
|
||||||
def = def or {}
|
|
||||||
local cmd = mcl_commands.build(name, def)
|
|
||||||
if minetest.registered_chatcommands[name] then
|
|
||||||
error("[mcl_commands] Failed to register command: ["..name.."] command already existing! Use mcl_commands.overide_command() if you want to overide existing command")
|
|
||||||
end
|
|
||||||
minetest.register_chatcommand(name, cmd)
|
|
||||||
minetest.log("action", "[mcl_commands] ["..name.."] command registered successfully")
|
|
||||||
return cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_commands.override_command(name, def)
|
|
||||||
def = def or {}
|
|
||||||
local cmd = mcl_commands.build(name, def)
|
|
||||||
if minetest.registered_chatcommands[name] then
|
|
||||||
minetest.unregister_chatcommand(name)
|
|
||||||
end
|
|
||||||
minetest.register_chatcommand(name, cmd)
|
|
||||||
minetest.log("action", "[mcl_commands] ["..name.."] command overridden successfully")
|
|
||||||
return cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
local STATE_READY = 1
|
|
||||||
local STATE_PARAM = 2
|
|
||||||
local STATE_PARAM_TYPE = 3
|
|
||||||
local bad_chars = {"(", ")", ".", "%", "+", "-", "*", "?", "[", "^", "$"}
|
|
||||||
local function escape(char)
|
|
||||||
if bad_chars[char] then
|
|
||||||
return "%" .. char
|
|
||||||
else
|
|
||||||
return char
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local dprint = function() end
|
|
||||||
|
|
||||||
function mcl_commands.build(name, chat_def)
|
|
||||||
local cmd = {
|
|
||||||
_subs = {}
|
|
||||||
}
|
|
||||||
function cmd:sub(route, def)
|
|
||||||
dprint("Parsing " .. route)
|
|
||||||
|
|
||||||
if string.trim then
|
|
||||||
route = string.trim(route)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sub = {
|
|
||||||
pattern = "^",
|
|
||||||
params = {},
|
|
||||||
func = def.func,
|
|
||||||
privs = def.privs or {},
|
|
||||||
desc = def.desc,
|
|
||||||
params_desc = def.params or "",
|
|
||||||
}
|
|
||||||
|
|
||||||
-- End of param reached: add it to the pattern
|
|
||||||
local param = ""
|
|
||||||
local param_type = ""
|
|
||||||
local should_be_eos = false
|
|
||||||
local function finishParam()
|
|
||||||
if param ~= "" and param_type ~= "" then
|
|
||||||
dprint(" - Found param " .. param .. " type " .. param_type)
|
|
||||||
|
|
||||||
local pattern = mcl_commands.types[param_type][1]
|
|
||||||
if not pattern then
|
|
||||||
error("Unrecognised param_type=" .. param_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
sub.pattern = sub.pattern .. pattern
|
|
||||||
|
|
||||||
table.insert(sub.params, param_type)
|
|
||||||
|
|
||||||
param = ""
|
|
||||||
param_type = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Iterate through the route to find params
|
|
||||||
local state = STATE_READY
|
|
||||||
local catching_space = false
|
|
||||||
local match_space = " " -- change to "%s" to also catch tabs and newlines
|
|
||||||
local catch_space = match_space.."+"
|
|
||||||
for i = 1, #route do
|
|
||||||
local c = route:sub(i, i)
|
|
||||||
if should_be_eos then
|
|
||||||
error("Should be end of string. Nothing is allowed after a param of type text.")
|
|
||||||
end
|
|
||||||
|
|
||||||
if state == STATE_READY then
|
|
||||||
if c == ":" then
|
|
||||||
dprint(" - Found :, entering param")
|
|
||||||
state = STATE_PARAM
|
|
||||||
param_type = "word"
|
|
||||||
catching_space = false
|
|
||||||
elseif c:match(match_space) then
|
|
||||||
print(" - Found space")
|
|
||||||
if not catching_space then
|
|
||||||
catching_space = true
|
|
||||||
sub.pattern = sub.pattern .. catch_space
|
|
||||||
end
|
|
||||||
else
|
|
||||||
catching_space = false
|
|
||||||
sub.pattern = sub.pattern .. escape(c)
|
|
||||||
end
|
|
||||||
elseif state == STATE_PARAM then
|
|
||||||
if c == ":" then
|
|
||||||
dprint(" - Found :, entering param type")
|
|
||||||
state = STATE_PARAM_TYPE
|
|
||||||
param_type = ""
|
|
||||||
elseif c:match(match_space) then
|
|
||||||
print(" - Found whitespace, leaving param")
|
|
||||||
state = STATE_READY
|
|
||||||
finishParam()
|
|
||||||
catching_space = true
|
|
||||||
sub.pattern = sub.pattern .. catch_space
|
|
||||||
elseif c:match("%W") then
|
|
||||||
dprint(" - Found nonalphanum, leaving param")
|
|
||||||
state = STATE_READY
|
|
||||||
finishParam()
|
|
||||||
sub.pattern = sub.pattern .. escape(c)
|
|
||||||
else
|
|
||||||
param = param .. c
|
|
||||||
end
|
|
||||||
elseif state == STATE_PARAM_TYPE then
|
|
||||||
if c:match(match_space) then
|
|
||||||
print(" - Found space, leaving param type")
|
|
||||||
state = STATE_READY
|
|
||||||
finishParam()
|
|
||||||
catching_space = true
|
|
||||||
sub.pattern = sub.pattern .. catch_space
|
|
||||||
elseif c:match("%W") then
|
|
||||||
dprint(" - Found nonalphanum, leaving param type")
|
|
||||||
state = STATE_READY
|
|
||||||
finishParam()
|
|
||||||
sub.pattern = sub.pattern .. escape(c)
|
|
||||||
else
|
|
||||||
param_type = param_type .. c
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
dprint(" - End of route")
|
|
||||||
finishParam()
|
|
||||||
sub.pattern = sub.pattern .. "$"
|
|
||||||
dprint("Pattern: " .. sub.pattern)
|
|
||||||
|
|
||||||
table.insert(self._subs, sub)
|
|
||||||
end
|
|
||||||
|
|
||||||
if chat_def.func then
|
|
||||||
chat_def.func(cmd)
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.func = function(name, param)
|
|
||||||
local msg
|
|
||||||
for i = 1, #cmd._subs do
|
|
||||||
local sub = cmd._subs[i]
|
|
||||||
local res = { string.match(param, sub.pattern) }
|
|
||||||
if #res > 0 then
|
|
||||||
local pointer = 1
|
|
||||||
local params = { name }
|
|
||||||
for j = 1, #sub.params do
|
|
||||||
local param = sub.params[j]
|
|
||||||
local value
|
|
||||||
if mcl_commands.types[param] then
|
|
||||||
msg, value, pointer = mcl_commands.check_type(param, res, pointer)
|
|
||||||
table.insert(params, value)
|
|
||||||
else
|
|
||||||
table.insert(params, res[pointer])
|
|
||||||
pointer = pointer + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local can_execute, missing_privs = minetest.check_player_privs(name, sub.privs)
|
|
||||||
if can_execute then
|
|
||||||
if table.unpack then
|
|
||||||
-- lua 5.2 or later
|
|
||||||
return sub.func(table.unpack(params))
|
|
||||||
else
|
|
||||||
-- lua 5.1 or earlier
|
|
||||||
return sub.func(unpack(params))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local missing_privs_str = ""
|
|
||||||
for i = 1, #missing_privs do
|
|
||||||
if not i == #missing_privs then
|
|
||||||
missing_privs_str = missing_privs_str..missing_privs[i].." "
|
|
||||||
else
|
|
||||||
missing_privs_str = missing_privs_str..missing_privs[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false, C(mcl_colors.RED, S("You don't have permission to run this command (missing privilege: "..missing_privs_str..")"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false, C(mcl_colors.RED, msg)
|
|
||||||
end
|
|
||||||
if chat_def.params then
|
|
||||||
cmd.params = chat_def.params
|
|
||||||
else
|
|
||||||
cmd.params = ""
|
|
||||||
end
|
|
||||||
cmd.privs = chat_def.privs
|
|
||||||
cmd.description = chat_def.description
|
|
||||||
return cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_commands.check_type(type, res, pointer)
|
|
||||||
return mcl_commands.types[type][2](res, pointer)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_commands.register_chatcommand_alias(alias, cmd, bypass)
|
|
||||||
if not bypass then bypass = false end
|
|
||||||
if minetest.registered_chatcommands[alias] then
|
|
||||||
minetest.log("warning", "[mcl_commands] trying to alias ["..cmd.."] to already existing ["..alias.."] command")
|
|
||||||
elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then
|
|
||||||
minetest.register_chatcommand(alias, minetest.chatcommands[cmd])
|
|
||||||
minetest.log("action", "[mcl_commands] ["..cmd.."] command aliased successfully to ["..alias.."]")
|
|
||||||
else
|
|
||||||
minetest.log("action", "[mcl_commands] ["..cmd.."] command not aliased to ["..alias.."]")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_commands.rename_chatcommand(newname, cmd, bypass)
|
|
||||||
if not bypass then bypass = false end
|
|
||||||
if minetest.registered_chatcommands[newname] then
|
|
||||||
minetest.log("warning", "[mcl_commands] trying to rename ["..cmd.."] to already existing ["..alias.."] command")
|
|
||||||
elseif minetest.settings:get_bool("mcl_builtin_commands_overide", true) or bypass then
|
|
||||||
minetest.register_chatcommand(newname, minetest.chatcommands[cmd])
|
|
||||||
minetest.unregister_chatcommand(cmd)
|
|
||||||
minetest.log("action", "[mcl_commands] ["..cmd.."] command renamed successfully to ["..newname.."]")
|
|
||||||
else
|
|
||||||
minetest.log("action", "[mcl_commands] ["..cmd.."] command not renamed to ["..newname.."]")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,13 +0,0 @@
|
||||||
--mcl_commands
|
|
||||||
--derivated from ChatCommandBuilder by @rubenwardy
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mcl_commands")
|
|
||||||
|
|
||||||
local mod_death_messages = minetest.get_modpath("mcl_death_messages")
|
|
||||||
|
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
|
||||||
|
|
||||||
mcl_commands = {}
|
|
||||||
|
|
||||||
dofile(modpath.."/api.lua")
|
|
||||||
dofile(modpath.."/utils.lua")
|
|
|
@ -1,6 +0,0 @@
|
||||||
name = mcl_commands
|
|
||||||
author = AFCMS
|
|
||||||
description = MCL2 commands API
|
|
||||||
depends = mcl_colors, mcl_util
|
|
||||||
optional_depends =
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
local S = minetest.get_translator("mcl_commands")
|
|
||||||
|
|
||||||
function mcl_commands.get_target_selector(target, pos, name)
|
|
||||||
if minetest.player_exists(target) then
|
|
||||||
return nil, minetest.get_player_by_name(target)
|
|
||||||
elseif target == "@a" then
|
|
||||||
return nil, minetest.get_connected_players()
|
|
||||||
elseif target == "@r" then
|
|
||||||
local connected = minetest.get_connected_players()
|
|
||||||
return nil, connected[math.random(1, #connected)]
|
|
||||||
elseif target == "@s" then
|
|
||||||
if name then
|
|
||||||
local player = minetest.get_player_by_name(name)
|
|
||||||
end
|
|
||||||
if player then
|
|
||||||
return nil, player
|
|
||||||
else
|
|
||||||
return S("Not a valid player")
|
|
||||||
end
|
|
||||||
elseif target == "@p" then
|
|
||||||
local smallest = math.huge
|
|
||||||
local nearest
|
|
||||||
for _,player in pairs(minetest.get_connected_players()) do
|
|
||||||
local distance = vector.distance(pos, player:get_pos())
|
|
||||||
if distance < min_distance then
|
|
||||||
min_distance = distance
|
|
||||||
nearest = player
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if nearest then
|
|
||||||
return nil, nearest
|
|
||||||
else
|
|
||||||
return S("No player online")
|
|
||||||
end
|
|
||||||
elseif target == "@e" then
|
|
||||||
return minetest.luaentities --TODO: add filtering of not valid entities.
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
mcl_damage = {
|
||||||
|
modifiers = {},
|
||||||
|
damage_callbacks = {},
|
||||||
|
death_callbacks = {},
|
||||||
|
types = {
|
||||||
|
in_fire = {is_fire = true},
|
||||||
|
lightning_bolt = {is_lightning = true},
|
||||||
|
on_fire = {is_fire = true, bypasses_armor = true},
|
||||||
|
lava = {is_fire = true},
|
||||||
|
hot_floor = {is_fire = true},
|
||||||
|
in_wall = {bypasses_armor = true},
|
||||||
|
drown = {bypasses_armor = true},
|
||||||
|
starve = {bypasses_armor = true, bypasses_magic = true},
|
||||||
|
cactus = {},
|
||||||
|
fall = {bypasses_armor = true},
|
||||||
|
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||||
|
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true},
|
||||||
|
generic = {bypasses_armor = true},
|
||||||
|
magic = {is_magic = true, bypasses_armor = true},
|
||||||
|
dragon_breath = {is_magic = true, bypasses_armor = true}, -- this is only used for dragon fireball; dragon fireball does not actually deal impact damage tho, so this is unreachable
|
||||||
|
wither = {bypasses_armor = true}, -- unused
|
||||||
|
wither_skull = {is_magic = true, is_explosion = true}, -- this is non-MC but a workaround to get the proper death message
|
||||||
|
anvil = {},
|
||||||
|
falling_node = {}, -- this is falling_block in MC
|
||||||
|
mob = {},
|
||||||
|
player = {},
|
||||||
|
arrow = {is_projectile = true},
|
||||||
|
fireball = {is_projectile = true, is_fire = true},
|
||||||
|
thorns = {is_magic = true},
|
||||||
|
explosion = {is_explosion = true},
|
||||||
|
cramming = {bypasses_armor = true}, -- unused
|
||||||
|
fireworks = {is_explosion = true}, -- unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mcl_damage.register_modifier(func, priority)
|
||||||
|
table.insert(mcl_damage.modifiers, {func = func, priority = priority or 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.register_on_damage(func)
|
||||||
|
table.insert(mcl_damage.damage_callbacks, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.register_on_death(func)
|
||||||
|
table.insert(mcl_damage.death_callbacks, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.run_modifiers(obj, damage, reason)
|
||||||
|
for _, modf in ipairs(mcl_damage.modifiers) do
|
||||||
|
damage = modf.func(obj, damage, reason) or damage
|
||||||
|
if damage == 0 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return damage
|
||||||
|
end
|
||||||
|
|
||||||
|
local function run_callbacks(funcs, ...)
|
||||||
|
for _, func in pairs(funcs) do
|
||||||
|
func(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.run_damage_callbacks(obj, damage, reason)
|
||||||
|
run_callbacks(mcl_damage.damage_callbacks, obj, damage, reason)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.run_death_callbacks(obj, reason)
|
||||||
|
run_callbacks(mcl_damage.death_callbacks, obj, reason)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.from_punch(mcl_reason, object)
|
||||||
|
mcl_reason.direct = object
|
||||||
|
local luaentity = mcl_reason.direct:get_luaentity()
|
||||||
|
if luaentity then
|
||||||
|
if luaentity._is_arrow then
|
||||||
|
mcl_reason.type = "arrow"
|
||||||
|
elseif luaentity._is_fireball then
|
||||||
|
mcl_reason.type = "fireball"
|
||||||
|
elseif luaentity._cmi_is_mob then
|
||||||
|
mcl_reason.type = "mob"
|
||||||
|
end
|
||||||
|
mcl_reason.source = mcl_reason.source or luaentity._source_object
|
||||||
|
else
|
||||||
|
mcl_reason.type = "player"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.finish_reason(mcl_reason)
|
||||||
|
mcl_reason.source = mcl_reason.source or mcl_reason.direct
|
||||||
|
mcl_reason.flags = mcl_damage.types[mcl_reason.type]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.from_mt(mt_reason)
|
||||||
|
if mt_reason._mcl_chached_reason then
|
||||||
|
return mt_reason._mcl_chached_reason
|
||||||
|
end
|
||||||
|
|
||||||
|
local mcl_reason
|
||||||
|
|
||||||
|
if mt_reason._mcl_reason then
|
||||||
|
mcl_reason = mt_reason._mcl_reason
|
||||||
|
else
|
||||||
|
mcl_reason = {type = "generic"}
|
||||||
|
|
||||||
|
if mt_reason._mcl_type then
|
||||||
|
mcl_reason.type = mt_reason._mcl_type
|
||||||
|
elseif mt_reason.type == "fall" then
|
||||||
|
mcl_reason.type = "fall"
|
||||||
|
elseif mt_reason.type == "drown" then
|
||||||
|
mcl_reason.type = "drown"
|
||||||
|
elseif mt_reason.type == "punch" then
|
||||||
|
mcl_damage.from_punch(mcl_reason, mt_reason.object)
|
||||||
|
elseif mt_reason.type == "node_damage" and mt_reason.node then
|
||||||
|
if minetest.get_item_group(mt_reason.node, "fire") > 0 then
|
||||||
|
mcl_reason.type = "in_fire"
|
||||||
|
end
|
||||||
|
if minetest.get_item_group(mt_reason.node, "lava") > 0 then
|
||||||
|
mcl_reason.type = "lava"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for key, value in pairs(mt_reason) do
|
||||||
|
if key:find("_mcl_") == 1 then
|
||||||
|
mcl_reason[key:sub(6, #key)] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_damage.finish_reason(mcl_reason)
|
||||||
|
mt_reason._mcl_cached_reason = mcl_reason
|
||||||
|
|
||||||
|
return mcl_reason
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_damage.register_type(name, def)
|
||||||
|
mcl_damage.types[name] = def
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
|
if hp_change < 0 then
|
||||||
|
if player:get_hp() <= 0 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
hp_change = -mcl_damage.run_modifiers(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
||||||
|
end
|
||||||
|
return hp_change
|
||||||
|
end, true)
|
||||||
|
|
||||||
|
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
|
||||||
|
if player:get_hp() > 0 then
|
||||||
|
mt_reason.approved = true
|
||||||
|
if hp_change < 0 then
|
||||||
|
mcl_damage.run_damage_callbacks(player, -hp_change, mcl_damage.from_mt(mt_reason))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, false)
|
||||||
|
|
||||||
|
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))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_mods_loaded(function()
|
||||||
|
table.sort(mcl_damage.modifiers, function(a, b) return a.priority < b.priority end)
|
||||||
|
end)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = mcl_damage
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Minecraft-like damage reason system
|
|
@ -12,11 +12,12 @@ under the LGPLv2.1 license.
|
||||||
|
|
||||||
mcl_explosions = {}
|
mcl_explosions = {}
|
||||||
|
|
||||||
local mod_death_messages = minetest.get_modpath("mcl_death_messages") ~= nil
|
local mod_fire = minetest.get_modpath("mcl_fire")
|
||||||
local mod_fire = minetest.get_modpath("mcl_fire") ~= nil
|
--local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
||||||
local CONTENT_FIRE = minetest.get_content_id("mcl_fire:fire")
|
|
||||||
|
|
||||||
local S = minetest.get_translator("mcl_explosions")
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
local table = table
|
||||||
|
|
||||||
local hash_node_position = minetest.hash_node_position
|
local hash_node_position = minetest.hash_node_position
|
||||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
@ -27,6 +28,7 @@ local get_voxel_manip = minetest.get_voxel_manip
|
||||||
local bulk_set_node = minetest.bulk_set_node
|
local bulk_set_node = minetest.bulk_set_node
|
||||||
local check_for_falling = minetest.check_for_falling
|
local check_for_falling = minetest.check_for_falling
|
||||||
local add_item = minetest.add_item
|
local add_item = minetest.add_item
|
||||||
|
local pos_to_string = minetest.pos_to_string
|
||||||
|
|
||||||
-- Saved sphere explosion shapes for various radiuses
|
-- Saved sphere explosion shapes for various radiuses
|
||||||
local sphere_shapes = {}
|
local sphere_shapes = {}
|
||||||
|
@ -67,46 +69,44 @@ local function compute_sphere_rays(radius)
|
||||||
local rays = {}
|
local rays = {}
|
||||||
local sphere = {}
|
local sphere = {}
|
||||||
|
|
||||||
for i=1, 2 do
|
local function add_ray(pos)
|
||||||
|
sphere[hash_node_position(pos)] = pos
|
||||||
|
end
|
||||||
|
|
||||||
|
for y = -radius, radius do
|
||||||
|
for z = -radius, radius do
|
||||||
|
for x = -radius, 0 do
|
||||||
|
local d = x * x + y * y + z * z
|
||||||
|
if d <= radius * radius then
|
||||||
|
add_ray(vector.new(x, y, z))
|
||||||
|
add_ray(vector.new(-x, y, z))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = -radius, radius do
|
||||||
|
for z = -radius, radius do
|
||||||
|
for y = -radius, 0 do
|
||||||
|
local d = x * x + y * y + z * z
|
||||||
|
if d <= radius * radius then
|
||||||
|
add_ray(vector.new(x, y, z))
|
||||||
|
add_ray(vector.new(x, -y, z))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = -radius, radius do
|
||||||
for y = -radius, radius do
|
for y = -radius, radius do
|
||||||
for z = -radius, radius do
|
for z = -radius, 0 do
|
||||||
for x = -radius, 0, 1 do
|
local d = x * x + y * y + z * z
|
||||||
local d = x * x + y * y + z * z
|
if d <= radius * radius then
|
||||||
if d <= radius * radius then
|
add_ray(vector.new(x, y, z))
|
||||||
local pos = { x = x, y = y, z = z }
|
add_ray(vector.new(x, y, -z))
|
||||||
sphere[hash_node_position(pos)] = pos
|
break
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=1,2 do
|
|
||||||
for x = -radius, radius do
|
|
||||||
for z = -radius, radius do
|
|
||||||
for y = -radius, 0, 1 do
|
|
||||||
local d = x * x + y * y + z * z
|
|
||||||
if d <= radius * radius then
|
|
||||||
local pos = { x = x, y = y, z = z }
|
|
||||||
sphere[hash_node_position(pos)] = pos
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=1,2 do
|
|
||||||
for x = -radius, radius do
|
|
||||||
for y = -radius, radius do
|
|
||||||
for z = -radius, 0, 1 do
|
|
||||||
local d = x * x + y * y + z * z
|
|
||||||
if d <= radius * radius then
|
|
||||||
local pos = { x = x, y = y, z = z }
|
|
||||||
sphere[hash_node_position(pos)] = pos
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -150,7 +150,8 @@ end
|
||||||
-- raydirs - The directions for each ray
|
-- raydirs - The directions for each ray
|
||||||
-- radius - The maximum distance each ray will go
|
-- radius - The maximum distance each ray will go
|
||||||
-- info - Table containing information about explosion
|
-- info - Table containing information about explosion
|
||||||
-- puncher - object that punches other objects (optional)
|
-- direct - direct source object of the damage (optional)
|
||||||
|
-- source - indirect source object of the damage (optional)
|
||||||
--
|
--
|
||||||
-- Values in info:
|
-- Values in info:
|
||||||
-- drop_chance - The chance that destroyed nodes will drop their items
|
-- drop_chance - The chance that destroyed nodes will drop their items
|
||||||
|
@ -165,7 +166,7 @@ end
|
||||||
-- Note that this function has been optimized, it contains code which has been
|
-- Note that this function has been optimized, it contains code which has been
|
||||||
-- inlined to avoid function calls and unnecessary table creation. This was
|
-- inlined to avoid function calls and unnecessary table creation. This was
|
||||||
-- measured to give a significant performance increase.
|
-- measured to give a significant performance increase.
|
||||||
local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
local function trace_explode(pos, strength, raydirs, radius, info, direct, source)
|
||||||
local vm = get_voxel_manip()
|
local vm = get_voxel_manip()
|
||||||
|
|
||||||
local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
|
local emin, emax = vm:read_from_map(vector.subtract(pos, radius),
|
||||||
|
@ -176,14 +177,11 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
|
|
||||||
local ystride = (emax.x - emin_x + 1)
|
local ystride = (emax.x - emin_x + 1)
|
||||||
local zstride = ystride * (emax.y - emin_y + 1)
|
local zstride = ystride * (emax.y - emin_y + 1)
|
||||||
local pos_x = pos.x
|
|
||||||
local pos_y = pos.y
|
|
||||||
local pos_z = pos.z
|
|
||||||
|
|
||||||
local area = VoxelArea:new {
|
--[[local area = VoxelArea:new {
|
||||||
MinEdge = emin,
|
MinEdge = emin,
|
||||||
MaxEdge = emax
|
MaxEdge = emax
|
||||||
}
|
}]]
|
||||||
local data = vm:get_data()
|
local data = vm:get_data()
|
||||||
local destroy = {}
|
local destroy = {}
|
||||||
|
|
||||||
|
@ -212,7 +210,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
npos_x - emin_x + 1
|
npos_x - emin_x + 1
|
||||||
|
|
||||||
local cid = data[idx]
|
local cid = data[idx]
|
||||||
local br = node_blastres[cid]
|
local br = node_blastres[cid] or INDESTRUCT_BLASTRES
|
||||||
if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
|
if br < INDESTRUCT_BLASTRES and br > max_blast_resistance then
|
||||||
br = max_blast_resistance
|
br = max_blast_resistance
|
||||||
end
|
end
|
||||||
|
@ -247,7 +245,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
local ent = obj:get_luaentity()
|
local ent = obj:get_luaentity()
|
||||||
|
|
||||||
-- Ignore items to lower lag
|
-- Ignore items to lower lag
|
||||||
if obj:is_player() or (ent and ent.name ~= '__builtin.item') then
|
if (obj:is_player() or (ent and ent.name ~= "__builtin.item")) and obj:get_hp() > 0 then
|
||||||
local opos = obj:get_pos()
|
local opos = obj:get_pos()
|
||||||
local collisionbox = nil
|
local collisionbox = nil
|
||||||
|
|
||||||
|
@ -260,12 +258,12 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
|
|
||||||
if collisionbox then
|
if collisionbox then
|
||||||
-- Create rays from random points in the collision box
|
-- Create rays from random points in the collision box
|
||||||
local x1 = collisionbox[1] * 2
|
local x1 = collisionbox[1]
|
||||||
local y1 = collisionbox[2] * 2
|
local y1 = collisionbox[2]
|
||||||
local z1 = collisionbox[3] * 2
|
local z1 = collisionbox[3]
|
||||||
local x2 = collisionbox[4] * 2
|
local x2 = collisionbox[4]
|
||||||
local y2 = collisionbox[5] * 2
|
local y2 = collisionbox[5]
|
||||||
local z2 = collisionbox[6] * 2
|
local z2 = collisionbox[6]
|
||||||
local x_len = math.abs(x2 - x1)
|
local x_len = math.abs(x2 - x1)
|
||||||
local y_len = math.abs(y2 - y1)
|
local y_len = math.abs(y2 - y1)
|
||||||
local z_len = math.abs(z2 - z1)
|
local z_len = math.abs(z2 - z1)
|
||||||
|
@ -321,7 +319,6 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
impact = 0
|
impact = 0
|
||||||
end
|
end
|
||||||
local damage = math.floor((impact * impact + impact) * 7 * strength + 1)
|
local damage = math.floor((impact * impact + impact) * 7 * strength + 1)
|
||||||
local source = puncher or obj
|
|
||||||
|
|
||||||
local sleep_formspec_doesnt_close_mt53 = false
|
local sleep_formspec_doesnt_close_mt53 = false
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
|
@ -333,26 +330,22 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
sleep_formspec_doesnt_close_mt53 = true
|
sleep_formspec_doesnt_close_mt53 = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if mod_death_messages then
|
|
||||||
mcl_death_messages.player_damage(obj, S("@1 was caught in an explosion.", name))
|
|
||||||
end
|
|
||||||
if rawget(_G, "armor") and armor.last_damage_types then
|
|
||||||
armor.last_damage_types[name] = "explosion"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if sleep_formspec_doesnt_close_mt53 then
|
if sleep_formspec_doesnt_close_mt53 then
|
||||||
minetest.after(0.3, function(obj, damage, impact, punch_dir) -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
minetest.after(0.3, function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
||||||
if not obj then return end
|
if not obj:is_player() then
|
||||||
obj:punch(obj, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir)
|
return
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
end
|
||||||
end, obj, damage, impact, vector.new(punch_dir))
|
|
||||||
else
|
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||||
obj:punch(source, 10, { damage_groups = { full_punch_interval = 1, fleshy = damage, knockback = impact * 20.0 } }, punch_dir)
|
|
||||||
|
|
||||||
if obj:is_player() then
|
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
elseif ent.tnt_knockback then
|
end)
|
||||||
|
else
|
||||||
|
mcl_util.deal_damage(obj, damage, {type = "explosion", direct = direct, source = source})
|
||||||
|
|
||||||
|
if obj:is_player() or ent.tnt_knockback then
|
||||||
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
obj:add_velocity(vector.multiply(punch_dir, impact * 20))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -368,9 +361,9 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
local on_blast = node_on_blast[data[idx]]
|
local on_blast = node_on_blast[data[idx]]
|
||||||
local remove = true
|
local remove = true
|
||||||
|
|
||||||
if do_drop or on_blast ~= nil then
|
if do_drop or on_blast then
|
||||||
local npos = get_position_from_hash(hash)
|
local npos = get_position_from_hash(hash)
|
||||||
if on_blast ~= nil then
|
if on_blast then
|
||||||
on_blast(npos, 1.0, do_drop)
|
on_blast(npos, 1.0, do_drop)
|
||||||
remove = false
|
remove = false
|
||||||
else
|
else
|
||||||
|
@ -412,8 +405,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, puncher)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Log explosion
|
-- Log explosion
|
||||||
minetest.log('action', 'Explosion at ' .. minetest.pos_to_string(pos) ..
|
minetest.log("action", "Explosion at "..pos_to_string(pos).." with strength "..strength.." and radius "..radius)
|
||||||
' with strength ' .. strength .. ' and radius ' .. radius)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create an explosion with strength at pos.
|
-- Create an explosion with strength at pos.
|
||||||
|
@ -422,7 +414,8 @@ end
|
||||||
-- pos - The position where the explosion originates from
|
-- pos - The position where the explosion originates from
|
||||||
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
|
-- strength - The blast strength of the explosion (a TNT explosion uses 4)
|
||||||
-- info - Table containing information about explosion
|
-- info - Table containing information about explosion
|
||||||
-- puncher - object that is reported as source of punches/damage (optional)
|
-- direct - direct source object of the damage (optional)
|
||||||
|
-- source - indirect source object of the damage (optional)
|
||||||
--
|
--
|
||||||
-- Values in info:
|
-- Values in info:
|
||||||
-- drop_chance - If specified becomes the drop chance of all nodes in the
|
-- drop_chance - If specified becomes the drop chance of all nodes in the
|
||||||
|
@ -436,7 +429,7 @@ end
|
||||||
-- griefing - If true, the explosion will destroy nodes (default: true)
|
-- griefing - If true, the explosion will destroy nodes (default: true)
|
||||||
-- grief_protected - If true, the explosion will also destroy nodes which have
|
-- grief_protected - If true, the explosion will also destroy nodes which have
|
||||||
-- been protected (default: false)
|
-- been protected (default: false)
|
||||||
function mcl_explosions.explode(pos, strength, info, puncher)
|
function mcl_explosions.explode(pos, strength, info, direct, source)
|
||||||
if info == nil then
|
if info == nil then
|
||||||
info = {}
|
info = {}
|
||||||
end
|
end
|
||||||
|
@ -465,7 +458,7 @@ function mcl_explosions.explode(pos, strength, info, puncher)
|
||||||
info.drop_chance = 0
|
info.drop_chance = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
trace_explode(pos, strength, shape, radius, info, puncher)
|
trace_explode(pos, strength, shape, radius, info, direct, source)
|
||||||
|
|
||||||
if info.particles then
|
if info.particles then
|
||||||
add_particles(pos, radius)
|
add_particles(pos, radius)
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 wurde Opfer einer Explosion.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 a été pris dans une explosion.
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# textdomain:mcl_explosions
|
||||||
|
@1 was caught in an explosion.=@1 została wysadzona.
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 не удалось пережить взрыва.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=
|
|
|
@ -32,9 +32,9 @@ local singlenode = mg_name == "singlenode"
|
||||||
|
|
||||||
-- Calculate mapgen_edge_min/mapgen_edge_max
|
-- Calculate mapgen_edge_min/mapgen_edge_max
|
||||||
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize")) or 5)
|
||||||
mcl_vars.MAP_BLOCKSIZE = math.max(1, core.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, core.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
|
||||||
|
|
|
@ -40,10 +40,9 @@ function mcl_loot.get_loot(loot_definitions, pr)
|
||||||
total_weight = total_weight + (loot_definitions.items[i].weight or 1)
|
total_weight = total_weight + (loot_definitions.items[i].weight or 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local stacks_min = loot_definitions.stacks_min
|
--local stacks_min = loot_definitions.stacks_min or 1
|
||||||
local stacks_max = loot_definitions.stacks_max
|
--local stacks_max = loot_definitions.stacks_max or 1
|
||||||
if not stacks_min then stacks_min = 1 end
|
|
||||||
if not stacks_max then stacks_max = 1 end
|
|
||||||
local stacks = pr:next(loot_definitions.stacks_min, loot_definitions.stacks_max)
|
local stacks = pr:next(loot_definitions.stacks_min, loot_definitions.stacks_max)
|
||||||
for s=1, stacks do
|
for s=1, stacks do
|
||||||
local r = pr:next(1, total_weight)
|
local r = pr:next(1, total_weight)
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
local vector = vector
|
||||||
|
local table = table
|
||||||
|
|
||||||
|
local hash_node_position = minetest.hash_node_position
|
||||||
|
local add_particlespawner = minetest.add_particlespawner
|
||||||
|
local delete_particlespawner = minetest.delete_particlespawner
|
||||||
|
|
||||||
|
local ipairs = ipairs
|
||||||
|
|
||||||
mcl_particles = {}
|
mcl_particles = {}
|
||||||
|
|
||||||
-- Table of particlespawner IDs on a per-node hash basis
|
-- Table of particlespawner IDs on a per-node hash basis
|
||||||
|
@ -32,11 +41,11 @@ function mcl_particles.add_node_particlespawner(pos, particlespawner_definition,
|
||||||
if allowed_level == 0 or levels[level] > allowed_level then
|
if allowed_level == 0 or levels[level] > allowed_level then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local poshash = minetest.hash_node_position(pos)
|
local poshash = hash_node_position(pos)
|
||||||
if not poshash then
|
if not poshash then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local id = minetest.add_particlespawner(particlespawner_definition)
|
local id = add_particlespawner(particlespawner_definition)
|
||||||
if id == -1 then
|
if id == -1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -47,6 +56,8 @@ function mcl_particles.add_node_particlespawner(pos, particlespawner_definition,
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local add_node_particlespawner = mcl_particles.add_node_particlespawner
|
||||||
|
|
||||||
-- Deletes all particlespawners that are assigned to a node position.
|
-- Deletes all particlespawners that are assigned to a node position.
|
||||||
-- If no particlespawners exist for this position, nothing happens.
|
-- If no particlespawners exist for this position, nothing happens.
|
||||||
-- pos: Node positon. MUST use integer values!
|
-- pos: Node positon. MUST use integer values!
|
||||||
|
@ -55,14 +66,66 @@ function mcl_particles.delete_node_particlespawners(pos)
|
||||||
if allowed_level == 0 then
|
if allowed_level == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local poshash = minetest.hash_node_position(pos)
|
local poshash = hash_node_position(pos)
|
||||||
local ids = particle_nodes[poshash]
|
local ids = particle_nodes[poshash]
|
||||||
if ids then
|
if ids then
|
||||||
for i=1, #ids do
|
for i=1, #ids do
|
||||||
minetest.delete_particlespawner(ids[i])
|
delete_particlespawner(ids[i])
|
||||||
end
|
end
|
||||||
particle_nodes[poshash] = nil
|
particle_nodes[poshash] = nil
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 3 exptime variants because the animation is not tied to particle expiration time.
|
||||||
|
-- 3 colorized variants to imitate minecraft's
|
||||||
|
local smoke_pdef_cached = {}
|
||||||
|
|
||||||
|
function mcl_particles.spawn_smoke(pos, name, smoke_pdef_base)
|
||||||
|
local new_minpos = vector.add(pos, smoke_pdef_base.minrelpos)
|
||||||
|
local new_maxpos = vector.add(pos, smoke_pdef_base.maxrelpos)
|
||||||
|
|
||||||
|
-- populate the cache
|
||||||
|
if smoke_pdef_cached[name] then
|
||||||
|
for i, smoke_pdef in ipairs(smoke_pdef_cached[name]) do
|
||||||
|
smoke_pdef.minpos = new_minpos
|
||||||
|
smoke_pdef.maxpos = new_maxpos
|
||||||
|
add_node_particlespawner(pos, smoke_pdef, "high")
|
||||||
|
end
|
||||||
|
-- cache already populated
|
||||||
|
else
|
||||||
|
smoke_pdef_cached[name] = {}
|
||||||
|
|
||||||
|
local smoke_pdef = table.copy(smoke_pdef_base)
|
||||||
|
smoke_pdef.amount = smoke_pdef_base.amount / 9
|
||||||
|
smoke_pdef.time = 0
|
||||||
|
smoke_pdef.animation = {
|
||||||
|
type = "vertical_frames",
|
||||||
|
aspect_w = 8,
|
||||||
|
aspect_h = 8,
|
||||||
|
-- length = 3 exptime variants
|
||||||
|
}
|
||||||
|
smoke_pdef.collisiondetection = true
|
||||||
|
smoke_pdef.minpos = new_minpos
|
||||||
|
smoke_pdef.maxpos = new_maxpos
|
||||||
|
|
||||||
|
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
|
||||||
|
-- to have varying exptime for each variant.
|
||||||
|
local exptimes = { 0.175, 0.375, 1.0 }
|
||||||
|
local colorizes = { "199", "209", "243" } -- round(78%, 82%, 90% of 256) - 1
|
||||||
|
|
||||||
|
for _,exptime in ipairs(exptimes) do
|
||||||
|
for _,colorize in ipairs(colorizes) do
|
||||||
|
smoke_pdef.maxexptime = exptime * smoke_pdef_base.maxexptime
|
||||||
|
smoke_pdef.animation.length = exptime + 0.1
|
||||||
|
-- minexptime must be set such that the last frame is actully rendered,
|
||||||
|
-- even if its very short. Larger exptime -> larger range
|
||||||
|
smoke_pdef.minexptime = math.min(exptime, (7.0/8.0 * (exptime + 0.1) + 0.1))
|
||||||
|
smoke_pdef.texture = "mcl_particles_smoke_anim.png^[colorize:#000000:" ..colorize
|
||||||
|
add_node_particlespawner(pos, smoke_pdef, "high")
|
||||||
|
table.insert(smoke_pdef_cached[name], table.copy(smoke_pdef))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 148 B |
After Width: | Height: | Size: 154 B |
After Width: | Height: | Size: 155 B |
After Width: | Height: | Size: 165 B |
|
@ -1,11 +0,0 @@
|
||||||
mcl_util.registered_blacklisted_entities = {}
|
|
||||||
function mcl_util.register_blacklisted_entities(type)
|
|
||||||
mcl_util.registered_blacklisted_entities[type] = true
|
|
||||||
end
|
|
||||||
function mcl_util.get_real_entities()
|
|
||||||
for key, val in pairs(minetest.luaentities) do
|
|
||||||
local def = minetest.registered_entities[minetest.luaentities[key].name]
|
|
||||||
if not mcl_util.registered_blacklisted_entities[def.type] then
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,7 +1,27 @@
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
|
||||||
|
|
||||||
mcl_util = {}
|
mcl_util = {}
|
||||||
|
|
||||||
|
-- Updates all values in t using values from to*.
|
||||||
|
function table.update(t, ...)
|
||||||
|
for _, to in ipairs{...} do
|
||||||
|
for k,v in pairs(to) do
|
||||||
|
t[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Updates nil values in t using values from to*.
|
||||||
|
function table.update_nil(t, ...)
|
||||||
|
for _, to in ipairs{...} do
|
||||||
|
for k,v in pairs(to) do
|
||||||
|
if t[k] == nil then
|
||||||
|
t[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
-- Based on minetest.rotate_and_place
|
-- Based on minetest.rotate_and_place
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
@ -152,7 +172,7 @@ function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_i
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns true if itemstack is a shulker box
|
-- Returns true if itemstack is a shulker box
|
||||||
local is_not_shulker_box = function(itemstack)
|
local function is_not_shulker_box(itemstack)
|
||||||
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
|
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
|
||||||
return g == 0 or g == nil
|
return g == 0 or g == nil
|
||||||
end
|
end
|
||||||
|
@ -214,7 +234,7 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Normalize double container by forcing to always use the left segment first
|
-- Normalize double container by forcing to always use the left segment first
|
||||||
local normalize_double_container = function(pos, node, ctype)
|
local function normalize_double_container(pos, node, ctype)
|
||||||
if ctype == 6 then
|
if ctype == 6 then
|
||||||
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||||
if not pos then
|
if not pos then
|
||||||
|
@ -421,4 +441,131 @@ function mcl_util.get_color(colorstr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dofile(modpath.."/entities.lua")
|
function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
|
||||||
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
if pointed_thing and pointed_thing.type == "node" then
|
||||||
|
local pos = pointed_thing.under
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
if player and not player:get_player_control().sneak then
|
||||||
|
local nodedef = minetest.registered_nodes[node.name]
|
||||||
|
local on_rightclick = nodedef and nodedef.on_rightclick
|
||||||
|
if on_rightclick then
|
||||||
|
return on_rightclick(pos, node, player, itemstack, pointed_thing) or itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.calculate_durability(itemstack)
|
||||||
|
local unbreaking_level = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
|
||||||
|
local armor_uses = minetest.get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
||||||
|
|
||||||
|
local uses
|
||||||
|
|
||||||
|
if armor_uses > 0 then
|
||||||
|
uses = armor_uses
|
||||||
|
if unbreaking_level > 0 then
|
||||||
|
uses = uses / (0.6 + 0.4 / (unbreaking_level + 1))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local def = itemstack:get_definition()
|
||||||
|
if def then
|
||||||
|
local fixed_uses = def._mcl_uses
|
||||||
|
if fixed_uses then
|
||||||
|
uses = fixed_uses
|
||||||
|
if unbreaking_level > 0 then
|
||||||
|
uses = uses * (unbreaking_level + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
uses = uses or (next(itemstack:get_tool_capabilities().groupcaps) or {}).uses
|
||||||
|
end
|
||||||
|
|
||||||
|
return uses or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.use_item_durability(itemstack, n)
|
||||||
|
local uses = mcl_util.calculate_durability(itemstack)
|
||||||
|
itemstack:add_wear(65535 / uses * n)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.deal_damage(target, damage, mcl_reason)
|
||||||
|
local luaentity = target:get_luaentity()
|
||||||
|
|
||||||
|
if luaentity then
|
||||||
|
if luaentity.deal_damage then
|
||||||
|
luaentity:deal_damage(damage, mcl_reason or {type = "generic"})
|
||||||
|
return
|
||||||
|
elseif luaentity._cmi_is_mob then
|
||||||
|
-- local puncher = mcl_reason and mcl_reason.direct or target
|
||||||
|
-- target:punch(puncher, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy = damage}}, vector.direction(puncher:get_pos(), target:get_pos()), damage)
|
||||||
|
if luaentity.health > 0 then
|
||||||
|
luaentity.health = luaentity.health - damage
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local hp = target:get_hp()
|
||||||
|
|
||||||
|
if hp > 0 then
|
||||||
|
target:set_hp(hp - damage, {_mcl_reason = mcl_reason})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.get_hp(obj)
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
|
||||||
|
if luaentity and luaentity._cmi_is_mob then
|
||||||
|
return luaentity.health
|
||||||
|
else
|
||||||
|
return obj:get_hp()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.get_inventory(object, create)
|
||||||
|
if object:is_player() then
|
||||||
|
return object:get_inventory()
|
||||||
|
else
|
||||||
|
local luaentity = object:get_luaentity()
|
||||||
|
local inventory = luaentity.inventory
|
||||||
|
|
||||||
|
if create and not inventory and luaentity.create_inventory then
|
||||||
|
inventory = luaentity:create_inventory()
|
||||||
|
end
|
||||||
|
|
||||||
|
return inventory
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.get_wielded_item(object)
|
||||||
|
if object:is_player() then
|
||||||
|
return object:get_wielded_item()
|
||||||
|
else
|
||||||
|
-- ToDo: implement getting wielditems from mobs as soon as mobs have wielditems
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.get_object_name(object)
|
||||||
|
if object:is_player() then
|
||||||
|
return object:get_player_name()
|
||||||
|
else
|
||||||
|
local luaentity = object:get_luaentity()
|
||||||
|
|
||||||
|
if not luaentity then
|
||||||
|
return tostring(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_util.replace_mob(obj, mob)
|
||||||
|
local rot = obj:get_yaw()
|
||||||
|
local pos = obj:get_pos()
|
||||||
|
obj:remove()
|
||||||
|
obj = minetest.add_entity(pos, mob)
|
||||||
|
obj:set_yaw(rot)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ Params:
|
||||||
|
|
||||||
* pos: position
|
* pos: position
|
||||||
|
|
||||||
## mcl_worlds.y_to_layer(y)
|
## mcl_worlds.y_to_layer(y)
|
||||||
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
||||||
Mainly used for ore generation.
|
Mainly used for ore generation.
|
||||||
Takes an Y coordinate as input and returns:
|
Takes an Y coordinate as input and returns:
|
||||||
|
@ -61,20 +61,21 @@ In mc, you cant use clock in the nether and the end.
|
||||||
|
|
||||||
* pos: position
|
* pos: position
|
||||||
|
|
||||||
## mcl_worlds.register_on_dimension_change(function(player, dimension))
|
## mcl_worlds.register_on_dimension_change(function(player, dimension, last_dimension))
|
||||||
Register a callback function func(player, dimension).
|
Register a callback function func(player, dimension).
|
||||||
It will be called whenever a player changes between dimensions.
|
It will be called whenever a player changes between dimensions.
|
||||||
The void counts as dimension.
|
The void counts as dimension.
|
||||||
|
|
||||||
* player: player, the player who changed the dimension
|
* player: player, the player who changed of dimension
|
||||||
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
|
* dimension: string, The new dimension of the player ("overworld", "nether", "end", "void").
|
||||||
|
* last_dimension: string, The dimension where the player was ("overworld", "nether", "end", "void").
|
||||||
|
|
||||||
|
|
||||||
## mcl_worlds.registered_on_dimension_change
|
## mcl_worlds.registered_on_dimension_change
|
||||||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
||||||
|
|
||||||
## mcl_worlds.dimension_change(player, dimension)
|
## mcl_worlds.dimension_change(player, dimension)
|
||||||
Notify this mod of a dimmension change of <player> to <dimension>
|
Notify this mod of a dimension change of <player> to <dimension>
|
||||||
|
|
||||||
* player: player, player who changed the dimension
|
* player: player, player who changed the dimension
|
||||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
@ -1,5 +1,7 @@
|
||||||
mcl_worlds = {}
|
mcl_worlds = {}
|
||||||
|
|
||||||
|
local get_connected_players = minetest.get_connected_players
|
||||||
|
|
||||||
-- For a given position, returns a 2-tuple:
|
-- For a given position, returns a 2-tuple:
|
||||||
-- 1st return value: true if pos is in void
|
-- 1st return value: true if pos is in void
|
||||||
-- 2nd return value: true if it is in the deadly part of the void
|
-- 2nd return value: true if it is in the deadly part of the void
|
||||||
|
@ -33,60 +35,64 @@ end
|
||||||
-- If the Y coordinate is not located in any dimension, it will return:
|
-- If the Y coordinate is not located in any dimension, it will return:
|
||||||
-- nil, "void"
|
-- nil, "void"
|
||||||
function mcl_worlds.y_to_layer(y)
|
function mcl_worlds.y_to_layer(y)
|
||||||
if y >= mcl_vars.mg_overworld_min then
|
if y >= mcl_vars.mg_overworld_min then
|
||||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
||||||
return y - mcl_vars.mg_nether_min, "nether"
|
return y - mcl_vars.mg_nether_min, "nether"
|
||||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||||
return y - mcl_vars.mg_end_min, "end"
|
return y - mcl_vars.mg_end_min, "end"
|
||||||
else
|
else
|
||||||
return nil, "void"
|
return nil, "void"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local y_to_layer = mcl_worlds.y_to_layer
|
||||||
|
|
||||||
-- Takes a pos and returns the dimension it belongs to (same as above)
|
-- Takes a pos and returns the dimension it belongs to (same as above)
|
||||||
function mcl_worlds.pos_to_dimension(pos)
|
function mcl_worlds.pos_to_dimension(pos)
|
||||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
local _, dim = y_to_layer(pos.y)
|
||||||
return dim
|
return dim
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local pos_to_dimension = mcl_worlds.pos_to_dimension
|
||||||
|
|
||||||
-- Takes a Minecraft layer and a “dimension” name
|
-- Takes a Minecraft layer and a “dimension” name
|
||||||
-- and returns the corresponding Y coordinate for
|
-- and returns the corresponding Y coordinate for
|
||||||
-- MineClone 2.
|
-- MineClone 2.
|
||||||
-- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
|
-- mc_dimension is one of "overworld", "nether", "end" (default: "overworld").
|
||||||
function mcl_worlds.layer_to_y(layer, mc_dimension)
|
function mcl_worlds.layer_to_y(layer, mc_dimension)
|
||||||
if mc_dimension == "overworld" or mc_dimension == nil then
|
if mc_dimension == "overworld" or mc_dimension == nil then
|
||||||
return layer + mcl_vars.mg_overworld_min
|
return layer + mcl_vars.mg_overworld_min
|
||||||
elseif mc_dimension == "nether" then
|
elseif mc_dimension == "nether" then
|
||||||
return layer + mcl_vars.mg_nether_min
|
return layer + mcl_vars.mg_nether_min
|
||||||
elseif mc_dimension == "end" then
|
elseif mc_dimension == "end" then
|
||||||
return layer + mcl_vars.mg_end_min
|
return layer + mcl_vars.mg_end_min
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Takes a position and returns true if this position can have weather
|
-- Takes a position and returns true if this position can have weather
|
||||||
function mcl_worlds.has_weather(pos)
|
function mcl_worlds.has_weather(pos)
|
||||||
-- Weather in the Overworld and the high part of the void below
|
-- Weather in the Overworld and the high part of the void below
|
||||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Takes a position and returns true if this position can have Nether dust
|
-- Takes a position and returns true if this position can have Nether dust
|
||||||
function mcl_worlds.has_dust(pos)
|
function mcl_worlds.has_dust(pos)
|
||||||
-- Weather in the Overworld and the high part of the void below
|
-- Weather in the Overworld and the high part of the void below
|
||||||
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Takes a position (pos) and returns true if compasses are working here
|
-- Takes a position (pos) and returns true if compasses are working here
|
||||||
function mcl_worlds.compass_works(pos)
|
function mcl_worlds.compass_works(pos)
|
||||||
-- It doesn't work in Nether and the End, but it works in the Overworld and in the high part of the void below
|
-- It doesn't work in Nether and the End, but it works in the Overworld and in the high part of the void below
|
||||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||||
if dim == "nether" or dim == "end" then
|
if dim == "nether" or dim == "end" then
|
||||||
return false
|
return false
|
||||||
elseif dim == "void" then
|
elseif dim == "void" then
|
||||||
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
return pos.y <= mcl_vars.mg_overworld_max and pos.y >= mcl_vars.mg_overworld_min - 64
|
||||||
else
|
else
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Takes a position (pos) and returns true if clocks are working here
|
-- Takes a position (pos) and returns true if clocks are working here
|
||||||
|
@ -112,12 +118,15 @@ local last_dimension = {}
|
||||||
-- * player: Player who changed the dimension
|
-- * player: Player who changed the dimension
|
||||||
-- * dimension: New dimension ("overworld", "nether", "end", "void")
|
-- * dimension: New dimension ("overworld", "nether", "end", "void")
|
||||||
function mcl_worlds.dimension_change(player, dimension)
|
function mcl_worlds.dimension_change(player, dimension)
|
||||||
|
local playername = player:get_player_name()
|
||||||
for i=1, #mcl_worlds.registered_on_dimension_change do
|
for i=1, #mcl_worlds.registered_on_dimension_change do
|
||||||
mcl_worlds.registered_on_dimension_change[i](player, dimension)
|
mcl_worlds.registered_on_dimension_change[i](player, dimension, last_dimension[playername])
|
||||||
last_dimension[player:get_player_name()] = dimension
|
|
||||||
end
|
end
|
||||||
|
last_dimension[playername] = dimension
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local dimension_change = mcl_worlds.dimension_change
|
||||||
|
|
||||||
----------------------- INTERNAL STUFF ----------------------
|
----------------------- INTERNAL STUFF ----------------------
|
||||||
|
|
||||||
-- Update the dimension callbacks every DIM_UPDATE seconds
|
-- Update the dimension callbacks every DIM_UPDATE seconds
|
||||||
|
@ -125,19 +134,19 @@ local DIM_UPDATE = 1
|
||||||
local dimtimer = 0
|
local dimtimer = 0
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
last_dimension[player:get_player_name()] = mcl_worlds.pos_to_dimension(player:get_pos())
|
last_dimension[player:get_player_name()] = pos_to_dimension(player:get_pos())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
-- regular updates based on iterval
|
-- regular updates based on iterval
|
||||||
dimtimer = dimtimer + dtime;
|
dimtimer = dimtimer + dtime;
|
||||||
if dimtimer >= DIM_UPDATE then
|
if dimtimer >= DIM_UPDATE then
|
||||||
local players = minetest.get_connected_players()
|
local players = get_connected_players()
|
||||||
for p=1, #players do
|
for p = 1, #players do
|
||||||
local dim = mcl_worlds.pos_to_dimension(players[p]:get_pos())
|
local dim = pos_to_dimension(players[p]:get_pos())
|
||||||
local name = players[p]:get_player_name()
|
local name = players[p]:get_player_name()
|
||||||
if dim ~= last_dimension[name] then
|
if dim ~= last_dimension[name] then
|
||||||
mcl_worlds.dimension_change(players[p], dim)
|
dimension_change(players[p], dim)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dimtimer = 0
|
dimtimer = 0
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# tga_encoder
|
||||||
|
A TGA Encoder written in Lua without the use of external Libraries.
|
||||||
|
|
||||||
|
May be used as a Minetest mod.
|
|
@ -0,0 +1,92 @@
|
||||||
|
tga_encoder = {}
|
||||||
|
|
||||||
|
local image = setmetatable({}, {
|
||||||
|
__call = function(self, ...)
|
||||||
|
local t = setmetatable({}, {__index = self})
|
||||||
|
t:constructor(...)
|
||||||
|
return t
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
function image:constructor(pixels)
|
||||||
|
self.data = ""
|
||||||
|
self.pixels = pixels
|
||||||
|
self.width = #pixels[1]
|
||||||
|
self.height = #pixels
|
||||||
|
|
||||||
|
self:encode()
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode_colormap_spec()
|
||||||
|
self.data = self.data
|
||||||
|
.. string.char(0, 0) -- first entry index
|
||||||
|
.. string.char(0, 0) -- number of entries
|
||||||
|
.. string.char(0) -- bits per pixel
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode_image_spec()
|
||||||
|
self.data = self.data
|
||||||
|
.. string.char(0, 0) -- X-origin
|
||||||
|
.. string.char(0, 0) -- Y-origin
|
||||||
|
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width
|
||||||
|
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height
|
||||||
|
.. string.char(24) -- pixel depth (RGB = 3 bytes = 24 bits)
|
||||||
|
.. string.char(0) -- image descriptor
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode_header()
|
||||||
|
self.data = self.data
|
||||||
|
.. string.char(0) -- image id
|
||||||
|
.. string.char(0) -- color map type
|
||||||
|
.. string.char(10) -- image type (RLE RGB = 10)
|
||||||
|
self:encode_colormap_spec() -- color map specification
|
||||||
|
self:encode_image_spec() -- image specification
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode_data()
|
||||||
|
local current_pixel = ''
|
||||||
|
local previous_pixel = ''
|
||||||
|
local count = 1
|
||||||
|
local packets = {}
|
||||||
|
local rle_packet = ''
|
||||||
|
for _, row in ipairs(self.pixels) do
|
||||||
|
for _, pixel in ipairs(row) do
|
||||||
|
current_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
||||||
|
if current_pixel ~= previous_pixel or count == 128 then
|
||||||
|
packets[#packets +1] = rle_packet
|
||||||
|
count = 1
|
||||||
|
previous_pixel = current_pixel
|
||||||
|
else
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
rle_packet = string.char(128 + count - 1) .. current_pixel
|
||||||
|
end
|
||||||
|
end
|
||||||
|
packets[#packets +1] = rle_packet
|
||||||
|
self.data = self.data .. table.concat(packets)
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode_footer()
|
||||||
|
self.data = self.data
|
||||||
|
.. string.char(0, 0, 0, 0) -- extension area offset
|
||||||
|
.. string.char(0, 0, 0, 0) -- developer area offset
|
||||||
|
.. "TRUEVISION-XFILE"
|
||||||
|
.. "."
|
||||||
|
.. string.char(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:encode()
|
||||||
|
self:encode_header() -- header
|
||||||
|
-- no color map and image id data
|
||||||
|
self:encode_data() -- encode data
|
||||||
|
-- no extension or developer area
|
||||||
|
self:encode_footer() -- footer
|
||||||
|
end
|
||||||
|
|
||||||
|
function image:save(filename)
|
||||||
|
local f = assert(io.open(filename, "w"))
|
||||||
|
f:write(self.data)
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
tga_encoder.image = image
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = tga_encoder
|
||||||
|
author = Fleckenstein
|
||||||
|
description = A TGA Encoder written in Lua without the use of external Libraries.
|
|
@ -4,6 +4,7 @@ local get_connected_players = minetest.get_connected_players
|
||||||
local get_node = minetest.get_node
|
local get_node = minetest.get_node
|
||||||
local vector_add = vector.add
|
local vector_add = vector.add
|
||||||
local ceil = math.ceil
|
local ceil = math.ceil
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
walkover = {}
|
walkover = {}
|
||||||
walkover.registered_globals = {}
|
walkover.registered_globals = {}
|
||||||
|
@ -31,24 +32,21 @@ minetest.register_globalstep(function(dtime)
|
||||||
timer = timer + dtime;
|
timer = timer + dtime;
|
||||||
if timer >= 0.3 then
|
if timer >= 0.3 then
|
||||||
for _,player in pairs(get_connected_players()) do
|
for _,player in pairs(get_connected_players()) do
|
||||||
local pp = player:get_pos()
|
local pp = player:get_pos()
|
||||||
pp.y = ceil(pp.y)
|
pp.y = ceil(pp.y)
|
||||||
local loc = vector_add(pp, {x=0,y=-1,z=0})
|
local loc = vector_add(pp, {x=0,y=-1,z=0})
|
||||||
if loc ~= nil then
|
if loc then
|
||||||
|
local nodeiamon = get_node(loc)
|
||||||
local nodeiamon = get_node(loc)
|
if nodeiamon then
|
||||||
|
if on_walk[nodeiamon.name] then
|
||||||
if nodeiamon ~= nil then
|
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
||||||
if on_walk[nodeiamon.name] then
|
end
|
||||||
on_walk[nodeiamon.name](loc, nodeiamon, player)
|
for i = 1, #registered_globals do
|
||||||
end
|
|
||||||
for i = 1, #registered_globals do
|
|
||||||
registered_globals[i](loc, nodeiamon, player)
|
registered_globals[i](loc, nodeiamon, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
timer = 0
|
timer = 0
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
--Dripping Water Mod
|
|
||||||
--by kddekadenz
|
|
||||||
|
|
||||||
-- License of code, textures & sounds: CC0
|
|
||||||
|
|
||||||
--Drop entities
|
|
||||||
|
|
||||||
--water
|
|
||||||
|
|
||||||
local water_tex = "default_water_source_animated.png^[verticalframe:16:0"
|
|
||||||
minetest.register_entity("drippingwater:drop_water", {
|
|
||||||
hp_max = 1,
|
|
||||||
physical = true,
|
|
||||||
collide_with_objects = false,
|
|
||||||
collisionbox = {-0.025,-0.05,-0.025,0.025,-0.01,0.025},
|
|
||||||
pointable = false,
|
|
||||||
visual = "cube",
|
|
||||||
visual_size = {x=0.05, y=0.1},
|
|
||||||
textures = {water_tex, water_tex, water_tex, water_tex, water_tex, water_tex},
|
|
||||||
spritediv = {x=1, y=1},
|
|
||||||
initial_sprite_basepos = {x=0, y=0},
|
|
||||||
static_save = false,
|
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
|
||||||
self.object:set_sprite({x=0,y=0}, 1, 1, true)
|
|
||||||
end,
|
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
|
||||||
local k = math.random(1,222)
|
|
||||||
local ownpos = self.object:get_pos()
|
|
||||||
|
|
||||||
if k==1 then
|
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
|
||||||
self.object:remove()
|
|
||||||
minetest.sound_play({name="drippingwater_drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
--lava
|
|
||||||
|
|
||||||
local lava_tex = "default_lava_source_animated.png^[verticalframe:16:0"
|
|
||||||
minetest.register_entity("drippingwater:drop_lava", {
|
|
||||||
hp_max = 1,
|
|
||||||
physical = true,
|
|
||||||
collide_with_objects = false,
|
|
||||||
collisionbox = {-0.025,-0.05,-0.025,0.025,-0.01,0.025},
|
|
||||||
glow = math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3),
|
|
||||||
pointable = false,
|
|
||||||
visual = "cube",
|
|
||||||
visual_size = {x=0.05, y=0.1},
|
|
||||||
textures = {lava_tex, lava_tex, lava_tex, lava_tex, lava_tex, lava_tex},
|
|
||||||
spritediv = {x=1, y=1},
|
|
||||||
initial_sprite_basepos = {x=0, y=0},
|
|
||||||
static_save = false,
|
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
|
||||||
self.object:set_sprite({x=0,y=0}, 1, 0, true)
|
|
||||||
end,
|
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
|
||||||
local k = math.random(1,222)
|
|
||||||
local ownpos = self.object:get_pos()
|
|
||||||
|
|
||||||
if k==1 then
|
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
|
||||||
self.object:remove()
|
|
||||||
minetest.sound_play({name="drippingwater_lavadrip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--Create drop
|
|
||||||
|
|
||||||
minetest.register_abm(
|
|
||||||
{
|
|
||||||
label = "Create water drops",
|
|
||||||
nodenames = {"group:opaque", "group:leaves"},
|
|
||||||
neighbors = {"group:water"},
|
|
||||||
interval = 2,
|
|
||||||
chance = 22,
|
|
||||||
action = function(pos)
|
|
||||||
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "water") ~= 0 and
|
|
||||||
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
|
||||||
local i = math.random(-45,45) / 100
|
|
||||||
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_water")
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
--Create lava drop
|
|
||||||
|
|
||||||
minetest.register_abm(
|
|
||||||
{
|
|
||||||
label = "Create lava drops",
|
|
||||||
nodenames = {"group:opaque"},
|
|
||||||
neighbors = {"group:lava"},
|
|
||||||
interval = 2,
|
|
||||||
chance = 22,
|
|
||||||
action = function(pos)
|
|
||||||
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "lava") ~= 0 and
|
|
||||||
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
|
||||||
local i = math.random(-45,45) / 100
|
|
||||||
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_lava")
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -1,4 +1,4 @@
|
||||||
local S = minetest.get_translator("mcl_boats")
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
local boat_visual_size = {x = 1, y = 1, z = 1}
|
local boat_visual_size = {x = 1, y = 1, z = 1}
|
||||||
local paddling_speed = 22
|
local paddling_speed = 22
|
||||||
|
@ -84,7 +84,7 @@ local function attach_object(self, obj)
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
obj:set_look_horizontal(yaw)
|
obj:set_look_horizontal(yaw)
|
||||||
mcl_tmp_message.message(obj, S("Sneak to dismount"))
|
mcl_title.set(obj, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
||||||
else
|
else
|
||||||
obj:get_luaentity()._old_visual_size = visual_size
|
obj:get_luaentity()._old_visual_size = visual_size
|
||||||
end
|
end
|
||||||
|
@ -115,7 +115,7 @@ local boat = {
|
||||||
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mcl_boats_boat.b3d",
|
mesh = "mcl_boats_boat.b3d",
|
||||||
textures = {"mcl_boats_texture_oak_boat.png"},
|
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
|
||||||
visual_size = boat_visual_size,
|
visual_size = boat_visual_size,
|
||||||
hp_max = boat_max_hp,
|
hp_max = boat_max_hp,
|
||||||
damage_texture_modifier = "^[colorize:white:0",
|
damage_texture_modifier = "^[colorize:white:0",
|
||||||
|
@ -148,6 +148,11 @@ function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self._v = data.v
|
self._v = data.v
|
||||||
self._last_v = self._v
|
self._last_v = self._v
|
||||||
self._itemstring = data.itemstring
|
self._itemstring = data.itemstring
|
||||||
|
|
||||||
|
while #data.textures < 5 do
|
||||||
|
table.insert(data.textures, data.textures[1])
|
||||||
|
end
|
||||||
|
|
||||||
self.object:set_properties({textures = data.textures})
|
self.object:set_properties({textures = data.textures})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -188,7 +193,7 @@ function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, d
|
||||||
end
|
end
|
||||||
|
|
||||||
function boat.on_step(self, dtime, moveresult)
|
function boat.on_step(self, dtime, moveresult)
|
||||||
mcl_burning.tick(self.object, dtime)
|
mcl_burning.tick(self.object, dtime, self)
|
||||||
|
|
||||||
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
|
||||||
local v_factor = 1
|
local v_factor = 1
|
||||||
|
@ -328,16 +333,17 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
|
|
||||||
p.y = p.y - boat_y_offset
|
p.y = p.y - boat_y_offset
|
||||||
local new_velo
|
local new_velo
|
||||||
local new_acce = {x = 0, y = 0, z = 0}
|
local new_acce
|
||||||
if not is_water(p) and not on_ice then
|
if not is_water(p) and not on_ice then
|
||||||
-- Not on water or inside water: Free fall
|
-- Not on water or inside water: Free fall
|
||||||
local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
|
--local nodedef = minetest.registered_nodes[minetest.get_node(p).name]
|
||||||
new_acce = {x = 0, y = -9.8, z = 0}
|
new_acce = {x = 0, y = -9.8, z = 0}
|
||||||
new_velo = get_velocity(self._v, self.object:get_yaw(),
|
new_velo = get_velocity(self._v, self.object:get_yaw(),
|
||||||
self.object:get_velocity().y)
|
self.object:get_velocity().y)
|
||||||
else
|
else
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
if is_water(p) then
|
local is_obsidian_boat = self.object:get_luaentity()._itemstring == "mcl_boats:boat_obsidian"
|
||||||
|
if is_water(p) or is_obsidian_boat then
|
||||||
-- Inside water: Slowly sink
|
-- Inside water: Slowly sink
|
||||||
local y = self.object:get_velocity().y
|
local y = self.object:get_velocity().y
|
||||||
y = y - 0.01
|
y = y - 0.01
|
||||||
|
@ -377,13 +383,13 @@ end
|
||||||
-- Register one entity for all boat types
|
-- Register one entity for all boat types
|
||||||
minetest.register_entity("mcl_boats:boat", boat)
|
minetest.register_entity("mcl_boats:boat", boat)
|
||||||
|
|
||||||
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak" }
|
local boat_ids = { "boat", "boat_spruce", "boat_birch", "boat_jungle", "boat_acacia", "boat_dark_oak", "boat_obsidian" }
|
||||||
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat") }
|
local names = { S("Oak Boat"), S("Spruce Boat"), S("Birch Boat"), S("Jungle Boat"), S("Acacia Boat"), S("Dark Oak Boat"), S("Obsidian Boat") }
|
||||||
local craftstuffs = {}
|
local craftstuffs = {}
|
||||||
if minetest.get_modpath("mcl_core") then
|
if minetest.get_modpath("mcl_core") then
|
||||||
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood" }
|
craftstuffs = { "mcl_core:wood", "mcl_core:sprucewood", "mcl_core:birchwood", "mcl_core:junglewood", "mcl_core:acaciawood", "mcl_core:darkwood", "mcl_core:obsidian" }
|
||||||
end
|
end
|
||||||
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak" }
|
local images = { "oak", "spruce", "birch", "jungle", "acacia", "dark_oak", "obsidian" }
|
||||||
|
|
||||||
for b=1, #boat_ids do
|
for b=1, #boat_ids do
|
||||||
local itemstring = "mcl_boats:"..boat_ids[b]
|
local itemstring = "mcl_boats:"..boat_ids[b]
|
||||||
|
@ -394,7 +400,7 @@ for b=1, #boat_ids do
|
||||||
if b == 1 then
|
if b == 1 then
|
||||||
help = true
|
help = true
|
||||||
longdesc = S("Boats are used to travel on the surface of water.")
|
longdesc = S("Boats are used to travel on the surface of water.")
|
||||||
usagehelp = S("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. Rightclick the boat again to leave it, punch the boat to make it drop as an item.")
|
usagehelp = S("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.")
|
||||||
helpname = S("Boat")
|
helpname = S("Boat")
|
||||||
end
|
end
|
||||||
tt_help = S("Water vehicle")
|
tt_help = S("Water vehicle")
|
||||||
|
@ -434,8 +440,9 @@ for b=1, #boat_ids do
|
||||||
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
|
||||||
end
|
end
|
||||||
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
local boat = minetest.add_entity(pos, "mcl_boats:boat")
|
||||||
|
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
|
||||||
boat:get_luaentity()._itemstring = itemstring
|
boat:get_luaentity()._itemstring = itemstring
|
||||||
boat:set_properties({textures = { "mcl_boats_texture_"..images[b].."_boat.png" }})
|
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
|
||||||
boat:set_yaw(placer:get_look_horizontal())
|
boat:set_yaw(placer:get_look_horizontal())
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
itemstack:take_item()
|
itemstack:take_item()
|
||||||
|
@ -470,6 +477,6 @@ minetest.register_craft({
|
||||||
burntime = 20,
|
burntime = 20,
|
||||||
})
|
})
|
||||||
|
|
||||||
if minetest.get_modpath("doc_identifier") ~= nil then
|
if minetest.get_modpath("doc_identifier") then
|
||||||
doc.sub.identifier.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")
|
doc.sub.identifier.register_object("mcl_boats:boat", "craftitems", "mcl_boats:boat")
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ Boats are used to travel on the surface of water.=Boote werden benutzt, um sich
|
||||||
Dark Oak Boat=Schwarzeichenboot
|
Dark Oak Boat=Schwarzeichenboot
|
||||||
Jungle Boat=Dschungelboot
|
Jungle Boat=Dschungelboot
|
||||||
Oak Boat=Eichenboot
|
Oak Boat=Eichenboot
|
||||||
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. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Rechtsklicken Sie auf eine Wasserquelle, um das Boot zu platzieren. Rechtsklicken Sie auf das Boot, um es zu betreten. Mit [Links] und [Rechts] lenken, mit [Vorwärts] und [Rückwärts] Geschwindigkeit regeln oder rückwärts fahren. Rechtsklicken Sie erneut auf das Boot, um es zu verlassen, schlagen Sie das Boot, um es als Gegenstand fallen zu lassen.
|
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.=Rechtsklicken Sie auf eine Wasserquelle, um das Boot zu platzieren. Rechtsklicken Sie auf das Boot, um es zu betreten. Mit [Links] und [Rechts] lenken, mit [Vorwärts] und [Rückwärts] Geschwindigkeit regeln oder rückwärts fahren. Nutzen sie [Schleichen], um das Boot zu verlassen, schlagen Sie das Boot, um es als Gegenstand fallen zu lassen.
|
||||||
Spruce Boat=Fichtenboot
|
Spruce Boat=Fichtenboot
|
||||||
Water vehicle=Wasserfahrzeug
|
Water vehicle=Wasserfahrzeug
|
||||||
|
Sneak to dismount=Zum Aussteigen schleichen
|
||||||
|
|
|
@ -6,6 +6,7 @@ Boats are used to travel on the surface of water.=Les bateaux sont utilisés pou
|
||||||
Dark Oak Boat=Bateau en Chêne Noir
|
Dark Oak Boat=Bateau en Chêne Noir
|
||||||
Jungle Boat=Bateau en Acajou
|
Jungle Boat=Bateau en Acajou
|
||||||
Oak Boat=Bateau en Chêne
|
Oak Boat=Bateau en Chêne
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Cliquez de nouveau avec le bouton droit sur le bateau pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||||
Spruce Boat=Bateau en Sapin
|
Spruce Boat=Bateau en Sapin
|
||||||
Water vehicle=Véhicule aquatique
|
Water vehicle=Véhicule aquatique
|
||||||
|
Sneak to dismount=
|
|
@ -0,0 +1,12 @@
|
||||||
|
# textdomain: mcl_boats
|
||||||
|
Acacia Boat=Akacjowa łódź
|
||||||
|
Birch Boat=Brzozowa łódź
|
||||||
|
Boat=Łódź
|
||||||
|
Boats are used to travel on the surface of water.=Łodzie są wykorzystywane do podróżowania po powierzchni wody.
|
||||||
|
Dark Oak Boat=Ciemno-dębowa łódź
|
||||||
|
Jungle Boat=Tropikalna łódź
|
||||||
|
Oak Boat=Dębowa łódź
|
||||||
|
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.=Kliknij prawym przyciskiem myszy na źródło wody by postawić łódź. Kliknij prawym przyciskiem myszy by w nią wsiąść. Użyj przycisków [Lewy] oraz [Prawy] by sterować, [Naprzód] by przyspieszyć i [W tył] by zwolnić lub się cofać. Kliknij [Skradanie] by z niej wyjść, uderz ją by wziąć ją jako przedmiot.
|
||||||
|
Spruce Boat=Świerkowa łódź
|
||||||
|
Water vehicle=Pojazd wodny
|
||||||
|
Sneak to dismount=Skradaj się by opuścić łódź
|
|
@ -6,6 +6,7 @@ Boats are used to travel on the surface of water.=
|
||||||
Dark Oak Boat=
|
Dark Oak Boat=
|
||||||
Jungle Boat=
|
Jungle Boat=
|
||||||
Oak 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. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=
|
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=
|
Spruce Boat=
|
||||||
Water vehicle=
|
Water vehicle=
|
||||||
|
Sneak to dismount=
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib
|
depends = mcl_player, flowlib, mcl_title
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 264 B |
After Width: | Height: | Size: 535 B |
|
@ -1,184 +1,81 @@
|
||||||
local S = minetest.get_translator("mcl_burning")
|
function mcl_burning.get_storage(obj)
|
||||||
|
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||||
function mcl_burning.get_default(datatype)
|
|
||||||
local default_table = {string = "", float = 0.0, int = 0, bool = false}
|
|
||||||
return default_table[datatype]
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.get(obj, datatype, name)
|
|
||||||
local key
|
|
||||||
if obj:is_player() then
|
|
||||||
local meta = obj:get_meta()
|
|
||||||
return meta["get_" .. datatype](meta, "mcl_burning:" .. name)
|
|
||||||
else
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
return luaentity and luaentity["mcl_burning_" .. name] or mcl_burning.get_default(datatype)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.set(obj, datatype, name, value)
|
|
||||||
if obj:is_player() then
|
|
||||||
local meta = obj:get_meta()
|
|
||||||
meta["set_" .. datatype](meta, "mcl_burning:" .. name, value or mcl_burning.get_default(datatype))
|
|
||||||
else
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if mcl_burning.get_default(datatype) == value then
|
|
||||||
value = nil
|
|
||||||
end
|
|
||||||
luaentity["mcl_burning_" .. name] = value
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.is_burning(obj)
|
function mcl_burning.is_burning(obj)
|
||||||
return mcl_burning.get(obj, "float", "burn_time") > 0
|
return mcl_burning.get_storage(obj).burn_time
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.is_affected_by_rain(obj)
|
function mcl_burning.is_affected_by_rain(obj)
|
||||||
return mcl_weather and mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
return mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.get_collisionbox(obj, smaller)
|
function mcl_burning.get_collisionbox(obj, smaller, storage)
|
||||||
local box = obj:get_properties().collisionbox
|
local cache = storage.collisionbox_cache
|
||||||
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
if cache then
|
||||||
if smaller then
|
local box = cache[smaller and 2 or 1]
|
||||||
|
return box[1], box[2]
|
||||||
|
else
|
||||||
|
local box = obj:get_properties().collisionbox
|
||||||
|
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
||||||
local s_vec = vector.new(0.1, 0.1, 0.1)
|
local s_vec = vector.new(0.1, 0.1, 0.1)
|
||||||
minp = vector.add(minp, s_vec)
|
local s_minp = vector.add(minp, s_vec)
|
||||||
maxp = vector.subtract(maxp, s_vec)
|
local s_maxp = vector.subtract(maxp, s_vec)
|
||||||
|
storage.collisionbox_cache = {{minp, maxp}, {s_minp, s_maxp}}
|
||||||
|
return minp, maxp
|
||||||
end
|
end
|
||||||
return minp, maxp
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.get_touching_nodes(obj, nodenames)
|
function mcl_burning.get_touching_nodes(obj, nodenames, storage)
|
||||||
local pos = obj:get_pos()
|
local pos = obj:get_pos()
|
||||||
local box = obj:get_properties().collisionbox
|
local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage)
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(obj, true)
|
|
||||||
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
||||||
return nodes
|
return nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.get_highest_group_value(obj, groupname)
|
function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
local nodes = mcl_burning.get_touching_nodes(obj, "group:" .. groupname, true)
|
|
||||||
local highest_group_value = 0
|
|
||||||
|
|
||||||
for _, pos in pairs(nodes) do
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local group_value = minetest.get_item_group(node.name, groupname)
|
|
||||||
if group_value > highest_group_value then
|
|
||||||
highest_group_value = group_value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return highest_group_value
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.damage(obj)
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
local health
|
|
||||||
|
|
||||||
if luaentity then
|
|
||||||
health = luaentity.health
|
|
||||||
end
|
|
||||||
|
|
||||||
local hp = health or obj:get_hp()
|
|
||||||
|
|
||||||
if hp <= 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local do_damage = true
|
|
||||||
|
|
||||||
if obj:is_player() then
|
|
||||||
if mcl_potions.player_has_effect(obj, "fire_proof") then
|
|
||||||
do_damage = false
|
|
||||||
else
|
|
||||||
local name = obj:get_player_name()
|
|
||||||
armor.last_damage_types[name] = "fire"
|
|
||||||
local deathmsg = S("@1 burned to death.", name)
|
|
||||||
local reason = mcl_burning.get(obj, "string", "reason")
|
|
||||||
if reason ~= "" then
|
|
||||||
deathmsg = S("@1 was burned by @2.", name, reason)
|
|
||||||
end
|
|
||||||
mcl_death_messages.player_damage(obj, deathmsg)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if luaentity.fire_damage_resistant then
|
|
||||||
do_damage = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if do_damage then
|
|
||||||
local new_hp = hp - 1
|
|
||||||
if health then
|
|
||||||
luaentity.health = new_hp
|
|
||||||
else
|
|
||||||
obj:set_hp(new_hp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|
||||||
if obj:get_hp() < 0 then
|
if obj:get_hp() < 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local storage = mcl_burning.get_storage(obj)
|
||||||
|
|
||||||
local luaentity = obj:get_luaentity()
|
local luaentity = obj:get_luaentity()
|
||||||
if luaentity and luaentity.fire_resistant then
|
if luaentity and luaentity.fire_resistant then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local old_burn_time = mcl_burning.get(obj, "float", "burn_time")
|
if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
local max_fire_prot_lvl = 0
|
burn_time = 0
|
||||||
|
else
|
||||||
|
local max_fire_prot_lvl = 0
|
||||||
|
local inv = mcl_util.get_inventory(obj)
|
||||||
|
local armor_list = inv and inv:get_list("armor")
|
||||||
|
|
||||||
if obj:is_player() then
|
if armor_list then
|
||||||
if minetest.is_creative_enabled(obj:get_player_name()) then
|
for _, stack in pairs(armor_list) do
|
||||||
burn_time = burn_time / 100
|
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
|
||||||
end
|
if fire_prot_lvl > max_fire_prot_lvl then
|
||||||
|
max_fire_prot_lvl = fire_prot_lvl
|
||||||
local inv = obj:get_inventory()
|
end
|
||||||
|
|
||||||
for i = 2, 5 do
|
|
||||||
local stack = inv:get_stack("armor", i)
|
|
||||||
|
|
||||||
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
|
|
||||||
max_fire_prot_lvl = math.max(max_fire_prot_lvl, fire_prot_lvl)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if max_fire_prot_lvl > 0 then
|
|
||||||
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_burn_time <= burn_time then
|
|
||||||
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
|
||||||
if sound_id == 0 then
|
|
||||||
sound_id = minetest.sound_play("fire_fire", {
|
|
||||||
object = obj,
|
|
||||||
gain = 0.18,
|
|
||||||
max_hear_distance = 16,
|
|
||||||
loop = true,
|
|
||||||
}) + 1
|
|
||||||
end]]--
|
|
||||||
|
|
||||||
local hud_id
|
|
||||||
if obj:is_player() then
|
|
||||||
hud_id = mcl_burning.get(obj, "int", "hud_id")
|
|
||||||
if hud_id == 0 then
|
|
||||||
hud_id = obj:hud_add({
|
|
||||||
hud_elem_type = "image",
|
|
||||||
position = {x = 0.5, y = 0.5},
|
|
||||||
scale = {x = -100, y = -100},
|
|
||||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
|
||||||
z_index = 1000,
|
|
||||||
}) + 1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
|
||||||
mcl_burning.set(obj, "string", "reason", reason)
|
if max_fire_prot_lvl > 0 then
|
||||||
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
||||||
--mcl_burning.set(obj, "int", "sound_id", sound_id)
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not storage.burn_time or burn_time >= storage.burn_time then
|
||||||
|
if obj:is_player() then
|
||||||
|
mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames))
|
||||||
|
mcl_burning.channels[obj]:send_all("start")
|
||||||
|
end
|
||||||
|
storage.burn_time = burn_time
|
||||||
|
storage.fire_damage_timer = 0
|
||||||
|
|
||||||
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(obj)
|
local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage)
|
||||||
local obj_size = obj:get_properties().visual_size
|
local obj_size = obj:get_properties().visual_size
|
||||||
|
|
||||||
local vertical_grow_factor = 1.2
|
local vertical_grow_factor = 1.2
|
||||||
|
@ -192,111 +89,50 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||||
|
|
||||||
fire_entity:set_properties({visual_size = size})
|
fire_entity:set_properties({visual_size = size})
|
||||||
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
||||||
mcl_burning.update_animation_frame(obj, fire_entity, 0)
|
local fire_luaentity = fire_entity:get_luaentity()
|
||||||
|
|
||||||
|
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
||||||
|
local other_luaentity = other:get_luaentity()
|
||||||
|
if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then
|
||||||
|
other:remove()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.extinguish(obj)
|
function mcl_burning.extinguish(obj)
|
||||||
if mcl_burning.is_burning(obj) then
|
if mcl_burning.is_burning(obj) then
|
||||||
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
local storage = mcl_burning.get_storage(obj)
|
||||||
--minetest.sound_stop(sound_id)
|
|
||||||
|
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
mcl_burning.channels[obj]:send_all("stop")
|
||||||
obj:hud_remove(hud_id)
|
mcl_burning.storage[obj] = {}
|
||||||
end
|
else
|
||||||
|
storage.burn_time = nil
|
||||||
mcl_burning.set(obj, "string", "reason")
|
storage.fire_damage_timer = nil
|
||||||
mcl_burning.set(obj, "float", "burn_time")
|
|
||||||
mcl_burning.set(obj, "float", "damage_timer")
|
|
||||||
mcl_burning.set(obj, "int", "hud_id")
|
|
||||||
--mcl_burning.set(obj, "int", "sound_id")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.catch_fire_tick(obj, dtime)
|
|
||||||
if mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire") > 0 then
|
|
||||||
mcl_burning.extinguish(obj)
|
|
||||||
else
|
|
||||||
local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire")
|
|
||||||
|
|
||||||
if set_on_fire_value > 0 then
|
|
||||||
mcl_burning.set_on_fire(obj, set_on_fire_value)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.tick(obj, dtime)
|
function mcl_burning.tick(obj, dtime, storage)
|
||||||
local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime
|
if storage.burn_time then
|
||||||
|
storage.burn_time = storage.burn_time - dtime
|
||||||
|
|
||||||
if burn_time <= 0 then
|
if storage.burn_time <= 0 or mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire", storage) > 0 then
|
||||||
mcl_burning.extinguish(obj)
|
mcl_burning.extinguish(obj)
|
||||||
else
|
return true
|
||||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
else
|
||||||
|
storage.fire_damage_timer = storage.fire_damage_timer + dtime
|
||||||
|
|
||||||
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
|
if storage.fire_damage_timer >= 1 then
|
||||||
|
storage.fire_damage_timer = 0
|
||||||
|
|
||||||
if damage_timer >= 1 then
|
local luaentity = obj:get_luaentity()
|
||||||
damage_timer = 0
|
|
||||||
mcl_burning.damage(obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_burning.set(obj, "float", "damage_timer", damage_timer)
|
if not luaentity or not luaentity.fire_damage_resistant then
|
||||||
end
|
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
|
||||||
|
end
|
||||||
mcl_burning.catch_fire_tick(obj, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.update_animation_frame(obj, fire_entity, animation_frame)
|
|
||||||
local fire_texture = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
|
||||||
local fire_HUD_texture = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
|
|
||||||
fire_entity:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
|
|
||||||
if obj:is_player() then
|
|
||||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
|
||||||
obj:hud_change(hud_id, "text", fire_HUD_texture)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_burning.fire_entity_step(self, dtime)
|
|
||||||
if self.removed then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj = self.object
|
|
||||||
local parent = obj:get_attach()
|
|
||||||
local do_remove
|
|
||||||
|
|
||||||
self.doing_step = true
|
|
||||||
|
|
||||||
if not parent or not mcl_burning.is_burning(parent) then
|
|
||||||
do_remove = true
|
|
||||||
else
|
|
||||||
for _, other in pairs(minetest.get_objects_inside_radius(obj:get_pos(), 0)) do
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if luaentity and luaentity.name == "mcl_burning:fire" and not luaentity.doing_step and not luaentity.removed then
|
|
||||||
do_remove = true
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.doing_step = false
|
|
||||||
|
|
||||||
if do_remove then
|
|
||||||
self.removed = true
|
|
||||||
obj:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local animation_timer = self.animation_timer + dtime
|
|
||||||
if animation_timer >= 0.015 then
|
|
||||||
animation_timer = 0
|
|
||||||
local animation_frame = self.animation_frame + 1
|
|
||||||
if animation_frame > mcl_burning.animation_frames - 1 then
|
|
||||||
animation_frame = 0
|
|
||||||
end
|
|
||||||
mcl_burning.update_animation_frame(parent, obj, animation_frame)
|
|
||||||
self.animation_frame = animation_frame
|
|
||||||
end
|
|
||||||
self.animation_timer = animation_timer
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +1,42 @@
|
||||||
local S = minetest.get_translator("mcl_burning")
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
local modpath = minetest.get_modpath("mcl_burning")
|
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
|
local get_connected_players = minetest.get_connected_players
|
||||||
|
local get_item_group = minetest.get_item_group
|
||||||
|
|
||||||
mcl_burning = {
|
mcl_burning = {
|
||||||
|
storage = {},
|
||||||
|
channels = {},
|
||||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||||
}
|
}
|
||||||
|
|
||||||
dofile(modpath .. "/api.lua")
|
dofile(modpath .. "/api.lua")
|
||||||
|
|
||||||
minetest.register_entity("mcl_burning:fire", {
|
|
||||||
initial_properties = {
|
|
||||||
physical = false,
|
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
|
||||||
visual = "cube",
|
|
||||||
pointable = false,
|
|
||||||
glow = -1,
|
|
||||||
},
|
|
||||||
|
|
||||||
animation_frame = 0,
|
|
||||||
animation_timer = 0,
|
|
||||||
on_step = mcl_burning.fire_entity_step,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(function(dtime)
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
for _, player in pairs(get_connected_players()) do
|
||||||
mcl_burning.tick(player, dtime)
|
local storage = mcl_burning.storage[player]
|
||||||
|
if not mcl_burning.tick(player, dtime, storage) and not mcl_burning.is_affected_by_rain(player) then
|
||||||
|
local nodes = mcl_burning.get_touching_nodes(player, {"group:puts_out_fire", "group:set_on_fire"}, storage)
|
||||||
|
local burn_time = 0
|
||||||
|
|
||||||
|
for _, pos in pairs(nodes) do
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
if get_item_group(node.name, "puts_out_fire") > 0 then
|
||||||
|
burn_time = 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local value = get_item_group(node.name, "set_on_fire")
|
||||||
|
if value > burn_time then
|
||||||
|
burn_time = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if burn_time > 0 then
|
||||||
|
mcl_burning.set_on_fire(player, burn_time)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -31,6 +44,68 @@ minetest.register_on_respawnplayer(function(player)
|
||||||
mcl_burning.extinguish(player)
|
mcl_burning.extinguish(player)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
function mcl_burning.init_player(player)
|
||||||
mcl_burning.set(player, "int", "hud_id")
|
local meta = player:get_meta()
|
||||||
|
-- NOTE: mcl_burning:data may be "return nil" (which deserialize into nil) for reasons unknown.
|
||||||
|
if meta:get_string("mcl_burning:data"):find("return nil", 1, true) then
|
||||||
|
minetest.log("warning", "[mcl_burning] 'mcl_burning:data' player meta field is invalid! Please report this bug")
|
||||||
|
end
|
||||||
|
mcl_burning.storage[player] = meta:contains("mcl_burning:data") and minetest.deserialize(meta:get_string("mcl_burning:data")) or {}
|
||||||
|
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
mcl_burning.init_player(player)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player]))
|
||||||
|
mcl_burning.storage[player] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_burning:fire", {
|
||||||
|
initial_properties = {
|
||||||
|
physical = false,
|
||||||
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||||
|
visual = "upright_sprite",
|
||||||
|
textures = {
|
||||||
|
name = "mcl_burning_entity_flame_animated.png",
|
||||||
|
animation = {
|
||||||
|
type = "vertical_frames",
|
||||||
|
aspect_w = 16,
|
||||||
|
aspect_h = 16,
|
||||||
|
length = 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spritediv = {x = 1, y = mcl_burning.animation_frames},
|
||||||
|
pointable = false,
|
||||||
|
glow = -1,
|
||||||
|
backface_culling = false,
|
||||||
|
},
|
||||||
|
animation_frame = 0,
|
||||||
|
animation_timer = 0,
|
||||||
|
on_activate = function(self)
|
||||||
|
self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames)
|
||||||
|
end,
|
||||||
|
on_step = function(self)
|
||||||
|
if not self:sanity_check() then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
sanity_check = function(self)
|
||||||
|
local parent = self.object:get_attach()
|
||||||
|
|
||||||
|
if not parent then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local storage = mcl_burning.get_storage(parent)
|
||||||
|
|
||||||
|
if not storage or not storage.burn_time then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
-- Dripping Water Mod
|
||||||
|
-- by kddekadenz
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
|
||||||
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
|
local function register_drop(liquid, glow, sound, nodes)
|
||||||
|
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
|
||||||
|
hp_max = 1,
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = false,
|
||||||
|
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
|
||||||
|
glow = glow,
|
||||||
|
pointable = false,
|
||||||
|
visual = "sprite",
|
||||||
|
visual_size = {x = 0.1, y = 0.1},
|
||||||
|
textures = {""},
|
||||||
|
spritediv = {x = 1, y = 1},
|
||||||
|
initial_sprite_basepos = {x = 0, y = 0},
|
||||||
|
static_save = false,
|
||||||
|
_dropped = false,
|
||||||
|
on_activate = function(self)
|
||||||
|
self.object:set_properties({
|
||||||
|
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
local k = math.random(1, 222)
|
||||||
|
local ownpos = self.object:get_pos()
|
||||||
|
if k == 1 then
|
||||||
|
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||||
|
end
|
||||||
|
if minetest.get_node(vector.offset(ownpos, 0, 0.5, 0)).name == "air" then
|
||||||
|
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||||
|
end
|
||||||
|
if minetest.get_node(vector.offset(ownpos, 0, -0.1, 0)).name ~= "air" then
|
||||||
|
local ent = self.object:get_luaentity()
|
||||||
|
if not ent._dropped then
|
||||||
|
ent._dropped = true
|
||||||
|
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
|
end
|
||||||
|
if k < 3 then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
minetest.register_abm({
|
||||||
|
label = "Create drops",
|
||||||
|
nodenames = nodes,
|
||||||
|
neighbors = {"group:" .. liquid},
|
||||||
|
interval = 2,
|
||||||
|
chance = 22,
|
||||||
|
action = function(pos)
|
||||||
|
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
||||||
|
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
||||||
|
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||||
|
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
||||||
|
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
|
@ -1,4 +1,4 @@
|
||||||
name = drippingwater
|
name = mcl_dripping
|
||||||
author = kddekadenz
|
author = kddekadenz
|
||||||
description = Drops are generated rarely under solid nodes
|
description = Drops are generated rarely under solid nodes
|
||||||
depends = mcl_core
|
depends = mcl_core
|
|
@ -1,12 +1,12 @@
|
||||||
Dripping Water Mod
|
Dripping Mod
|
||||||
by kddekadenz
|
by kddekadenz
|
||||||
|
|
||||||
modified for MineClone 2 by Wuzzy
|
modified for MineClone 2 by Wuzzy and NO11
|
||||||
|
|
||||||
|
|
||||||
Installing instructions:
|
Installing instructions:
|
||||||
|
|
||||||
1. Copy the drippingwater mod folder into games/gamemode/mods
|
1. Copy the mcl_dripping mod folder into games/gamemode/mods
|
||||||
|
|
||||||
2. Start game and enjoy :)
|
2. Start game and enjoy :)
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
local S = minetest.get_translator("mcl_falling_nodes")
|
local function get_falling_depth(self)
|
||||||
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
|
|
||||||
local has_mcl_armor = minetest.get_modpath("mcl_armor")
|
|
||||||
|
|
||||||
local is_creative_enabled = minetest.is_creative_enabled
|
|
||||||
|
|
||||||
local get_falling_depth = function(self)
|
|
||||||
if not self._startpos then
|
if not self._startpos then
|
||||||
-- Fallback
|
-- Fallback
|
||||||
self._startpos = self.object:get_pos()
|
self._startpos = self.object:get_pos()
|
||||||
|
@ -12,7 +6,7 @@ local get_falling_depth = function(self)
|
||||||
return self._startpos.y - vector.round(self.object:get_pos()).y
|
return self._startpos.y - vector.round(self.object:get_pos()).y
|
||||||
end
|
end
|
||||||
|
|
||||||
local deal_falling_damage = function(self, dtime)
|
local function deal_falling_damage(self, dtime)
|
||||||
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -23,80 +17,34 @@ local deal_falling_damage = function(self, dtime)
|
||||||
-- Fallback
|
-- Fallback
|
||||||
self._startpos = pos
|
self._startpos = pos
|
||||||
end
|
end
|
||||||
local objs = minetest.get_objects_inside_radius(pos, 1)
|
self._hit = self._hit or {}
|
||||||
for _,v in ipairs(objs) do
|
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||||
if v:is_player() then
|
local entity = obj:get_luaentity()
|
||||||
local hp = v:get_hp()
|
if entity and entity.name == "__builtin:item" then
|
||||||
local name = v:get_player_name()
|
obj:remove()
|
||||||
if hp ~= 0 then
|
elseif mcl_util.get_hp(obj) > 0 and not self._hit[obj] then
|
||||||
if not self._hit_players then
|
self._hit[obj] = true
|
||||||
self._hit_players = {}
|
local way = self._startpos.y - pos.y
|
||||||
end
|
local damage = (way - 1) * 2
|
||||||
local hit = false
|
damage = math.min(40, math.max(0, damage))
|
||||||
for _,v in ipairs(self._hit_players) do
|
if damage >= 1 then
|
||||||
if name == v then
|
-- Reduce damage if wearing a helmet
|
||||||
hit = true
|
local inv = mcl_util.get_inventory(obj)
|
||||||
|
if inv then
|
||||||
|
local helmet = inv:get_stack("armor", 2)
|
||||||
|
if minetest.get_item_group(helmet:get_name(), "combat_armor") > 0 then
|
||||||
|
damage = damage / 4 * 3
|
||||||
|
mcl_util.use_item_durability(helmet, 1)
|
||||||
|
inv:set_stack("armor", 2, helmet)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not hit then
|
local dmg_type
|
||||||
table.insert(self._hit_players, name)
|
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||||
local way = self._startpos.y - pos.y
|
dmg_type = "anvil"
|
||||||
local damage = (way - 1) * 2
|
else
|
||||||
damage = math.min(40, math.max(0, damage))
|
dmg_type = "falling_node"
|
||||||
if damage >= 1 then
|
|
||||||
hp = hp - damage
|
|
||||||
if hp < 0 then
|
|
||||||
hp = 0
|
|
||||||
end
|
|
||||||
-- Reduce damage if wearing a helmet
|
|
||||||
local inv = v:get_inventory()
|
|
||||||
local helmet = inv:get_stack("armor", 2)
|
|
||||||
if has_mcl_armor and not helmet:is_empty() then
|
|
||||||
hp = hp/4*3
|
|
||||||
if not is_creative_enabled(name) then
|
|
||||||
helmet:add_wear(65535/helmet:get_definition().groups.mcl_armor_uses) --TODO: be sure damage is exactly like mc (informations are missing in the mc wiki)
|
|
||||||
inv:set_stack("armor", 2, helmet)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local msg
|
|
||||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
|
||||||
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
|
||||||
else
|
|
||||||
msg = S("@1 was smashed by a falling block.", v:get_player_name())
|
|
||||||
end
|
|
||||||
if dmes then
|
|
||||||
mcl_death_messages.player_damage(v, msg)
|
|
||||||
end
|
|
||||||
v:set_hp(hp, { type = "punch", from = "mod" })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local hp = v:get_luaentity().health
|
|
||||||
if hp and hp ~= 0 then
|
|
||||||
if not self._hit_mobs then
|
|
||||||
self._hit_mobs = {}
|
|
||||||
end
|
|
||||||
local hit = false
|
|
||||||
for _,mob in ipairs(self._hit_mobs) do
|
|
||||||
if v == mob then
|
|
||||||
hit = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--TODO: reduce damage for mobs then they will be able to wear armor
|
|
||||||
if not hit then
|
|
||||||
table.insert(self._hit_mobs, v)
|
|
||||||
local way = self._startpos.y - pos.y
|
|
||||||
local damage = (way - 1) * 2
|
|
||||||
damage = math.min(40, math.max(0, damage))
|
|
||||||
if damage >= 1 then
|
|
||||||
hp = hp - damage
|
|
||||||
if hp < 0 then
|
|
||||||
hp = 0
|
|
||||||
end
|
|
||||||
v:get_luaentity().health = hp
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
mcl_util.deal_damage(obj, damage, {type = dmg_type})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -112,10 +60,8 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||||
},
|
},
|
||||||
|
|
||||||
node = {},
|
node = {},
|
||||||
meta = {},
|
meta = {},
|
||||||
|
|
||||||
set_node = function(self, node, meta)
|
set_node = function(self, node, meta)
|
||||||
local def = minetest.registered_nodes[node.name]
|
local def = minetest.registered_nodes[node.name]
|
||||||
-- Change falling node if definition tells us to
|
-- Change falling node if definition tells us to
|
||||||
|
@ -142,7 +88,6 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
glow = glow,
|
glow = glow,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
local meta = self.meta
|
local meta = self.meta
|
||||||
-- Workaround: Save inventory seperately from metadata.
|
-- Workaround: Save inventory seperately from metadata.
|
||||||
|
@ -163,7 +108,6 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
}
|
}
|
||||||
return minetest.serialize(ds)
|
return minetest.serialize(ds)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
on_activate = function(self, staticdata)
|
||||||
self.object:set_armor_groups({immortal = 1})
|
self.object:set_armor_groups({immortal = 1})
|
||||||
|
|
||||||
|
@ -186,7 +130,6 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
end
|
end
|
||||||
self._startpos = vector.round(self._startpos)
|
self._startpos = vector.round(self._startpos)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
-- Set gravity
|
-- Set gravity
|
||||||
local acceleration = self.object:get_acceleration()
|
local acceleration = self.object:get_acceleration()
|
||||||
|
@ -238,10 +181,9 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local nd = minetest.registered_nodes[n2.name]
|
local nd = minetest.registered_nodes[n2.name]
|
||||||
if n2.name == "mcl_portals:portal_end" then
|
--if n2.name == "mcl_portals:portal_end" then
|
||||||
-- TODO: Teleport falling node.
|
-- TODO: Teleport falling node.
|
||||||
|
if (nd and nd.buildable_to == true) or minetest.get_item_group(self.node.name, "crush_after_fall") ~= 0 then
|
||||||
elseif (nd and nd.buildable_to == true) or minetest.get_item_group(self.node.name, "crush_after_fall") ~= 0 then
|
|
||||||
-- Replace destination node if it's buildable to
|
-- Replace destination node if it's buildable to
|
||||||
minetest.remove_node(np)
|
minetest.remove_node(np)
|
||||||
-- Run script hook
|
-- Run script hook
|
||||||
|
@ -308,7 +250,6 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
self.object:set_pos(npos)
|
self.object:set_pos(npos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
deal_falling_damage(self, dtime)
|
deal_falling_damage(self, dtime)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 wurde von einem fallenden Amboss zerschmettert.
|
|
||||||
@1 was smashed by a falling block.=@1 wurde von einem fallenden Block zerschmettert.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 fue aplastado por la caída de un yunque.
|
|
||||||
@1 was smashed by a falling block.=@1 fue aplastado por la caída de un bloque.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 a été écrasé par une enclume qui tombait.
|
|
||||||
@1 was smashed by a falling block.=@1 a été écrasé par un bloc qui tombait.
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_falling_nodes
|
||||||
|
@1 was smashed by a falling anvil.=@1 została zmiażdżona przez spadające kowadło.
|
||||||
|
@1 was smashed by a falling block.=@1 została zmiażdżona przez spadający blok.
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 придавило падающей наковальней.
|
|
||||||
@1 was smashed by a falling block.=@1 раздавило падающим блоком.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=
|
|
||||||
@1 was smashed by a falling block.=
|
|
|
@ -1,5 +1,5 @@
|
||||||
--these are lua locals, used for higher performance
|
--these are lua locals, used for higher performance
|
||||||
local minetest,math,vector,ipairs = minetest,math,vector,ipairs
|
local minetest, math, vector, ipairs, pairs = minetest, math, vector, ipairs, pairs
|
||||||
|
|
||||||
--this is used for the player pool in the sound buffer
|
--this is used for the player pool in the sound buffer
|
||||||
local pool = {}
|
local pool = {}
|
||||||
|
@ -38,7 +38,7 @@ item_drop_settings.drop_single_item = false --if true, the drop control dro
|
||||||
|
|
||||||
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 get_gravity = function()
|
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
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blaze
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||||
|
|
||||||
local check_pickup_achievements = function(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()
|
||||||
|
@ -72,7 +72,7 @@ local check_pickup_achievements = function(object, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local enable_physics = function(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({
|
||||||
|
@ -83,7 +83,7 @@ local enable_physics = function(object, luaentity, ignore_check)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local disable_physics = function(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
|
||||||
object:set_properties({
|
object:set_properties({
|
||||||
|
@ -98,13 +98,11 @@ end
|
||||||
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
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()
|
||||||
|
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
|
@ -235,7 +233,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
local dug_node = minetest.get_node(pos)
|
local dug_node = minetest.get_node(pos)
|
||||||
local tooldef
|
local tooldef
|
||||||
local tool
|
local tool
|
||||||
if digger ~= nil then
|
if digger then
|
||||||
tool = digger:get_wielded_item()
|
tool = digger:get_wielded_item()
|
||||||
tooldef = minetest.registered_tools[tool:get_name()]
|
tooldef = minetest.registered_tools[tool:get_name()]
|
||||||
|
|
||||||
|
@ -292,10 +290,10 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if digger and mcl_experience.throw_experience 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_experience(pos, experience_amount)
|
mcl_experience.throw_xp(pos, experience_amount)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -316,7 +314,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
-- Spawn item and apply random speed
|
-- Spawn item and apply random speed
|
||||||
local obj = minetest.add_item(dpos, drop_item)
|
local obj = minetest.add_item(dpos, drop_item)
|
||||||
if obj ~= nil then
|
if obj then
|
||||||
local x = math.random(1, 5)
|
local x = math.random(1, 5)
|
||||||
if math.random(1,2) == 1 then
|
if math.random(1,2) == 1 then
|
||||||
x = -x
|
x = -x
|
||||||
|
@ -365,6 +363,17 @@ if not time_to_live then
|
||||||
time_to_live = 300
|
time_to_live = 300
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function cxcz(o, cw, one, zero)
|
||||||
|
if cw < 0 then
|
||||||
|
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
||||||
|
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
||||||
|
else
|
||||||
|
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
||||||
|
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
||||||
|
end
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
minetest.register_entity(":__builtin:item", {
|
minetest.register_entity(":__builtin:item", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
|
@ -385,7 +394,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- The itemstring MUST be set immediately to a non-empty string after creating the entity.
|
-- The itemstring MUST be set immediately to a non-empty string after creating the entity.
|
||||||
-- The hand is NOT permitted as dropped item. ;-)
|
-- The hand is NOT permitted as dropped item. ;-)
|
||||||
-- Item entities will be deleted if they still have an empty itemstring on their first on_step tick.
|
-- Item entities will be deleted if they still have an empty itemstring on their first on_step tick.
|
||||||
itemstring = '',
|
itemstring = "",
|
||||||
|
|
||||||
-- If true, item will fall
|
-- If true, item will fall
|
||||||
physical_state = true,
|
physical_state = true,
|
||||||
|
@ -406,6 +415,14 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local stack = ItemStack(itemstring)
|
local stack = ItemStack(itemstring)
|
||||||
|
if minetest.get_item_group(stack:get_name(), "compass") > 0 then
|
||||||
|
stack:set_name("mcl_compass:16")
|
||||||
|
itemstring = stack:to_string()
|
||||||
|
self.itemstring = itemstring
|
||||||
|
end
|
||||||
|
if minetest.get_item_group(stack:get_name(), "clock") > 0 then
|
||||||
|
self.is_clock = true
|
||||||
|
end
|
||||||
local count = stack:get_count()
|
local count = stack:get_count()
|
||||||
local max_count = stack:get_stack_max()
|
local max_count = stack:get_stack_max()
|
||||||
if count > max_count then
|
if count > max_count then
|
||||||
|
@ -418,13 +435,9 @@ minetest.register_entity(":__builtin:item", {
|
||||||
if itemtable then
|
if itemtable then
|
||||||
itemname = stack:to_table().name
|
itemname = stack:to_table().name
|
||||||
end
|
end
|
||||||
local item_texture = nil
|
|
||||||
local item_type = ""
|
|
||||||
local glow
|
local glow
|
||||||
local def = minetest.registered_items[itemname]
|
local def = minetest.registered_items[itemname]
|
||||||
if def then
|
if def then
|
||||||
item_texture = def.inventory_image
|
|
||||||
item_type = def.type
|
|
||||||
description = def.description
|
description = def.description
|
||||||
glow = def.light_source
|
glow = def.light_source
|
||||||
end
|
end
|
||||||
|
@ -467,7 +480,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
return minetest.serialize({
|
local data = minetest.serialize({
|
||||||
itemstring = self.itemstring,
|
itemstring = self.itemstring,
|
||||||
always_collect = self.always_collect,
|
always_collect = self.always_collect,
|
||||||
age = self.age,
|
age = self.age,
|
||||||
|
@ -475,6 +488,39 @@ minetest.register_entity(":__builtin:item", {
|
||||||
_flowing = self._flowing,
|
_flowing = self._flowing,
|
||||||
_removed = self._removed,
|
_removed = self._removed,
|
||||||
})
|
})
|
||||||
|
-- sfan5 guessed that the biggest serializable item
|
||||||
|
-- entity would have a size of 65530 bytes. This has
|
||||||
|
-- been experimentally verified to be still too large.
|
||||||
|
--
|
||||||
|
-- anon5 has calculated that the biggest serializable
|
||||||
|
-- item entity has a size of exactly 65487 bytes:
|
||||||
|
--
|
||||||
|
-- 1. serializeString16 can handle max. 65535 bytes.
|
||||||
|
-- 2. The following engine metadata is always saved:
|
||||||
|
-- • 1 byte (version)
|
||||||
|
-- • 2 byte (length prefix)
|
||||||
|
-- • 14 byte “__builtin:item”
|
||||||
|
-- • 4 byte (length prefix)
|
||||||
|
-- • 2 byte (health)
|
||||||
|
-- • 3 × 4 byte = 12 byte (position)
|
||||||
|
-- • 4 byte (yaw)
|
||||||
|
-- • 1 byte (version 2)
|
||||||
|
-- • 2 × 4 byte = 8 byte (pitch and roll)
|
||||||
|
-- 3. This leaves 65487 bytes for the serialization.
|
||||||
|
if #data > 65487 then -- would crash the engine
|
||||||
|
local stack = ItemStack(self.itemstring)
|
||||||
|
stack:get_meta():from_table(nil)
|
||||||
|
self.itemstring = stack:to_string()
|
||||||
|
minetest.log(
|
||||||
|
"warning",
|
||||||
|
"Overlong item entity metadata removed: “" ..
|
||||||
|
self.itemstring ..
|
||||||
|
"” had serialized length of " ..
|
||||||
|
#data
|
||||||
|
)
|
||||||
|
return self:get_staticdata()
|
||||||
|
end
|
||||||
|
return data
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime_s)
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
@ -562,7 +608,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime, moveresult)
|
||||||
if self._removed then
|
if self._removed then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
physical = false
|
physical = false
|
||||||
|
@ -572,7 +618,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.age = self.age + dtime
|
self.age = self.age + dtime
|
||||||
if self._collector_timer ~= nil then
|
if self._collector_timer then
|
||||||
self._collector_timer = self._collector_timer + dtime
|
self._collector_timer = self._collector_timer + dtime
|
||||||
end
|
end
|
||||||
if time_to_live > 0 and self.age > time_to_live then
|
if time_to_live > 0 and self.age > time_to_live then
|
||||||
|
@ -593,6 +639,12 @@ minetest.register_entity(":__builtin:item", {
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = minetest.get_node_or_nil(p)
|
||||||
local in_unloaded = (node == nil)
|
local in_unloaded = (node == nil)
|
||||||
|
|
||||||
|
if self.is_clock then
|
||||||
|
self.object:set_properties({
|
||||||
|
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
-- If no collector was found for a long enough time, declare the magnet as disabled
|
-- If no collector was found for a long enough time, declare the magnet as disabled
|
||||||
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
if self._magnet_active and (self._collector_timer == nil or (self._collector_timer > item_drop_settings.magnet_time)) then
|
||||||
self._magnet_active = false
|
self._magnet_active = false
|
||||||
|
@ -623,6 +675,18 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Destroy item when it collides with a cactus
|
||||||
|
if moveresult and moveresult.collides then
|
||||||
|
for _, collision in pairs(moveresult.collisions) do
|
||||||
|
local pos = collision.node_pos
|
||||||
|
if collision.type == "node" and minetest.get_node(pos).name == "mcl_core:cactus" then
|
||||||
|
self._removed = true
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Push item out when stuck inside solid opaque node
|
-- Push item out when stuck inside solid opaque node
|
||||||
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||||
local shootdir
|
local shootdir
|
||||||
|
@ -634,16 +698,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
-- 1st: closest
|
-- 1st: closest
|
||||||
-- 2nd: other direction
|
-- 2nd: other direction
|
||||||
-- 3rd and 4th: other axis
|
-- 3rd and 4th: other axis
|
||||||
local cxcz = function(o, cw, one, zero)
|
|
||||||
if cw < 0 then
|
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
|
||||||
else
|
|
||||||
table.insert(o, { [one]=-1, y=0, [zero]=0 })
|
|
||||||
table.insert(o, { [one]=1, y=0, [zero]=0 })
|
|
||||||
end
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
if math.abs(cx) < math.abs(cz) then
|
if math.abs(cx) < math.abs(cz) then
|
||||||
order = cxcz(order, cx, "x", "z")
|
order = cxcz(order, cx, "x", "z")
|
||||||
order = cxcz(order, cz, "z", "x")
|
order = cxcz(order, cz, "z", "x")
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
function mcl_minecarts:get_sign(z)
|
function mcl_minecarts:get_sign(z)
|
||||||
if z == 0 then
|
if z == 0 then
|
||||||
return 0
|
return 0
|
||||||
|
@ -38,11 +40,9 @@ end
|
||||||
|
|
||||||
function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype)
|
function mcl_minecarts:check_front_up_down(pos, dir_, check_down, railtype)
|
||||||
local dir = vector.new(dir_)
|
local dir = vector.new(dir_)
|
||||||
local cur = nil
|
|
||||||
|
|
||||||
-- Front
|
-- Front
|
||||||
dir.y = 0
|
dir.y = 0
|
||||||
cur = vector.add(pos, dir)
|
local cur = vector.add(pos, dir)
|
||||||
if mcl_minecarts:is_rail(cur, railtype) then
|
if mcl_minecarts:is_rail(cur, railtype) then
|
||||||
return dir
|
return dir
|
||||||
end
|
end
|
||||||
|
@ -65,7 +65,7 @@ end
|
||||||
|
|
||||||
function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
local pos = vector.round(pos_)
|
local pos = vector.round(pos_)
|
||||||
local cur = nil
|
local cur
|
||||||
local left_check, right_check = true, true
|
local left_check, right_check = true, true
|
||||||
|
|
||||||
-- Check left and right
|
-- Check left and right
|
||||||
|
@ -122,7 +122,6 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Backwards
|
-- Backwards
|
||||||
if not old_switch then
|
if not old_switch then
|
||||||
cur = mcl_minecarts:check_front_up_down(pos, {
|
cur = mcl_minecarts:check_front_up_down(pos, {
|
||||||
|
@ -134,7 +133,5 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {x=0, y=0, z=0}
|
return {x=0, y=0, z=0}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
local S = minetest.get_translator("mcl_minecarts")
|
local modname = minetest.get_current_modname()
|
||||||
|
local S = minetest.get_translator(modname)
|
||||||
|
|
||||||
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
||||||
|
|
||||||
mcl_minecarts = {}
|
mcl_minecarts = {}
|
||||||
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
mcl_minecarts.modpath = minetest.get_modpath(modname)
|
||||||
mcl_minecarts.speed_max = 10
|
mcl_minecarts.speed_max = 10
|
||||||
mcl_minecarts.check_float_time = 15
|
mcl_minecarts.check_float_time = 15
|
||||||
|
|
||||||
|
@ -197,14 +198,27 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
else
|
else
|
||||||
self._last_float_check = self._last_float_check + dtime
|
self._last_float_check = self._last_float_check + dtime
|
||||||
end
|
end
|
||||||
local pos, rou_pos, node
|
|
||||||
|
local pos, rou_pos, node = self.object:get_pos()
|
||||||
|
local r = 0.6
|
||||||
|
for _, node_pos in pairs({{r, 0}, {0, r}, {-r, 0}, {0, -r}}) do
|
||||||
|
if minetest.get_node(vector.offset(pos, node_pos[1], 0, node_pos[2])).name == "mcl_core:cactus" then
|
||||||
|
detach_driver(self)
|
||||||
|
for d = 1, #drop do
|
||||||
|
minetest.add_item(pos, drop[d])
|
||||||
|
end
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Drop minecart if it isn't on a rail anymore
|
-- Drop minecart if it isn't on a rail anymore
|
||||||
if self._last_float_check >= mcl_minecarts.check_float_time then
|
if self._last_float_check >= mcl_minecarts.check_float_time then
|
||||||
pos = self.object:get_pos()
|
pos = self.object:get_pos()
|
||||||
rou_pos = vector.round(pos)
|
rou_pos = vector.round(pos)
|
||||||
node = minetest.get_node(rou_pos)
|
node = minetest.get_node(rou_pos)
|
||||||
local g = minetest.get_item_group(node.name, "connect_to_raillike")
|
local g = minetest.get_item_group(node.name, "connect_to_raillike")
|
||||||
if g ~= self._railtype and self._railtype ~= nil then
|
if g ~= self._railtype and self._railtype then
|
||||||
-- Detach driver
|
-- Detach driver
|
||||||
if player then
|
if player then
|
||||||
if self._old_pos then
|
if self._old_pos then
|
||||||
|
@ -486,7 +500,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
if update.pos then
|
if update.pos then
|
||||||
self.object:set_pos(pos)
|
self.object:set_pos(pos)
|
||||||
end
|
end
|
||||||
update = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cart:get_staticdata()
|
function cart:get_staticdata()
|
||||||
|
@ -497,7 +510,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Place a minecart at pointed_thing
|
-- Place a minecart at pointed_thing
|
||||||
mcl_minecarts.place_minecart = function(itemstack, pointed_thing, placer)
|
function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
|
||||||
if not pointed_thing.type == "node" then
|
if not pointed_thing.type == "node" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -524,7 +537,7 @@ mcl_minecarts.place_minecart = function(itemstack, pointed_thing, placer)
|
||||||
local cart = minetest.add_entity(railpos, entity_id)
|
local cart = minetest.add_entity(railpos, entity_id)
|
||||||
local railtype = minetest.get_item_group(node.name, "connect_to_raillike")
|
local railtype = minetest.get_item_group(node.name, "connect_to_raillike")
|
||||||
local le = cart:get_luaentity()
|
local le = cart:get_luaentity()
|
||||||
if le ~= nil then
|
if le then
|
||||||
le._railtype = railtype
|
le._railtype = railtype
|
||||||
end
|
end
|
||||||
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
|
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
|
||||||
|
@ -541,7 +554,7 @@ mcl_minecarts.place_minecart = function(itemstack, pointed_thing, placer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local register_craftitem = function(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
local function register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
||||||
entity_mapping[itemstring] = entity_id
|
entity_mapping[itemstring] = entity_id
|
||||||
|
|
||||||
local groups = { minecart = 1, transport = 1 }
|
local groups = { minecart = 1, transport = 1 }
|
||||||
|
@ -607,7 +620,7 @@ Register a minecart
|
||||||
local function register_minecart(itemstring, entity_id, description, tt_help, longdesc, usagehelp, mesh, textures, icon, drop, on_rightclick, on_activate_by_rail, creative)
|
local function register_minecart(itemstring, entity_id, description, tt_help, longdesc, usagehelp, mesh, textures, icon, drop, on_rightclick, on_activate_by_rail, creative)
|
||||||
register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail)
|
register_entity(entity_id, mesh, textures, drop, on_rightclick, on_activate_by_rail)
|
||||||
register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
register_craftitem(itemstring, entity_id, description, tt_help, longdesc, usagehelp, icon, creative)
|
||||||
if minetest.get_modpath("doc_identifier") ~= nil then
|
if minetest.get_modpath("doc_identifier") then
|
||||||
doc.sub.identifier.register_object(entity_id, "craftitems", itemstring)
|
doc.sub.identifier.register_object(entity_id, "craftitems", itemstring)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -646,7 +659,7 @@ register_minecart(
|
||||||
if player then
|
if player then
|
||||||
mcl_player.player_set_animation(player, "sit" , 30)
|
mcl_player.player_set_animation(player, "sit" , 30)
|
||||||
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
||||||
mcl_tmp_message.message(clicker, S("Sneak to dismount"))
|
mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
end
|
end
|
||||||
|
@ -817,31 +830,30 @@ minetest.register_craft({
|
||||||
})
|
})
|
||||||
|
|
||||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||||
if false then
|
--[[minetest.register_craft({
|
||||||
minetest.register_craft({
|
output = "mcl_minecarts:furnace_minecart",
|
||||||
output = "mcl_minecarts:furnace_minecart",
|
recipe = {
|
||||||
recipe = {
|
{"mcl_furnaces:furnace"},
|
||||||
{"mcl_furnaces:furnace"},
|
{"mcl_minecarts:minecart"},
|
||||||
{"mcl_minecarts:minecart"},
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_minecarts:hopper_minecart",
|
output = "mcl_minecarts:hopper_minecart",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_hoppers:hopper"},
|
{"mcl_hoppers:hopper"},
|
||||||
{"mcl_minecarts:minecart"},
|
{"mcl_minecarts:minecart"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "mcl_minecarts:chest_minecart",
|
||||||
|
recipe = {
|
||||||
|
{"mcl_chests:chest"},
|
||||||
|
{"mcl_minecarts:minecart"},
|
||||||
|
},
|
||||||
|
})]]
|
||||||
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_minecarts:chest_minecart",
|
|
||||||
recipe = {
|
|
||||||
{"mcl_chests:chest"},
|
|
||||||
{"mcl_minecarts:minecart"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if has_mcl_wip then
|
if has_mcl_wip then
|
||||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||||
|
|
|
@ -33,3 +33,4 @@ Activates minecarts when powered=Aktiviert Loren, wenn bestromt
|
||||||
Emits redstone power when a minecart is detected=Gibt ein Redstonesignal aus, wenn eine Lore erfasst wird
|
Emits redstone power when a minecart is detected=Gibt ein Redstonesignal aus, wenn eine Lore erfasst wird
|
||||||
Vehicle for fast travel on rails=Fahrzeug zum schnellen Transport auf Schienen
|
Vehicle for fast travel on rails=Fahrzeug zum schnellen Transport auf Schienen
|
||||||
Can be ignited by tools or powered activator rail=Kann mit Werkzeugen oder bestromten Aktivierungsschienen angezündet werden
|
Can be ignited by tools or powered activator rail=Kann mit Werkzeugen oder bestromten Aktivierungsschienen angezündet werden
|
||||||
|
Sneak to dismount=Zum Aussteigen schleichen
|
||||||
|
|
|
@ -33,3 +33,4 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
||||||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||||
|
Sneak to dismount=
|
|
@ -0,0 +1,36 @@
|
||||||
|
# textdomain: mcl_minecarts
|
||||||
|
Minecart=Wagonik
|
||||||
|
Minecarts can be used for a quick transportion on rails.=Wagoniki mogą być użyte do szybkiego transportu po torach.
|
||||||
|
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.=Wagoniki mogą jeździć tylko po torach i zawsze podążają za wytyczoną ścieżką. W przypadku skrzyżowań typu T, gdzie nie ma prostej ścieżki, skręcają w lew. Ich szybkość zależy od typu torów.
|
||||||
|
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Możesz postawić wagonik na torach. Kliknij prawym przyciskiem myszy aby do niego wejść. Uderz go by zaczął się poruszać.
|
||||||
|
To obtain the minecart, punch it while holding down the sneak key.=Aby odzyskać wagonik uderz go podczas skradania.
|
||||||
|
A minecart with TNT is an explosive vehicle that travels on rail.=Wagonik z TNT jest wybuchowym pojazdem podróżującym po torach.
|
||||||
|
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.=Postaw go na torach. Uderz by zaczął się poruszać. TNT zapala się krzesiwem lub gdy wagonik jest na zasilonych torach aktywacyjnych.
|
||||||
|
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Aby odzyskać wagonik z TNT uderz go podczas skradania. Nie możesz tego zrobić gdy TNT jest zapalone.
|
||||||
|
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Wagonik z piecem jest pojazdem podróżującym na torach. Napędza on samego siebie za pomocą paliwa.
|
||||||
|
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.=Postaw go na torach. Jeśli dasz mu nieco węgla piec zacznie palić przez długi czas, a wagonik będzie się sam poruszał. Uderz go by zaczął się poruszać.
|
||||||
|
To obtain the minecart and furnace, punch them while holding down the sneak key.=Aby odzyskać wagonik z piecem uderz go podczas skradania.
|
||||||
|
Minecart with Chest=Wagonik ze skrzynią
|
||||||
|
Minecart with Furnace=Wagonik z piecem
|
||||||
|
Minecart with Command Block=Wagonik z blokiem poleceń
|
||||||
|
Minecart with Hopper=Wagonik z lejem
|
||||||
|
Minecart with TNT=Wagonik z 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.=Postaw je na ziemi by zbudować ścieżkę z torów. Tory automatycznie połączą się ze sobą i zamienią się w zakręty, skrzyżowania typu T, skrzyżowania i równie w zależności od potrzeb.
|
||||||
|
Rail=Tor
|
||||||
|
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Zwyczajne tory nieco spowalniają wagoniki ze względu na tarcie.
|
||||||
|
Powered Rail=Zasilane tory
|
||||||
|
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Zasilane tory mogą przyspieszać lub spowalniać wagoniki.
|
||||||
|
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Bez zasilania czerwienitem tory będą spowalniać wagoniki. Aby sprawić by je przyspieszały zasil je czerwienitem.
|
||||||
|
Activator Rail=Tory aktywacyjne
|
||||||
|
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Tory aktywacyjne są wykorzystywane do aktywacji specjalnych wagoników.
|
||||||
|
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Aby ten tor aktywował wagonik, zasil go czerwienitem i spraw by wagonik po nim przejechał.
|
||||||
|
Detector Rail=Tory z czujnikiem
|
||||||
|
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.=Tory mogą być wykorzystane do zbudowania torów dla wagoników. Tory z czujnikiem są w stanie wykryć kiedy wagonik po nich przejeżdża i wysłać sygnał do czerwienitowych mechanizmów.
|
||||||
|
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Aby wykryć wagonik i dostarczyć zasilanie czerwienitem podłącz go czerwienitem to mechanizmu i spraw by wagonik po nim przejechał.
|
||||||
|
Track for minecarts=Tor dla wagoników
|
||||||
|
Speed up when powered, slow down when not powered=Przyspiesza gdy zasilane, spowalnia gdy nie
|
||||||
|
Activates minecarts when powered=Aktywuje wagoniki gdy zasilane
|
||||||
|
Emits redstone power when a minecart is detected=Emituje zasilanie czerwienitem gdy wagonik jest wykryty
|
||||||
|
Vehicle for fast travel on rails=Pojazd do szybkiej podróży na torach
|
||||||
|
Can be ignited by tools or powered activator rail=Może być zapalony przez narzędzia, lub zasilane tor aktywacyjne
|
||||||
|
Sneak to dismount=Zacznij się skradać by zejść
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier, mcl_wip
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local S = minetest.get_translator("mcl_minecarts")
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
-- Template rail function
|
-- Template rail function
|
||||||
local register_rail = function(itemstring, tiles, def_extras, creative)
|
local function register_rail(itemstring, tiles, def_extras, creative)
|
||||||
local groups = {handy=1,pickaxey=1, attached_node=1,rail=1,connect_to_raillike=minetest.raillike_group("rail"),dig_by_water=1,destroy_by_lava_flow=1, transport=1}
|
local groups = {handy=1,pickaxey=1, attached_node=1,rail=1,connect_to_raillike=minetest.raillike_group("rail"),dig_by_water=1,destroy_by_lava_flow=1, transport=1}
|
||||||
if creative == false then
|
if creative == false then
|
||||||
groups.not_in_creative_inventory = 1
|
groups.not_in_creative_inventory = 1
|
||||||
|
@ -206,11 +206,11 @@ register_rail("mcl_minecarts:detector_rail_on",
|
||||||
|
|
||||||
-- Crafting
|
-- Crafting
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = 'mcl_minecarts:rail 16',
|
output = "mcl_minecarts:rail 16",
|
||||||
recipe = {
|
recipe = {
|
||||||
{'mcl_core:iron_ingot', '', 'mcl_core:iron_ingot'},
|
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
||||||
{'mcl_core:iron_ingot', 'mcl_core:stick', 'mcl_core:iron_ingot'},
|
{"mcl_core:iron_ingot", "mcl_core:stick", "mcl_core:iron_ingot"},
|
||||||
{'mcl_core:iron_ingot', '', 'mcl_core:iron_ingot'},
|
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -502,20 +502,6 @@ and damages any entity caught inside the blast radius. Protection will limit
|
||||||
node destruction but not entity damage.
|
node destruction but not entity damage.
|
||||||
|
|
||||||
|
|
||||||
mobs:capture_mob
|
|
||||||
----------------
|
|
||||||
|
|
||||||
mobs:capture_mob(...)
|
|
||||||
|
|
||||||
Does nothing and returns false.
|
|
||||||
|
|
||||||
This function is provided for compability with Mobs Redo for an attempt to
|
|
||||||
capture a mob.
|
|
||||||
Mobs cannot be captured in MineClone 2.
|
|
||||||
|
|
||||||
In Mobs Redo, this is generally called inside the on_rightclick section of the mob
|
|
||||||
api code, it provides a chance of capturing the mob. See Mobs Redo documentation
|
|
||||||
of parameters.
|
|
||||||
|
|
||||||
Feeding and Taming/Breeding
|
Feeding and Taming/Breeding
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -535,19 +521,6 @@ Will return true when mob is fed with item it likes.
|
||||||
them up
|
them up
|
||||||
|
|
||||||
|
|
||||||
Protecting Mobs
|
|
||||||
---------------
|
|
||||||
|
|
||||||
mobs:protect(self, clicker)
|
|
||||||
|
|
||||||
This function can be used to right-click any tamed mob with mobs:protector item,
|
|
||||||
this will protect the mob from harm inside of a protected area from other
|
|
||||||
players. Will return true when mob right-clicked with mobs:protector item.
|
|
||||||
|
|
||||||
'self' mob information
|
|
||||||
'clicker' player information
|
|
||||||
|
|
||||||
|
|
||||||
Riding Mobs
|
Riding Mobs
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -605,7 +578,7 @@ Note: animation names above are from the pre-defined animation lists inside mob
|
||||||
registry without extensions.
|
registry without extensions.
|
||||||
|
|
||||||
|
|
||||||
mobs:set_animation(self, name)
|
mobs.set_mob_animation(self, name)
|
||||||
|
|
||||||
This function sets the current animation for mob, defaulting to "stand" if not
|
This function sets the current animation for mob, defaulting to "stand" if not
|
||||||
found.
|
found.
|
||||||
|
@ -781,8 +754,5 @@ mobs:register_mob("mob_horse:horse", {
|
||||||
inv:remove_item("main", "mobs:saddle")
|
inv:remove_item("main", "mobs:saddle")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- used to capture horse with magic lasso
|
|
||||||
mobs:capture_mob(self, clicker, 0, 0, 80, false, nil)
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,736 @@
|
||||||
|
-- API for Mobs Redo: MineClone 2 Delux 2.0 DRM Free Early Access Super Extreme Edition
|
||||||
|
|
||||||
|
-- mobs library
|
||||||
|
mobs = {}
|
||||||
|
|
||||||
|
-- lua locals - can grab from this to easily plop them into the api lua files
|
||||||
|
|
||||||
|
--localize minetest functions
|
||||||
|
local minetest_settings = minetest.settings
|
||||||
|
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
local minetest_get_modpath = minetest.get_modpath
|
||||||
|
local minetest_registered_nodes = minetest.registered_nodes
|
||||||
|
local minetest_get_node = minetest.get_node
|
||||||
|
--local minetest_get_item_group = minetest.get_item_group
|
||||||
|
local minetest_registered_entities = minetest.registered_entities
|
||||||
|
--local minetest_line_of_sight = minetest.line_of_sight
|
||||||
|
--local minetest_after = minetest.after
|
||||||
|
--local minetest_sound_play = minetest.sound_play
|
||||||
|
--local minetest_add_particlespawner = minetest.add_particlespawner
|
||||||
|
--local minetest_registered_items = minetest.registered_items
|
||||||
|
--local minetest_set_node = minetest.set_node
|
||||||
|
local minetest_add_item = minetest.add_item
|
||||||
|
--local minetest_get_craft_result = minetest.get_craft_result
|
||||||
|
--local minetest_find_path = minetest.find_path
|
||||||
|
local minetest_is_creative_enabled = minetest.is_creative_enabled
|
||||||
|
--local minetest_find_node_near = minetest.find_node_near
|
||||||
|
--local minetest_find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
||||||
|
--local minetest_raycast = minetest.raycast
|
||||||
|
--local minetest_get_us_time = minetest.get_us_time
|
||||||
|
local minetest_add_entity = minetest.add_entity
|
||||||
|
--local minetest_get_natural_light = minetest.get_natural_light
|
||||||
|
--local minetest_get_node_or_nil = minetest.get_node_or_nil
|
||||||
|
|
||||||
|
-- localize math functions
|
||||||
|
local math = math
|
||||||
|
|
||||||
|
-- localize vector functions
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local string = string
|
||||||
|
|
||||||
|
-- mob constants
|
||||||
|
--local BREED_TIME = 30
|
||||||
|
--local BREED_TIME_AGAIN = 300
|
||||||
|
--local CHILD_GROW_TIME = 60*20
|
||||||
|
--local DEATH_DELAY = 0.5
|
||||||
|
local DEFAULT_FALL_SPEED = -10
|
||||||
|
--local FLOP_HEIGHT = 5.0
|
||||||
|
--local FLOP_HOR_SPEED = 1.5
|
||||||
|
local GRAVITY = minetest_settings:get("movement_gravity")-- + 9.81
|
||||||
|
|
||||||
|
local MAX_MOB_NAME_LENGTH = 30
|
||||||
|
|
||||||
|
|
||||||
|
--[[local MOB_CAP = {}
|
||||||
|
MOB_CAP.hostile = 70
|
||||||
|
MOB_CAP.passive = 10
|
||||||
|
MOB_CAP.ambient = 15
|
||||||
|
MOB_CAP.water = 15
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- Load main settings
|
||||||
|
--local damage_enabled = minetest_settings:get_bool("enable_damage")
|
||||||
|
--local disable_blood = minetest_settings:get_bool("mobs_disable_blood")
|
||||||
|
--local mobs_drop_items = minetest_settings:get_bool("mobs_drop_items") ~= false
|
||||||
|
--local mobs_griefing = minetest_settings:get_bool("mobs_griefing") ~= false
|
||||||
|
--local spawn_protected = minetest_settings:get_bool("mobs_spawn_protected") ~= false
|
||||||
|
--local remove_far = true
|
||||||
|
local difficulty = tonumber(minetest_settings:get("mob_difficulty")) or 1.0
|
||||||
|
--local show_health = false
|
||||||
|
--local max_per_block = tonumber(minetest_settings:get("max_objects_per_block") or 64)
|
||||||
|
---local mobs_spawn_chance = tonumber(minetest_settings:get("mobs_spawn_chance") or 2.5)
|
||||||
|
|
||||||
|
-- pathfinding settings
|
||||||
|
--local enable_pathfinding = true
|
||||||
|
--local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||||
|
--local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
||||||
|
|
||||||
|
-- default nodes
|
||||||
|
--local node_ice = "mcl_core:ice"
|
||||||
|
--local node_snowblock = "mcl_core:snowblock"
|
||||||
|
--local node_snow = "mcl_core:snow"
|
||||||
|
mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
||||||
|
|
||||||
|
--local mod_weather = minetest_get_modpath("mcl_weather")
|
||||||
|
--local mod_explosions = minetest_get_modpath("mcl_explosions")
|
||||||
|
local mod_mobspawners = minetest_get_modpath("mcl_mobspawners")
|
||||||
|
--local mod_hunger = minetest_get_modpath("mcl_hunger")
|
||||||
|
--local mod_worlds = minetest_get_modpath("mcl_worlds")
|
||||||
|
--local mod_armor = minetest_get_modpath("mcl_armor")
|
||||||
|
--local mod_experience = minetest_get_modpath("mcl_experience")
|
||||||
|
|
||||||
|
|
||||||
|
-- random locals I found
|
||||||
|
--local los_switcher = false
|
||||||
|
--local height_switcher = false
|
||||||
|
|
||||||
|
-- Get translator
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
-- CMI support check
|
||||||
|
--local use_cmi = minetest.global_exists("cmi")
|
||||||
|
|
||||||
|
-- creative check
|
||||||
|
function mobs.is_creative(name)
|
||||||
|
return minetest_is_creative_enabled(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[local function atan(x)
|
||||||
|
if not x or x ~= x then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return math.atan(x)
|
||||||
|
end
|
||||||
|
end]]
|
||||||
|
|
||||||
|
-- Shows helpful debug info above each mob
|
||||||
|
--local mobs_debug = minetest_settings:get_bool("mobs_debug", false)
|
||||||
|
|
||||||
|
-- Peaceful mode message so players will know there are no monsters
|
||||||
|
if minetest_settings:get_bool("only_peaceful_mobs", false) then
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
minetest.chat_send_player(player:get_player_name(),
|
||||||
|
S("Peaceful mode active! No monsters will spawn."))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local api_path = minetest.get_modpath(minetest.get_current_modname()).."/api/mob_functions/"
|
||||||
|
|
||||||
|
--ignite all parts of the api
|
||||||
|
dofile(api_path .. "flow_lib.lua")
|
||||||
|
dofile(api_path .. "ai.lua")
|
||||||
|
dofile(api_path .. "animation.lua")
|
||||||
|
dofile(api_path .. "collision.lua")
|
||||||
|
dofile(api_path .. "environment.lua")
|
||||||
|
dofile(api_path .. "interaction.lua")
|
||||||
|
dofile(api_path .. "movement.lua")
|
||||||
|
dofile(api_path .. "set_up.lua")
|
||||||
|
dofile(api_path .. "attack_type_instructions.lua")
|
||||||
|
dofile(api_path .. "sound_handling.lua")
|
||||||
|
dofile(api_path .. "death_logic.lua")
|
||||||
|
dofile(api_path .. "mob_effects.lua")
|
||||||
|
dofile(api_path .. "projectile_handling.lua")
|
||||||
|
dofile(api_path .. "breeding.lua")
|
||||||
|
dofile(api_path .. "head_logic.lua")
|
||||||
|
|
||||||
|
|
||||||
|
mobs.spawning_mobs = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- register mob entity
|
||||||
|
function mobs:register_mob(name, def)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
mobs.spawning_mobs[name] = true
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
minetest.register_entity(name, {
|
||||||
|
description = def.description,
|
||||||
|
use_texture_alpha = def.use_texture_alpha,
|
||||||
|
stepheight = def.stepheight or 0.6,
|
||||||
|
stepheight_backup = def.stepheight or 0.6,
|
||||||
|
name = name,
|
||||||
|
type = def.type,
|
||||||
|
attack_type = def.attack_type,
|
||||||
|
fly = def.fly,
|
||||||
|
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,
|
||||||
|
jump_height = def.jump_height or 4, -- was 6
|
||||||
|
rotate = def.rotate or 0, -- 0=front, 90=side, 180=back, 270=side2
|
||||||
|
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
||||||
|
hp_max = scale_difficulty(def.hp_max, 10, 1),
|
||||||
|
xp_min = def.xp_min or 1,
|
||||||
|
xp_max = def.xp_max or 5,
|
||||||
|
breath_max = def.breath_max or 6,
|
||||||
|
breathes_in_water = def.breathes_in_water or false,
|
||||||
|
physical = true,
|
||||||
|
collisionbox = collisionbox,
|
||||||
|
collide_with_objects = def.collide_with_objects or false,
|
||||||
|
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 = mobs.create_mob_on_rightclick(def.on_rightclick),
|
||||||
|
arrow = def.arrow,
|
||||||
|
shoot_interval = def.shoot_interval,
|
||||||
|
sounds = def.sounds or {},
|
||||||
|
animation = def.animation,
|
||||||
|
jump = def.jump ~= false,
|
||||||
|
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,
|
||||||
|
state_timer = 0,
|
||||||
|
env_damage_timer = 0,
|
||||||
|
tamed = false,
|
||||||
|
pause_timer = 0,
|
||||||
|
gotten = false,
|
||||||
|
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,
|
||||||
|
specific_attack = def.specific_attack,
|
||||||
|
runaway_from = def.runaway_from,
|
||||||
|
owner_loyal = def.owner_loyal,
|
||||||
|
facing_fence = false,
|
||||||
|
|
||||||
|
_cmi_is_mob = true,
|
||||||
|
|
||||||
|
pushable = def.pushable or true,
|
||||||
|
|
||||||
|
--j4i stuff
|
||||||
|
yaw = 0,
|
||||||
|
automatic_face_movement_dir = def.rotate or 0, -- 0=front, 90=side, 180=back, 270=side2
|
||||||
|
automatic_face_movement_max_rotation_per_sec = 360, --degrees
|
||||||
|
backface_culling = true,
|
||||||
|
walk_timer = 0,
|
||||||
|
stand_timer = 0,
|
||||||
|
current_animation = "",
|
||||||
|
gravity = GRAVITY,
|
||||||
|
swim = def.swim,
|
||||||
|
swim_in = def.swim_in or {mobs_mc.items.water_source, "mcl_core:water_flowing", mobs_mc.items.river_water_source},
|
||||||
|
pitch_switch = "static",
|
||||||
|
jump_only = def.jump_only,
|
||||||
|
hostile = def.hostile,
|
||||||
|
neutral = def.neutral,
|
||||||
|
attacking = nil,
|
||||||
|
visual_size_origin = def.visual_size or {x = 1, y = 1, z = 1},
|
||||||
|
punch_timer_cooloff = def.punch_timer_cooloff or 0.5,
|
||||||
|
death_animation_timer = 0,
|
||||||
|
hostile_cooldown = def.hostile_cooldown or 15,
|
||||||
|
tilt_fly = def.tilt_fly,
|
||||||
|
tilt_swim = def.tilt_swim,
|
||||||
|
fall_slow = def.fall_slow,
|
||||||
|
projectile_cooldown_min = def.projectile_cooldown_min or 2,
|
||||||
|
projectile_cooldown_max = def.projectile_cooldown_max or 6,
|
||||||
|
skittish = def.skittish,
|
||||||
|
|
||||||
|
minimum_follow_distance = def.minimum_follow_distance or 0.5, --make mobs not freak out when underneath
|
||||||
|
|
||||||
|
memory = 0, -- memory timer if chasing/following
|
||||||
|
fly_random_while_attack = def.fly_random_while_attack,
|
||||||
|
|
||||||
|
--for spiders
|
||||||
|
always_climb = def.always_climb,
|
||||||
|
|
||||||
|
--despawn mechanic variables
|
||||||
|
lifetimer_reset = 30, --30 seconds
|
||||||
|
lifetimer = 30, --30 seconds
|
||||||
|
|
||||||
|
--breeding stuff
|
||||||
|
breed_timer = 0,
|
||||||
|
breed_lookout_timer = 0,
|
||||||
|
breed_distance = def.breed_distance or 1.5, --how far away mobs have to be to begin actual breeding
|
||||||
|
breed_lookout_timer_goal = 30, --30 seconds (this timer is for how long the mob looks for a mate)
|
||||||
|
breed_timer_cooloff = 5*60, -- 5 minutes (this timer is for how long the mob has to wait before being bred again)
|
||||||
|
bred = false,
|
||||||
|
follow = def.follow, --this item is also used for the breeding mechanism
|
||||||
|
follow_distance = def.follow_distance or 2,
|
||||||
|
baby_size = def.baby_size or 0.5,
|
||||||
|
baby = false,
|
||||||
|
grow_up_timer = 0,
|
||||||
|
grow_up_goal = 20*60, --in 20 minutes the mob grows up
|
||||||
|
special_breed_timer = 0, --this is used for the AHEM AHEM part of breeding
|
||||||
|
|
||||||
|
backup_visual_size = def.visual_size,
|
||||||
|
backup_collisionbox = collisionbox,
|
||||||
|
backup_selectionbox = def.selectionbox or def.collisionbox,
|
||||||
|
|
||||||
|
|
||||||
|
--fire timer
|
||||||
|
burn_timer = 0,
|
||||||
|
|
||||||
|
ignores_cobwebs = def.ignores_cobwebs,
|
||||||
|
breath = def.breath_max or 6,
|
||||||
|
|
||||||
|
random_sound_timer_min = 3,
|
||||||
|
random_sound_timer_max = 10,
|
||||||
|
|
||||||
|
--head code variables
|
||||||
|
--defaults are for the cow's default
|
||||||
|
--because I don't know what else to set them
|
||||||
|
--to :P
|
||||||
|
|
||||||
|
--you must use these to adjust the mob's head positions
|
||||||
|
|
||||||
|
--has_head is used as a logic gate (quick easy check)
|
||||||
|
has_head = def.has_head or false,
|
||||||
|
--head_bone is the actual bone in the model which the head
|
||||||
|
--is attached to for animation
|
||||||
|
head_bone = def.head_bone or "head",
|
||||||
|
|
||||||
|
--this part controls the base position of the head calculations
|
||||||
|
--localized to the mob's visual yaw when gotten (self.object:get_yaw())
|
||||||
|
--you can enable the debug in /mob_functions/head_logic.lua by uncommenting the
|
||||||
|
--particle spawner code
|
||||||
|
head_height_offset = def.head_height_offset or 1.0525,
|
||||||
|
head_direction_offset = def.head_direction_offset or 0.5,
|
||||||
|
|
||||||
|
--this part controls the visual of the head
|
||||||
|
head_bone_pos_y = def.head_bone_pos_y or 3.6,
|
||||||
|
head_bone_pos_z = def.head_bone_pos_z or -0.6,
|
||||||
|
head_pitch_modifier = def.head_pitch_modifier or 0,
|
||||||
|
|
||||||
|
--these variables are switches in case the model
|
||||||
|
--moves the wrong way
|
||||||
|
swap_y_with_x = def.swap_y_with_x or false,
|
||||||
|
reverse_head_yaw = def.reverse_head_yaw or false,
|
||||||
|
|
||||||
|
--END HEAD CODE VARIABLES
|
||||||
|
|
||||||
|
--end j4i stuff
|
||||||
|
|
||||||
|
-- MCL2 extensions
|
||||||
|
teleport = mobs.teleport,
|
||||||
|
do_teleport = def.do_teleport,
|
||||||
|
spawn_class = def.spawn_class,
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
eye_height = def.eye_height or 1.5,
|
||||||
|
defuse_reach = def.defuse_reach or 4,
|
||||||
|
-- End of MCL2 extensions
|
||||||
|
|
||||||
|
on_spawn = def.on_spawn,
|
||||||
|
|
||||||
|
--on_blast = def.on_blast or do_tnt,
|
||||||
|
|
||||||
|
on_step = mobs.mob_step,
|
||||||
|
|
||||||
|
--do_punch = def.do_punch,
|
||||||
|
|
||||||
|
on_punch = mobs.mob_punch,
|
||||||
|
|
||||||
|
--on_breed = def.on_breed,
|
||||||
|
|
||||||
|
--on_grown = def.on_grown,
|
||||||
|
|
||||||
|
--on_detach_child = mob_detach_child,
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata, dtime)
|
||||||
|
self.object:set_acceleration(vector.new(0,-GRAVITY, 0))
|
||||||
|
return mobs.mob_activate(self, staticdata, def, dtime)
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_staticdata = function(self)
|
||||||
|
return mobs.mob_staticdata(self)
|
||||||
|
end,
|
||||||
|
|
||||||
|
--harmed_by_heal = def.harmed_by_heal,
|
||||||
|
})
|
||||||
|
|
||||||
|
if minetest_get_modpath("doc_identifier") then
|
||||||
|
doc.sub.identifier.register_object(name, "basics", "mobs")
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- END mobs:register_mob function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- register arrow for shoot attack
|
||||||
|
function mobs:register_arrow(name, def)
|
||||||
|
|
||||||
|
-- errorcheck
|
||||||
|
if not name or not def then
|
||||||
|
print("failed to register arrow entity")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_entity(name.."_entity", {
|
||||||
|
|
||||||
|
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,
|
||||||
|
speed = def.speed or nil,
|
||||||
|
on_step = function(self)
|
||||||
|
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
if self.timer > 150
|
||||||
|
or not mobs.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
|
||||||
|
|
||||||
|
--do this to prevent clipping through main entity sprite
|
||||||
|
local pos_adjustment = vector.multiply(vector.normalize(vel), -1)
|
||||||
|
local divider = def.tail_distance_divider or 1
|
||||||
|
pos_adjustment = vector.divide(pos_adjustment, divider)
|
||||||
|
local new_pos = vector.add(pos, pos_adjustment)
|
||||||
|
minetest.add_particle({
|
||||||
|
pos = new_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 = minetest_get_node(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
|
||||||
|
|
||||||
|
if self.hit_player then
|
||||||
|
self.hit_player(self, player)
|
||||||
|
else
|
||||||
|
mobs.arrow_hit(self, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.object:remove();
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
local entity = player:get_luaentity()
|
||||||
|
|
||||||
|
if entity
|
||||||
|
and self.hit_mob
|
||||||
|
and entity._cmi_is_mob == true
|
||||||
|
and tostring(player) ~= self.owner_id
|
||||||
|
and entity.name ~= self.object:get_luaentity().name
|
||||||
|
and (self._shooter and entity.name ~= self._shooter:get_luaentity().name) then
|
||||||
|
|
||||||
|
--self.hit_mob(self, player)
|
||||||
|
self.object:remove();
|
||||||
|
return
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--[[
|
||||||
|
if entity
|
||||||
|
and self.hit_object
|
||||||
|
and (not entity._cmi_is_mob)
|
||||||
|
and tostring(player) ~= self.owner_id
|
||||||
|
and entity.name ~= self.object:get_luaentity().name
|
||||||
|
and (self._shooter and entity.name ~= self._shooter: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 mobs:register_egg(mob, desc, background, 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 = background
|
||||||
|
|
||||||
|
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 mod_mobspawners and 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 mobs.is_creative(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
|
||||||
|
|
||||||
|
local mob = minetest_add_entity(pos, mob)
|
||||||
|
minetest.log("action", "Mob spawned: "..name.." 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 mobs.is_creative(placer:get_player_name()) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local HALF_PI = math.pi/2
|
||||||
|
|
||||||
|
|
||||||
|
local vector_direction = vector.direction
|
||||||
|
local vector_distance = vector.distance
|
||||||
|
local vector_new = vector.new
|
||||||
|
|
||||||
|
local minetest_dir_to_yaw = minetest.dir_to_yaw
|
||||||
|
|
||||||
|
-- set defined animation
|
||||||
|
mobs.set_mob_animation = function(self, 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 (not self.animation[anim .. "_start"] or not self.animation[anim .. "_end"]) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--animations break if they are constantly set
|
||||||
|
--so we put this return gate to check if it is
|
||||||
|
--already at the animation we are trying to implement
|
||||||
|
if self.current_animation == anim then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.current_animation = anim
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mobs.death_effect = function(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
|
||||||
|
|
||||||
|
|
||||||
|
--this allows auto facedir rotation while making it so mobs
|
||||||
|
--don't look like wet noodles flopping around
|
||||||
|
mobs.movement_rotation_lock = function(self)
|
||||||
|
local current_engine_yaw = self.object:get_yaw()
|
||||||
|
local current_lua_yaw = self.yaw
|
||||||
|
|
||||||
|
if current_engine_yaw > math.pi * 2 then
|
||||||
|
current_engine_yaw = current_engine_yaw - (math.pi * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if math.abs(current_engine_yaw - current_lua_yaw) <= 0.05 and self.object:get_properties().automatic_face_movement_dir then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = false}
|
||||||
|
elseif math.abs(current_engine_yaw - current_lua_yaw) > 0.05 and self.object:get_properties().automatic_face_movement_dir == false then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = self.rotate}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--this is used when a mob is chasing a player
|
||||||
|
mobs.set_yaw_while_attacking = function(self)
|
||||||
|
|
||||||
|
if self.object:get_properties().automatic_face_movement_dir then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = false}
|
||||||
|
end
|
||||||
|
|
||||||
|
--turn positions into pseudo 2d vectors
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
|
||||||
|
local pos2 = self.attacking:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
|
||||||
|
local new_direction = vector_direction(pos1,pos2)
|
||||||
|
local new_yaw = minetest_dir_to_yaw(new_direction)
|
||||||
|
|
||||||
|
self.object:set_yaw(new_yaw)
|
||||||
|
self.yaw = new_yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is used to unlock a mob's yaw after attacking
|
||||||
|
mobs.unlock_yaw = function(self)
|
||||||
|
if self.object:get_properties().automatic_face_movement_dir == false then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = self.rotate}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is used to lock a mob's yaw when they're standing
|
||||||
|
mobs.lock_yaw = function(self)
|
||||||
|
if self.object:get_properties().automatic_face_movement_dir then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = false}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local calculate_pitch = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local pos2 = self.old_pos
|
||||||
|
|
||||||
|
if pos == nil or pos2 == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest_dir_to_yaw(vector_new(vector_distance(vector_new(pos.x,0,pos.z),vector_new(pos2.x,0,pos2.z)),0,pos.y - pos2.y)) + HALF_PI
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is a helper function used to make mobs pitch rotation dynamically flow when flying/swimming
|
||||||
|
mobs.set_dynamic_pitch = function(self)
|
||||||
|
local pitch = calculate_pitch(self)
|
||||||
|
|
||||||
|
if not pitch then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_rotation = self.object:get_rotation()
|
||||||
|
|
||||||
|
current_rotation.x = pitch
|
||||||
|
|
||||||
|
self.object:set_rotation(current_rotation)
|
||||||
|
|
||||||
|
self.pitch_switch = "dynamic"
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is a helper function used to make mobs pitch rotation reset when flying/swimming
|
||||||
|
mobs.set_static_pitch = function(self)
|
||||||
|
|
||||||
|
if self.pitch_switch == "static" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_rotation = self.object:get_rotation()
|
||||||
|
|
||||||
|
current_rotation.x = 0
|
||||||
|
|
||||||
|
self.object:set_rotation(current_rotation)
|
||||||
|
self.pitch_switch = "static"
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is a helper function for mobs explosion animation
|
||||||
|
mobs.handle_explosion_animation = function(self)
|
||||||
|
|
||||||
|
--secondary catch-all
|
||||||
|
if not self.explosion_animation then
|
||||||
|
self.explosion_animation = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--the timer works from 0 for sense of a 0 based counting
|
||||||
|
--but this just bumps it up so it's usable in here
|
||||||
|
local explosion_timer_adjust = self.explosion_animation + 1
|
||||||
|
|
||||||
|
|
||||||
|
local visual_size_modified = table.copy(self.visual_size_origin)
|
||||||
|
|
||||||
|
visual_size_modified.x = visual_size_modified.x * (explosion_timer_adjust ^ 3)
|
||||||
|
visual_size_modified.y = visual_size_modified.y * explosion_timer_adjust
|
||||||
|
|
||||||
|
self.object:set_properties({visual_size = visual_size_modified})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--this is used when a mob is following player
|
||||||
|
mobs.set_yaw_while_following = function(self)
|
||||||
|
|
||||||
|
if self.object:get_properties().automatic_face_movement_dir then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = false}
|
||||||
|
end
|
||||||
|
|
||||||
|
--turn positions into pseudo 2d vectors
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
|
||||||
|
local pos2 = self.following_person:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
|
||||||
|
local new_direction = vector_direction(pos1,pos2)
|
||||||
|
local new_yaw = minetest_dir_to_yaw(new_direction)
|
||||||
|
|
||||||
|
self.object:set_yaw(new_yaw)
|
||||||
|
self.yaw = new_yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is used for when mobs breed
|
||||||
|
mobs.set_yaw_while_breeding = function(self, mate)
|
||||||
|
|
||||||
|
if self.object:get_properties().automatic_face_movement_dir then
|
||||||
|
self.object:set_properties{automatic_face_movement_dir = false}
|
||||||
|
end
|
||||||
|
|
||||||
|
--turn positions into pseudo 2d vectors
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
|
||||||
|
local pos2 = mate:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
|
||||||
|
local new_direction = vector_direction(pos1,pos2)
|
||||||
|
local new_yaw = minetest_dir_to_yaw(new_direction)
|
||||||
|
|
||||||
|
self.object:set_yaw(new_yaw)
|
||||||
|
self.yaw = new_yaw
|
||||||
|
end
|
|
@ -0,0 +1,347 @@
|
||||||
|
local vector_direction = vector.direction
|
||||||
|
--local minetest_dir_to_yaw = minetest.dir_to_yaw
|
||||||
|
local vector_distance = vector.distance
|
||||||
|
local vector_multiply = vector.multiply
|
||||||
|
local math_random = math.random
|
||||||
|
|
||||||
|
--[[
|
||||||
|
_ _ _ _
|
||||||
|
| | | | | | | |
|
||||||
|
| | | | __ _ _ __ __| | | |
|
||||||
|
| | | | / _` | '_ \ / _` | | |
|
||||||
|
|_| | |___| (_| | | | | (_| | |_|
|
||||||
|
(_) \_____/\__,_|_| |_|\__,_| (_)
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
_____ _ _
|
||||||
|
| ___| | | | |
|
||||||
|
| |____ ___ __ | | ___ __| | ___
|
||||||
|
| __\ \/ / '_ \| |/ _ \ / _` |/ _ \
|
||||||
|
| |___> <| |_) | | (_) | (_| | __/
|
||||||
|
\____/_/\_\ .__/|_|\___/ \__,_|\___|
|
||||||
|
| |
|
||||||
|
|_|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
mobs.explode_attack_walk = function(self,dtime)
|
||||||
|
|
||||||
|
--this needs an exception
|
||||||
|
if self.attacking == nil or not self.attacking:is_player() then
|
||||||
|
self.attacking = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
|
||||||
|
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||||
|
|
||||||
|
--make mob walk up to player within 2 nodes distance then start exploding
|
||||||
|
if distance_from_attacking >= self.reach and
|
||||||
|
--don't allow explosion to cancel unless out of the reach boundary
|
||||||
|
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) then
|
||||||
|
|
||||||
|
mobs.set_velocity(self, self.run_velocity)
|
||||||
|
mobs.set_mob_animation(self,"run")
|
||||||
|
|
||||||
|
mobs.reverse_explosion_animation(self,dtime)
|
||||||
|
else
|
||||||
|
mobs.set_velocity(self,0)
|
||||||
|
|
||||||
|
--this is the only way I can reference this without dumping extra data on all mobs
|
||||||
|
if not self.explosion_animation then
|
||||||
|
self.explosion_animation = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--play ignite sound
|
||||||
|
if self.explosion_animation == 0 then
|
||||||
|
mobs.play_sound(self,"attack")
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_mob_animation(self,"stand")
|
||||||
|
|
||||||
|
mobs.handle_explosion_animation(self)
|
||||||
|
|
||||||
|
self.explosion_animation = self.explosion_animation + (dtime/2.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
--make explosive mobs jump
|
||||||
|
--check for nodes to jump over
|
||||||
|
--explosive mobs will just ride against walls for now
|
||||||
|
local node_in_front_of = mobs.jump_check(self)
|
||||||
|
if node_in_front_of == 1 then
|
||||||
|
mobs.jump(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--do biggening explosion thing
|
||||||
|
if self.explosion_animation and self.explosion_animation > self.explosion_timer then
|
||||||
|
mcl_explosions.explode(self.object:get_pos(), self.explosion_strength,{ drop_chance = 1.0 })
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--this is a small helper function to make working with explosion animations easier
|
||||||
|
mobs.reverse_explosion_animation = function(self,dtime)
|
||||||
|
--if explosion animation was greater than 0 then reverse it
|
||||||
|
if self.explosion_animation and self.explosion_animation > 0 then
|
||||||
|
self.explosion_animation = self.explosion_animation - dtime
|
||||||
|
if self.explosion_animation < 0 then
|
||||||
|
self.explosion_animation = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.handle_explosion_animation(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
______ _
|
||||||
|
| ___ \ | |
|
||||||
|
| |_/ / _ _ __ ___| |__
|
||||||
|
| __/ | | | '_ \ / __| '_ \
|
||||||
|
| | | |_| | | | | (__| | | |
|
||||||
|
\_| \__,_|_| |_|\___|_| |_|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mobs.punch_attack_walk = function(self,dtime)
|
||||||
|
--this needs an exception
|
||||||
|
if self.attacking == nil or not self.attacking:is_player() then
|
||||||
|
self.attacking = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local distance_from_attacking = mobs.get_2d_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||||
|
|
||||||
|
if distance_from_attacking >= self.minimum_follow_distance then
|
||||||
|
mobs.set_velocity(self, self.run_velocity)
|
||||||
|
mobs.set_mob_animation(self, "run")
|
||||||
|
else
|
||||||
|
mobs.set_velocity(self, 0)
|
||||||
|
mobs.set_mob_animation(self, "stand")
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
|
||||||
|
--make punchy mobs jump
|
||||||
|
--check for nodes to jump over
|
||||||
|
--explosive mobs will just ride against walls for now
|
||||||
|
local node_in_front_of = mobs.jump_check(self)
|
||||||
|
|
||||||
|
if node_in_front_of == 1 then
|
||||||
|
mobs.jump(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--mobs that can climb over stuff
|
||||||
|
if self.always_climb and node_in_front_of > 0 then
|
||||||
|
mobs.climb(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--auto reset punch_timer
|
||||||
|
if not self.punch_timer then
|
||||||
|
self.punch_timer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.punch_timer > 0 then
|
||||||
|
self.punch_timer = self.punch_timer - dtime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.punch_attack = function(self)
|
||||||
|
|
||||||
|
self.attacking:punch(self.object, 1.0, {
|
||||||
|
full_punch_interval = 1.0,
|
||||||
|
damage_groups = {fleshy = self.damage}
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
self.punch_timer = self.punch_timer_cooloff
|
||||||
|
|
||||||
|
|
||||||
|
--knockback
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
local pos2 = self.attacking:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
local dir = vector_direction(pos1,pos2)
|
||||||
|
|
||||||
|
dir = vector_multiply(dir,3)
|
||||||
|
|
||||||
|
if self.attacking:get_velocity().y <= 1 then
|
||||||
|
dir.y = 5
|
||||||
|
end
|
||||||
|
|
||||||
|
self.attacking:add_velocity(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
______ _ _ _ _
|
||||||
|
| ___ \ (_) | | (_) |
|
||||||
|
| |_/ / __ ___ _ ___ ___| |_ _| | ___
|
||||||
|
| __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
|
||||||
|
| | | | | (_) | | __/ (__| |_| | | __/
|
||||||
|
\_| |_| \___/| |\___|\___|\__|_|_|\___|
|
||||||
|
_/ |
|
||||||
|
|__/
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
mobs.projectile_attack_walk = function(self,dtime)
|
||||||
|
|
||||||
|
--this needs an exception
|
||||||
|
if self.attacking == nil or not self.attacking:is_player() then
|
||||||
|
self.attacking = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
|
||||||
|
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||||
|
|
||||||
|
|
||||||
|
if distance_from_attacking >= self.reach then
|
||||||
|
mobs.set_velocity(self, self.run_velocity)
|
||||||
|
mobs.set_mob_animation(self,"run")
|
||||||
|
else
|
||||||
|
mobs.set_velocity(self,0)
|
||||||
|
mobs.set_mob_animation(self,"stand")
|
||||||
|
end
|
||||||
|
|
||||||
|
--do this to not load data into other mobs
|
||||||
|
if not self.projectile_timer then
|
||||||
|
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
||||||
|
end
|
||||||
|
|
||||||
|
--run projectile timer
|
||||||
|
if self.projectile_timer > 0 then
|
||||||
|
self.projectile_timer = self.projectile_timer - dtime
|
||||||
|
|
||||||
|
--shoot
|
||||||
|
if self.projectile_timer <= 0 then
|
||||||
|
--reset timer
|
||||||
|
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
||||||
|
mobs.shoot_projectile(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--make shooty mobs jump
|
||||||
|
--check for nodes to jump over
|
||||||
|
--explosive mobs will just ride against walls for now
|
||||||
|
local node_in_front_of = mobs.jump_check(self)
|
||||||
|
if node_in_front_of == 1 then
|
||||||
|
mobs.jump(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
_ ______ _ _
|
||||||
|
| | | ___| | | |
|
||||||
|
| | | |_ | |_ _ | |
|
||||||
|
| | | _| | | | | | | |
|
||||||
|
|_| | | | | |_| | |_|
|
||||||
|
(_) \_| |_|\__, | (_)
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
______ _ _ _ _
|
||||||
|
| ___ \ (_) | | (_) |
|
||||||
|
| |_/ / __ ___ _ ___ ___| |_ _| | ___
|
||||||
|
| __/ '__/ _ \| |/ _ \/ __| __| | |/ _ \
|
||||||
|
| | | | | (_) | | __/ (__| |_| | | __/
|
||||||
|
\_| |_| \___/| |\___|\___|\__|_|_|\___|
|
||||||
|
_/ |
|
||||||
|
|__/
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local random_pitch_multiplier = {-1,1}
|
||||||
|
|
||||||
|
mobs.projectile_attack_fly = function(self, dtime)
|
||||||
|
--this needs an exception
|
||||||
|
if self.attacking == nil or not self.attacking:is_player() then
|
||||||
|
self.attacking = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is specifically for random ghast movement
|
||||||
|
if self.fly_random_while_attack then
|
||||||
|
|
||||||
|
--enable rotation locking
|
||||||
|
mobs.movement_rotation_lock(self)
|
||||||
|
|
||||||
|
self.walk_timer = self.walk_timer - dtime
|
||||||
|
|
||||||
|
--reset the walk timer
|
||||||
|
if self.walk_timer <= 0 then
|
||||||
|
--re-randomize the walk timer
|
||||||
|
self.walk_timer = math.random(1,6) + math.random()
|
||||||
|
--set the mob into a random direction
|
||||||
|
self.yaw = (math_random() * (math.pi * 2))
|
||||||
|
--create a truly random pitch, since there is no easy access to pitch math that I can find
|
||||||
|
self.pitch = math_random() * math.random(1,3) * random_pitch_multiplier[math_random(1,2)]
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_fly_velocity(self, self.run_velocity)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
|
||||||
|
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||||
|
|
||||||
|
if distance_from_attacking >= self.reach then
|
||||||
|
mobs.set_pitch_while_attacking(self)
|
||||||
|
mobs.set_fly_velocity(self, self.run_velocity)
|
||||||
|
mobs.set_mob_animation(self,"run")
|
||||||
|
else
|
||||||
|
mobs.set_pitch_while_attacking(self)
|
||||||
|
mobs.set_fly_velocity(self, 0)
|
||||||
|
mobs.set_mob_animation(self,"stand")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--do this to not load data into other mobs
|
||||||
|
if not self.projectile_timer then
|
||||||
|
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
||||||
|
end
|
||||||
|
|
||||||
|
--run projectile timer
|
||||||
|
if self.projectile_timer > 0 then
|
||||||
|
self.projectile_timer = self.projectile_timer - dtime
|
||||||
|
|
||||||
|
--shoot
|
||||||
|
if self.projectile_timer <= 0 then
|
||||||
|
|
||||||
|
if self.fly_random_while_attack then
|
||||||
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
self.walk_timer = 0
|
||||||
|
end
|
||||||
|
--reset timer
|
||||||
|
self.projectile_timer = math_random(self.projectile_cooldown_min, self.projectile_cooldown_max)
|
||||||
|
mobs.shoot_projectile(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,179 @@
|
||||||
|
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
--check to see if someone nearby has some tasty food
|
||||||
|
mobs.check_following = function(self) -- returns true or false
|
||||||
|
--ignore
|
||||||
|
if not self.follow then
|
||||||
|
self.following_person = nil
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--hey look, this thing works for passive mobs too!
|
||||||
|
local follower = mobs.detect_closest_player_within_radius(self,true,self.view_range,self.eye_height)
|
||||||
|
|
||||||
|
--check if the follower is a player incase they log out
|
||||||
|
if follower and follower:is_player() then
|
||||||
|
local stack = follower:get_wielded_item()
|
||||||
|
--safety check
|
||||||
|
if not stack then
|
||||||
|
self.following_person = nil
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local item_name = stack:get_name()
|
||||||
|
--all checks have passed, that guy has some good looking food
|
||||||
|
if item_name == self.follow then
|
||||||
|
self.following_person = follower
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--everything failed
|
||||||
|
self.following_person = nil
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--a function which attempts to make mobs enter
|
||||||
|
--the breeding state
|
||||||
|
mobs.enter_breed_state = function(self,clicker)
|
||||||
|
|
||||||
|
--do not breed if baby
|
||||||
|
if self.baby then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--do not do anything if looking for mate or
|
||||||
|
--if cooling off from breeding
|
||||||
|
if self.breed_lookout_timer > 0 or self.breed_timer > 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--if this is caught, that means something has gone
|
||||||
|
--seriously wrong
|
||||||
|
if not clicker or not clicker:is_player() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local stack = clicker:get_wielded_item()
|
||||||
|
--safety check
|
||||||
|
if not stack then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local item_name = stack:get_name()
|
||||||
|
--all checks have passed, that guy has some good looking food
|
||||||
|
if item_name == self.follow then
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
stack:take_item()
|
||||||
|
clicker:set_wielded_item(stack)
|
||||||
|
end
|
||||||
|
self.breed_lookout_timer = self.breed_lookout_timer_goal
|
||||||
|
self.bred = true
|
||||||
|
mobs.play_sound_specific(self,"mobs_mc_animal_eat_generic")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--everything failed
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--find the closest mate in the area
|
||||||
|
mobs.look_for_mate = function(self)
|
||||||
|
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = pos1.y + self.eye_height
|
||||||
|
|
||||||
|
local mates_in_area = {}
|
||||||
|
local winner_mate = nil
|
||||||
|
local mates_detected = 0
|
||||||
|
local radius = self.view_range
|
||||||
|
|
||||||
|
--get mates in radius
|
||||||
|
for _,mate in pairs(minetest_get_objects_inside_radius(pos1, radius)) do
|
||||||
|
|
||||||
|
--look for a breeding mate
|
||||||
|
if mate and mate:get_luaentity()
|
||||||
|
and mate:get_luaentity()._cmi_is_mob
|
||||||
|
and mate:get_luaentity().name == self.name
|
||||||
|
and mate:get_luaentity().breed_lookout_timer > 0
|
||||||
|
and mate:get_luaentity() ~= self then
|
||||||
|
|
||||||
|
local pos2 = mate:get_pos()
|
||||||
|
|
||||||
|
local distance = vector.distance(pos1,pos2)
|
||||||
|
|
||||||
|
if distance <= radius then
|
||||||
|
if minetest.line_of_sight then
|
||||||
|
--must add eye height or stuff breaks randomly because of
|
||||||
|
--seethrough nodes being a blocker (like grass)
|
||||||
|
if minetest.line_of_sight(
|
||||||
|
vector.new(pos1.x, pos1.y, pos1.z),
|
||||||
|
vector.new(pos2.x, pos2.y + mate:get_properties().eye_height, pos2.z)
|
||||||
|
) then
|
||||||
|
mates_detected = mates_detected + 1
|
||||||
|
mates_in_area[mate] = distance
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mates_detected = mates_detected + 1
|
||||||
|
mates_in_area[mate] = distance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--return if there's no one near by
|
||||||
|
if mates_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--do a default radius max
|
||||||
|
local shortest_distance = radius + 1
|
||||||
|
|
||||||
|
--sort through mates and find the closest mate
|
||||||
|
for mate,distance in pairs(mates_in_area) do
|
||||||
|
if distance < shortest_distance then
|
||||||
|
shortest_distance = distance
|
||||||
|
winner_mate = mate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return winner_mate
|
||||||
|
end
|
||||||
|
|
||||||
|
--make the baby grow up
|
||||||
|
mobs.baby_grow_up = function(self)
|
||||||
|
self.baby = nil
|
||||||
|
self.visual_size = self.backup_visual_size
|
||||||
|
self.collisionbox = self.backup_collisionbox
|
||||||
|
self.selectionbox = self.backup_selectionbox
|
||||||
|
self.object:set_properties(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--makes the baby grow up faster with diminishing returns
|
||||||
|
mobs.make_baby_grow_faster = function(self,clicker)
|
||||||
|
if clicker and clicker:is_player() then
|
||||||
|
local stack = clicker:get_wielded_item()
|
||||||
|
--safety check
|
||||||
|
if not stack then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local item_name = stack:get_name()
|
||||||
|
--all checks have passed, that guy has some good looking food
|
||||||
|
if item_name == self.follow then
|
||||||
|
self.grow_up_timer = self.grow_up_timer - (self.grow_up_timer * 0.10) --take 10 percent off - diminishing returns
|
||||||
|
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
stack:take_item()
|
||||||
|
clicker:set_wielded_item(stack)
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.play_sound_specific(self,"mobs_mc_animal_eat_generic")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
|
@ -0,0 +1,135 @@
|
||||||
|
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
|
||||||
|
local math_random = math.random
|
||||||
|
local vector_multiply = vector.multiply
|
||||||
|
|
||||||
|
local vector_direction = vector.direction
|
||||||
|
|
||||||
|
local integer_test = {-1,1}
|
||||||
|
|
||||||
|
mobs.collision = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
if not self or not self.object or not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--do collision detection from the base of the mob
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
pos.y = pos.y + collisionbox[2]
|
||||||
|
|
||||||
|
local collision_boundary = collisionbox[4]
|
||||||
|
|
||||||
|
local radius = collision_boundary
|
||||||
|
|
||||||
|
if collisionbox[5] > collision_boundary then
|
||||||
|
radius = collisionbox[5]
|
||||||
|
end
|
||||||
|
|
||||||
|
local collision_count = 0
|
||||||
|
|
||||||
|
|
||||||
|
local check_for_attack = false
|
||||||
|
|
||||||
|
if self.attack_type == "punch" and self.hostile and self.attacking then
|
||||||
|
check_for_attack = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,object in ipairs(minetest_get_objects_inside_radius(pos, radius*1.25)) do
|
||||||
|
if object and object ~= self.object and (object:is_player() or (object:get_luaentity() and object:get_luaentity()._cmi_is_mob == true and object:get_luaentity().health > 0)) and
|
||||||
|
--don't collide with rider, rider don't collide with thing
|
||||||
|
(not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object)) and
|
||||||
|
(not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then
|
||||||
|
--stop infinite loop
|
||||||
|
collision_count = collision_count + 1
|
||||||
|
--mob cramming
|
||||||
|
if collision_count > 30 then
|
||||||
|
self.health = -20
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos2 = object:get_pos()
|
||||||
|
|
||||||
|
local object_collisionbox = object:get_properties().collisionbox
|
||||||
|
|
||||||
|
pos2.y = pos2.y + object_collisionbox[2]
|
||||||
|
|
||||||
|
local object_collision_boundary = object_collisionbox[4]
|
||||||
|
|
||||||
|
|
||||||
|
--this is checking the difference of the object collided with's possision
|
||||||
|
--if positive top of other object is inside (y axis) of current object
|
||||||
|
local y_base_diff = (pos2.y + object_collisionbox[5]) - pos.y
|
||||||
|
|
||||||
|
local y_top_diff = (pos.y + collisionbox[5]) - pos2.y
|
||||||
|
|
||||||
|
|
||||||
|
local distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z))
|
||||||
|
|
||||||
|
if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
|
||||||
|
|
||||||
|
local dir = vector.direction(pos,pos2)
|
||||||
|
|
||||||
|
dir.y = 0
|
||||||
|
|
||||||
|
--eliminate mob being stuck in corners
|
||||||
|
if dir.x == 0 and dir.z == 0 then
|
||||||
|
--slightly adjust mob position to prevent equal length
|
||||||
|
--corner/wall sticking
|
||||||
|
dir.x = dir.x + ((math_random()/10)*integer_test[math.random(1,2)])
|
||||||
|
dir.z = dir.z + ((math_random()/10)*integer_test[math.random(1,2)])
|
||||||
|
end
|
||||||
|
|
||||||
|
local velocity = dir
|
||||||
|
|
||||||
|
--0.5 is the max force multiplier
|
||||||
|
local force = 0.5 - (0.5 * distance / (collision_boundary + object_collision_boundary))
|
||||||
|
|
||||||
|
local vel1 = vector.multiply(velocity, -1.5)
|
||||||
|
local vel2 = vector.multiply(velocity, 1.5)
|
||||||
|
|
||||||
|
vel1 = vector.multiply(vel1, force * 10)
|
||||||
|
vel2 = vector.multiply(vel2, force)
|
||||||
|
|
||||||
|
if object:is_player() then
|
||||||
|
vel2 = vector_multiply(vel2, 2.5)
|
||||||
|
|
||||||
|
--integrate mob punching into collision detection
|
||||||
|
if check_for_attack and self.punch_timer <= 0 then
|
||||||
|
if object == self.attacking then
|
||||||
|
mobs.punch_attack(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.object:add_velocity(vel1)
|
||||||
|
object:add_velocity(vel2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--this is used for arrow collisions
|
||||||
|
mobs.arrow_hit = function(self, player)
|
||||||
|
player:punch(self.object, 1.0, {
|
||||||
|
full_punch_interval = 1.0,
|
||||||
|
damage_groups = {fleshy = self._damage}
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
|
||||||
|
--knockback
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
local pos2 = player:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
local dir = vector_direction(pos1,pos2)
|
||||||
|
|
||||||
|
dir = vector_multiply(dir,3)
|
||||||
|
|
||||||
|
if player:get_velocity().y <= 1 then
|
||||||
|
dir.y = 5
|
||||||
|
end
|
||||||
|
|
||||||
|
player:add_velocity(dir)
|
||||||
|
end
|
|
@ -0,0 +1,161 @@
|
||||||
|
local minetest_add_item = minetest.add_item
|
||||||
|
--local minetest_sound_play = minetest.sound_play
|
||||||
|
|
||||||
|
local math_pi = math.pi
|
||||||
|
local math_random = math.random
|
||||||
|
local math_floor = math.floor
|
||||||
|
local HALF_PI = math_pi / 2
|
||||||
|
|
||||||
|
local vector_new = vector.new
|
||||||
|
|
||||||
|
|
||||||
|
-- drop items
|
||||||
|
local item_drop = function(self, cooked, looting_level)
|
||||||
|
|
||||||
|
looting_level = looting_level or 0
|
||||||
|
|
||||||
|
-- no drops for child mobs (except monster)
|
||||||
|
if (self.child and self.type ~= "monster") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj, item
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
self.drops = self.drops or {} -- nil check
|
||||||
|
|
||||||
|
for n = 1, #self.drops do
|
||||||
|
local dropdef = self.drops[n]
|
||||||
|
local chance = 1 / dropdef.chance
|
||||||
|
local looting_type = dropdef.looting
|
||||||
|
|
||||||
|
if looting_level > 0 then
|
||||||
|
local chance_function = dropdef.looting_chance_function
|
||||||
|
if chance_function then
|
||||||
|
chance = chance_function(looting_level)
|
||||||
|
elseif looting_type == "rare" then
|
||||||
|
chance = chance + (dropdef.looting_factor or 0.01) * looting_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local num = 0
|
||||||
|
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
||||||
|
if math_random() < chance then
|
||||||
|
num = math_random(dropdef.min or 1, dropdef.max or 1)
|
||||||
|
elseif not dropdef.looting_ignore_chance then
|
||||||
|
do_common_looting = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if do_common_looting then
|
||||||
|
num = num + math_floor(math_random(0, looting_level) + 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
if num > 0 then
|
||||||
|
item = dropdef.name
|
||||||
|
|
||||||
|
-- cook items when true
|
||||||
|
if cooked then
|
||||||
|
|
||||||
|
local output = minetest.get_craft_result({
|
||||||
|
method = "cooking",
|
||||||
|
width = 1,
|
||||||
|
items = {item},
|
||||||
|
})
|
||||||
|
|
||||||
|
if output and output.item and not output.item:is_empty() then
|
||||||
|
item = output.item:get_name()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add item if it exists
|
||||||
|
for x = 1, num do
|
||||||
|
obj = minetest_add_item(pos, ItemStack(item .. " " .. 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj and obj:get_luaentity() then
|
||||||
|
|
||||||
|
obj:set_velocity({
|
||||||
|
x = math_random(-10, 10) / 9,
|
||||||
|
y = 6,
|
||||||
|
z = math_random(-10, 10) / 9,
|
||||||
|
})
|
||||||
|
elseif obj then
|
||||||
|
obj:remove() -- item does not exist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.drops = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
mobs.death_logic = function(self, dtime)
|
||||||
|
|
||||||
|
--stop crashing game when object is nil
|
||||||
|
if not self or not self.object or not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.death_animation_timer = self.death_animation_timer + dtime
|
||||||
|
|
||||||
|
--get all attached entities and sort through them
|
||||||
|
local attached_entities = self.object:get_children()
|
||||||
|
if #attached_entities > 0 then
|
||||||
|
for _,entity in pairs(attached_entities) do
|
||||||
|
--kick the player off
|
||||||
|
if entity:is_player() then
|
||||||
|
mobs.detach(entity)
|
||||||
|
--kick mobs off
|
||||||
|
--if there is scaling issues, this needs an additional check
|
||||||
|
else
|
||||||
|
entity:set_detach()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--stop mob from getting in the way of other mobs you're fighting
|
||||||
|
if self.object:get_properties().pointable then
|
||||||
|
self.object:set_properties({pointable = false})
|
||||||
|
end
|
||||||
|
|
||||||
|
--the final POOF of a mob despawning
|
||||||
|
if self.death_animation_timer >= 1.25 then
|
||||||
|
item_drop(self,false,1)
|
||||||
|
mobs.death_effect(self)
|
||||||
|
mcl_experience.throw_xp(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
|
||||||
|
if self.on_die then
|
||||||
|
self.on_die(self, self.object:get_pos())
|
||||||
|
end
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--I'm sure there's a more efficient way to do this
|
||||||
|
--but this is the easiest, easier to work with 1 variable synced
|
||||||
|
--this is also not smooth
|
||||||
|
local death_animation_roll = self.death_animation_timer * 2 -- * 2 to make it faster
|
||||||
|
if death_animation_roll > 1 then
|
||||||
|
death_animation_roll = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local rot = self.object:get_rotation() --(no pun intended)
|
||||||
|
|
||||||
|
rot.z = death_animation_roll * HALF_PI
|
||||||
|
|
||||||
|
self.object:set_rotation(rot)
|
||||||
|
|
||||||
|
mobs.set_mob_animation(self,"stand", true)
|
||||||
|
|
||||||
|
|
||||||
|
--flying and swimming mobs just fall down
|
||||||
|
if self.fly or self.swim then
|
||||||
|
if self.object:get_acceleration().y ~= -self.gravity then
|
||||||
|
self.object:set_acceleration(vector_new(0,-self.gravity,0))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--when landing allow mob to slow down and just fall if in air
|
||||||
|
if self.pause_timer <= 0 then
|
||||||
|
mobs.set_velocity(self,0)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,250 @@
|
||||||
|
local minetest_line_of_sight = minetest.line_of_sight
|
||||||
|
--local minetest_dir_to_yaw = minetest.dir_to_yaw
|
||||||
|
local minetest_yaw_to_dir = minetest.yaw_to_dir
|
||||||
|
local minetest_get_node = minetest.get_node
|
||||||
|
local minetest_get_item_group = minetest.get_item_group
|
||||||
|
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
|
local minetest_get_node_or_nil = minetest.get_node_or_nil
|
||||||
|
local minetest_registered_nodes = minetest.registered_nodes
|
||||||
|
local minetest_get_connected_players = minetest.get_connected_players
|
||||||
|
|
||||||
|
local vector_new = vector.new
|
||||||
|
local vector_add = vector.add
|
||||||
|
local vector_multiply = vector.multiply
|
||||||
|
local vector_distance = vector.distance
|
||||||
|
|
||||||
|
local table_copy = table.copy
|
||||||
|
|
||||||
|
local math_abs = math.abs
|
||||||
|
|
||||||
|
-- default function when mobs are blown up with TNT
|
||||||
|
--[[local function do_tnt(obj, damage)
|
||||||
|
obj.object:punch(obj.object, 1.0, {
|
||||||
|
full_punch_interval = 1.0,
|
||||||
|
damage_groups = {fleshy = damage},
|
||||||
|
}, nil)
|
||||||
|
return false, true, {}
|
||||||
|
end]]
|
||||||
|
|
||||||
|
--a fast function to be able to detect only players without using objects_in_radius
|
||||||
|
mobs.detect_closest_player_within_radius = function(self, line_of_sight, radius, object_height_adder)
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
local players_in_area = {}
|
||||||
|
local winner_player = nil
|
||||||
|
local players_detected = 0
|
||||||
|
|
||||||
|
--get players in radius
|
||||||
|
for _,player in pairs(minetest.get_connected_players()) do
|
||||||
|
if player and player:get_hp() > 0 then
|
||||||
|
|
||||||
|
local pos2 = player:get_pos()
|
||||||
|
|
||||||
|
local distance = vector_distance(pos1,pos2)
|
||||||
|
|
||||||
|
if distance <= radius then
|
||||||
|
if line_of_sight then
|
||||||
|
--must add eye height or stuff breaks randomly because of
|
||||||
|
--seethrough nodes being a blocker (like grass)
|
||||||
|
if minetest_line_of_sight(
|
||||||
|
vector_new(pos1.x, pos1.y + object_height_adder, pos1.z),
|
||||||
|
vector_new(pos2.x, pos2.y + player:get_properties().eye_height, pos2.z)
|
||||||
|
) then
|
||||||
|
players_detected = players_detected + 1
|
||||||
|
players_in_area[player] = distance
|
||||||
|
end
|
||||||
|
else
|
||||||
|
players_detected = players_detected + 1
|
||||||
|
players_in_area[player] = distance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--return if there's no one near by
|
||||||
|
if players_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--do a default radius max
|
||||||
|
local shortest_distance = radius + 1
|
||||||
|
|
||||||
|
--sort through players and find the closest player
|
||||||
|
for player,distance in pairs(players_in_area) do
|
||||||
|
if distance < shortest_distance then
|
||||||
|
shortest_distance = distance
|
||||||
|
winner_player = player
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return winner_player
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--check if a mob needs to jump
|
||||||
|
mobs.jump_check = function(self,dtime)
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
pos.y = pos.y + 0.1
|
||||||
|
local dir = minetest_yaw_to_dir(self.yaw)
|
||||||
|
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
local radius = collisionbox[4] + 0.5
|
||||||
|
|
||||||
|
vector_multiply(dir, radius)
|
||||||
|
|
||||||
|
--only jump if there's a node and a non-solid node above it
|
||||||
|
local test_dir = vector_add(pos,dir)
|
||||||
|
|
||||||
|
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
|
||||||
|
|
||||||
|
test_dir.y = test_dir.y + 1
|
||||||
|
|
||||||
|
local green_flag_2 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") == 0
|
||||||
|
|
||||||
|
if green_flag_1 and green_flag_2 then
|
||||||
|
--can jump over node
|
||||||
|
return 1
|
||||||
|
elseif green_flag_1 and not green_flag_2 then
|
||||||
|
--wall in front of mob
|
||||||
|
return 2
|
||||||
|
end
|
||||||
|
--nothing to jump over
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- a helper function to quickly turn neutral passive mobs hostile
|
||||||
|
local turn_hostile = function(self,detected_mob)
|
||||||
|
--drop in variables for attacking (stops crash)
|
||||||
|
detected_mob.punch_timer = 0
|
||||||
|
--set to hostile
|
||||||
|
detected_mob.hostile = true
|
||||||
|
--hostile_cooldown timer is initialized here
|
||||||
|
detected_mob.hostile_cooldown_timer = detected_mob.hostile_cooldown
|
||||||
|
--set target to the same
|
||||||
|
detected_mob.attacking = self.attacking
|
||||||
|
end
|
||||||
|
|
||||||
|
--allow hostile mobs to signal to other mobs
|
||||||
|
--to switch from neutal passive to neutral hostile
|
||||||
|
mobs.group_attack_initialization = function(self)
|
||||||
|
|
||||||
|
--get basic data
|
||||||
|
local friends_list
|
||||||
|
|
||||||
|
if self.group_attack == true then
|
||||||
|
friends_list = {self.name}
|
||||||
|
else
|
||||||
|
friends_list = table_copy(self.group_attack)
|
||||||
|
end
|
||||||
|
|
||||||
|
local objects_in_area = minetest_get_objects_inside_radius(self.object:get_pos(), self.view_range)
|
||||||
|
|
||||||
|
--get the player's name
|
||||||
|
local name = self.attacking:get_player_name()
|
||||||
|
|
||||||
|
--re-use local variable
|
||||||
|
local detected_mob
|
||||||
|
|
||||||
|
--run through mobs in viewing distance
|
||||||
|
for _,object in pairs(objects_in_area) do
|
||||||
|
if object and object:get_luaentity() then
|
||||||
|
detected_mob = object:get_luaentity()
|
||||||
|
-- only alert members of same mob or friends
|
||||||
|
if detected_mob._cmi_is_mob and detected_mob.state ~= "attack" and detected_mob.owner ~= name then
|
||||||
|
if detected_mob.name == self.name then
|
||||||
|
turn_hostile(self,detected_mob)
|
||||||
|
else
|
||||||
|
for _,id in pairs(friends_list) do
|
||||||
|
if detected_mob.name == id then
|
||||||
|
turn_hostile(self,detected_mob)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--THIS NEEDS TO BE RE-IMPLEMENTED AS A GLOBAL HIT IN MOB_PUNCH!!
|
||||||
|
-- have owned mobs attack player threat
|
||||||
|
--if obj.owner == name and obj.owner_loyal then
|
||||||
|
-- do_attack(obj, self.object)
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if within physical map limits (-30911 to 30927)
|
||||||
|
-- within_limits, wmin, wmax = nil, -30913, 30928
|
||||||
|
mobs.within_limits = function(pos, radius)
|
||||||
|
local wmin, wmax
|
||||||
|
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
|
||||||
|
return pos
|
||||||
|
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
|
||||||
|
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
|
||||||
|
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get node but use fallback for nil or unknown
|
||||||
|
mobs.node_ok = function(pos, fallback)
|
||||||
|
|
||||||
|
fallback = fallback or 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
|
||||||
|
|
||||||
|
|
||||||
|
--a teleport functoin
|
||||||
|
mobs.teleport = function(self, target)
|
||||||
|
if self.do_teleport then
|
||||||
|
if self.do_teleport(self, target) == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--a function used for despawning mobs
|
||||||
|
mobs.check_for_player_within_area = function(self, radius)
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
--get players in radius
|
||||||
|
for _,player in pairs(minetest_get_connected_players()) do
|
||||||
|
if player and player:get_hp() > 0 then
|
||||||
|
local pos2 = player:get_pos()
|
||||||
|
local distance = vector_distance(pos1,pos2)
|
||||||
|
if distance < radius then
|
||||||
|
--found a player
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--did not find a player
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--a simple helper function for mobs following
|
||||||
|
mobs.get_2d_distance = function(pos1,pos2)
|
||||||
|
pos1.y = 0
|
||||||
|
pos2.y = 0
|
||||||
|
return vector_distance(pos1, pos2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- fall damage onto solid ground
|
||||||
|
mobs.calculate_fall_damage = function(self)
|
||||||
|
if self.old_velocity and self.old_velocity.y < -7 and self.object:get_velocity().y == 0 then
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
if vel then
|
||||||
|
local damage = math_abs(self.old_velocity.y + 7) * 2
|
||||||
|
self.pause_timer = 0.4
|
||||||
|
self.health = self.health - damage
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,78 @@
|
||||||
|
--this is from https://github.com/HybridDog/builtin_item/blob/e6dfd9dce86503b3cbd1474257eca5f6f6ca71c2/init.lua#L50
|
||||||
|
local
|
||||||
|
minetest,vector,math,pairs,minetest_get_node,vector_subtract,minetest_registered_nodes
|
||||||
|
=
|
||||||
|
minetest,vector,math,pairs,minetest.get_node,vector.subtract,minetest.registered_nodes
|
||||||
|
|
||||||
|
local tab
|
||||||
|
local n
|
||||||
|
local function get_nodes(pos)
|
||||||
|
tab,n = {},1
|
||||||
|
for i = -1,1,2 do
|
||||||
|
for _,p in pairs({
|
||||||
|
{x=pos.x+i, y=pos.y, z=pos.z},
|
||||||
|
{x=pos.x, y=pos.y, z=pos.z+i}
|
||||||
|
}) do
|
||||||
|
tab[n] = {p, minetest_get_node(p)}
|
||||||
|
n = n+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local data
|
||||||
|
local param2
|
||||||
|
local nd
|
||||||
|
local par2
|
||||||
|
local name
|
||||||
|
local tmp
|
||||||
|
local c_node
|
||||||
|
function mobs.get_flowing_dir(pos)
|
||||||
|
c_node = minetest_get_node(pos).name
|
||||||
|
if c_node ~= "mcl_core:water_flowing" and c_node ~= "mcl_core:water" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
data = get_nodes(pos)
|
||||||
|
param2 = minetest_get_node(pos).param2
|
||||||
|
if param2 > 7 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if c_node == "mcl_core:water" then
|
||||||
|
for _,i in pairs(data) do
|
||||||
|
nd = i[2]
|
||||||
|
name = nd.name
|
||||||
|
par2 = nd.param2
|
||||||
|
if name == "mcl_core:water_flowing" and par2 == 7 then
|
||||||
|
return(vector_subtract(i[1],pos))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _,i in pairs(data) do
|
||||||
|
nd = i[2]
|
||||||
|
name = nd.name
|
||||||
|
par2 = nd.param2
|
||||||
|
if name == "mcl_core:water_flowing" and par2 < param2 then
|
||||||
|
return(vector_subtract(i[1],pos))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _,i in pairs(data) do
|
||||||
|
nd = i[2]
|
||||||
|
name = nd.name
|
||||||
|
par2 = nd.param2
|
||||||
|
if name == "mcl_core:water_flowing" and par2 >= 11 then
|
||||||
|
return(vector_subtract(i[1],pos))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _,i in pairs(data) do
|
||||||
|
nd = i[2]
|
||||||
|
name = nd.name
|
||||||
|
par2 = nd.param2
|
||||||
|
tmp = minetest_registered_nodes[name]
|
||||||
|
if tmp and not tmp.walkable and name ~= "mcl_core:water_flowing" and name ~= "mcl_core:water" then
|
||||||
|
return(vector_subtract(i[1],pos))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
|
@ -0,0 +1,98 @@
|
||||||
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
--converts yaw to degrees
|
||||||
|
local degrees = function(yaw)
|
||||||
|
return yaw*180.0/math.pi
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.do_head_logic = function(self,dtime)
|
||||||
|
|
||||||
|
local player = minetest.get_player_by_name("singleplayer")
|
||||||
|
|
||||||
|
local look_at = player:get_pos()
|
||||||
|
look_at.y = look_at.y + player:get_properties().eye_height
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
|
||||||
|
local body_yaw = self.object:get_yaw()
|
||||||
|
|
||||||
|
local body_dir = minetest.yaw_to_dir(body_yaw)
|
||||||
|
|
||||||
|
pos.y = pos.y + self.head_height_offset
|
||||||
|
|
||||||
|
local head_offset = vector.multiply(body_dir, self.head_direction_offset)
|
||||||
|
|
||||||
|
pos = vector.add(pos, head_offset)
|
||||||
|
|
||||||
|
minetest.add_particle({
|
||||||
|
pos = pos,
|
||||||
|
velocity = {x=0, y=0, z=0},
|
||||||
|
acceleration = {x=0, y=0, z=0},
|
||||||
|
expirationtime = 0.2,
|
||||||
|
size = 1,
|
||||||
|
texture = "default_dirt.png",
|
||||||
|
})
|
||||||
|
|
||||||
|
local bone_pos = vector.new(0,0,0)
|
||||||
|
|
||||||
|
--(horizontal)
|
||||||
|
bone_pos.y = self.head_bone_pos_y
|
||||||
|
|
||||||
|
--(vertical)
|
||||||
|
bone_pos.z = self.head_bone_pos_z
|
||||||
|
|
||||||
|
--print(yaw)
|
||||||
|
|
||||||
|
--local _, bone_rot = self.object:get_bone_position("head")
|
||||||
|
|
||||||
|
--bone_rot.x = bone_rot.x + (dtime * 10)
|
||||||
|
--bone_rot.z = bone_rot.z + (dtime * 10)
|
||||||
|
|
||||||
|
local head_yaw = minetest.dir_to_yaw(vector.direction(pos,look_at)) - body_yaw
|
||||||
|
|
||||||
|
if self.reverse_head_yaw then
|
||||||
|
head_yaw = head_yaw * -1
|
||||||
|
end
|
||||||
|
|
||||||
|
--over rotation protection
|
||||||
|
--stops radians from going out of spec
|
||||||
|
if head_yaw > math.pi then
|
||||||
|
head_yaw = head_yaw - (math.pi * 2)
|
||||||
|
elseif head_yaw < -math.pi then
|
||||||
|
head_yaw = head_yaw + (math.pi * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local check_failed = false
|
||||||
|
--upper check + 90 degrees or upper math.radians (3.14/2)
|
||||||
|
if head_yaw > math.pi - (math.pi/2) then
|
||||||
|
head_yaw = 0
|
||||||
|
check_failed = true
|
||||||
|
--lower check - 90 degrees or lower negative math.radians (-3.14/2)
|
||||||
|
elseif head_yaw < -math.pi + (math.pi/2) then
|
||||||
|
head_yaw = 0
|
||||||
|
check_failed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local head_pitch = 0
|
||||||
|
|
||||||
|
--DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
|
||||||
|
--head_yaw = 0
|
||||||
|
--DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
|
||||||
|
|
||||||
|
if not check_failed then
|
||||||
|
head_pitch = minetest.dir_to_yaw(vector.new(vector.distance(vector.new(pos.x,0,pos.z),vector.new(look_at.x,0,look_at.z)),0,pos.y-look_at.y))+(math.pi/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.head_pitch_modifier then
|
||||||
|
head_pitch = head_pitch + self.head_pitch_modifier
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.swap_y_with_x then
|
||||||
|
self.object:set_bone_position(self.head_bone, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
||||||
|
else
|
||||||
|
self.object:set_bone_position(self.head_bone, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
||||||
|
end
|
||||||
|
--set_bone_position([bone, position, rotation])
|
||||||
|
end
|
|
@ -0,0 +1,276 @@
|
||||||
|
local minetest_after = minetest.after
|
||||||
|
local minetest_sound_play = minetest.sound_play
|
||||||
|
local minetest_dir_to_yaw = minetest.dir_to_yaw
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local MAX_MOB_NAME_LENGTH = 30
|
||||||
|
|
||||||
|
local mod_hunger = minetest.get_modpath("mcl_hunger")
|
||||||
|
|
||||||
|
mobs.feed_tame = function(self)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Code to execute before custom on_rightclick handling
|
||||||
|
local function on_rightclick_prefix(self, clicker)
|
||||||
|
local item = clicker:get_wielded_item()
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
mobs.update_tag(self)
|
||||||
|
|
||||||
|
if not mobs.is_creative(clicker:get_player_name()) then
|
||||||
|
item:take_item()
|
||||||
|
clicker:set_wielded_item(item)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- I have no idea what this does
|
||||||
|
mobs.create_mob_on_rightclick = function(on_rightclick)
|
||||||
|
return function(self, clicker)
|
||||||
|
--don't allow rightclicking dead mobs
|
||||||
|
if self.health <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local stop = on_rightclick_prefix(self, clicker)
|
||||||
|
if (not stop) and (on_rightclick) then
|
||||||
|
on_rightclick(self, clicker)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- deal damage and effects when mob punched
|
||||||
|
mobs.mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
||||||
|
--don't do anything if the mob is already dead
|
||||||
|
if self.health <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--neutral passive mobs switch to neutral hostile
|
||||||
|
if self.neutral then
|
||||||
|
--drop in variables for attacking (stops crash)
|
||||||
|
self.attacking = hitter
|
||||||
|
self.punch_timer = 0
|
||||||
|
self.hostile = true
|
||||||
|
--hostile_cooldown timer is initialized here
|
||||||
|
self.hostile_cooldown_timer = self.hostile_cooldown
|
||||||
|
|
||||||
|
--initialize the group attack (check for other mobs in area, make them neutral hostile)
|
||||||
|
if self.group_attack then
|
||||||
|
mobs.group_attack_initialization(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--turn skittish mobs away and RUN
|
||||||
|
if self.skittish then
|
||||||
|
|
||||||
|
self.state = "run"
|
||||||
|
|
||||||
|
self.run_timer = 5 --arbitrary 5 seconds
|
||||||
|
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
local pos2 = hitter:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
|
||||||
|
|
||||||
|
local dir = vector.direction(pos2,pos1)
|
||||||
|
|
||||||
|
local yaw = minetest_dir_to_yaw(dir)
|
||||||
|
|
||||||
|
self.yaw = yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
-- custom punch function
|
||||||
|
if self.do_punch then
|
||||||
|
-- when false skip going any further
|
||||||
|
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--don't do damage until pause timer resets
|
||||||
|
if self.pause_timer > 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- error checking when mod profiling is enabled
|
||||||
|
if not tool_capabilities then
|
||||||
|
minetest.log("warning", "[mobs_mc] Mod profiling enabled, damage not enabled")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_player = hitter:is_player()
|
||||||
|
|
||||||
|
-- punch interval
|
||||||
|
local weapon = hitter:get_wielded_item()
|
||||||
|
|
||||||
|
--local punch_interval = 1.4
|
||||||
|
|
||||||
|
-- exhaust attacker
|
||||||
|
if mod_hunger and is_player then
|
||||||
|
mcl_hunger.exhaust(hitter:get_player_name(), mcl_hunger.EXHAUST_ATTACK)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- calculate mob damage
|
||||||
|
local damage = 0
|
||||||
|
local armor = self.object:get_armor_groups() or {}
|
||||||
|
|
||||||
|
--calculate damage groups
|
||||||
|
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
||||||
|
damage = damage + (tool_capabilities.damage_groups[group] or 0) * ((armor[group] or 0) / 100.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if weapon then
|
||||||
|
local fire_aspect_level = mcl_enchanting.get_enchantment(weapon, "fire_aspect")
|
||||||
|
if fire_aspect_level > 0 then
|
||||||
|
mcl_burning.set_on_fire(self.object, fire_aspect_level * 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for tool immunity or special damage
|
||||||
|
for n = 1, #self.immune_to do
|
||||||
|
if self.immune_to[n][1] == weapon:get_name() then
|
||||||
|
damage = self.immune_to[n][2] or 0
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- healing
|
||||||
|
if damage <= -1 then
|
||||||
|
self.health = self.health - math.floor(damage)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--if tool_capabilities then
|
||||||
|
-- punch_interval = tool_capabilities.full_punch_interval or 1.4
|
||||||
|
--end
|
||||||
|
|
||||||
|
-- add weapon wear manually
|
||||||
|
-- Required because we have custom health handling ("health" property)
|
||||||
|
--minetest_is_creative_enabled("") ~= true --removed for now
|
||||||
|
if tool_capabilities then
|
||||||
|
if tool_capabilities.punch_attack_uses then
|
||||||
|
-- Without this delay, the wear does not work. Quite hacky ...
|
||||||
|
minetest_after(0, function(name)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
if not player then return end
|
||||||
|
local weapon = hitter:get_wielded_item(player)
|
||||||
|
local def = weapon:get_definition()
|
||||||
|
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
||||||
|
local wear = math.floor(65535/tool_capabilities.punch_attack_uses)
|
||||||
|
weapon:add_wear(wear)
|
||||||
|
hitter:set_wielded_item(weapon)
|
||||||
|
end
|
||||||
|
end, hitter:get_player_name())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--if player is falling multiply damage by 1.5
|
||||||
|
--critical hit
|
||||||
|
if hitter:get_velocity().y < 0 then
|
||||||
|
damage = damage * 1.5
|
||||||
|
mobs.critical_effect(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
||||||
|
if damage >= 0.1 then
|
||||||
|
|
||||||
|
minetest_sound_play("default_punch", {
|
||||||
|
object = self.object,
|
||||||
|
max_hear_distance = 16
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
-- do damage
|
||||||
|
self.health = self.health - damage
|
||||||
|
|
||||||
|
|
||||||
|
--0.4 seconds until you can hurt the mob again
|
||||||
|
self.pause_timer = 0.4
|
||||||
|
|
||||||
|
--don't do knockback from a rider
|
||||||
|
for _,obj in pairs(self.object:get_children()) do
|
||||||
|
if obj == hitter then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- knock back effect
|
||||||
|
local velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
--2d direction
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
pos1.y = 0
|
||||||
|
local pos2 = hitter:get_pos()
|
||||||
|
pos2.y = 0
|
||||||
|
|
||||||
|
local dir = vector.direction(pos2,pos1)
|
||||||
|
|
||||||
|
local up = 3
|
||||||
|
|
||||||
|
-- if already in air then dont go up anymore when hit
|
||||||
|
if velocity.y ~= 0 then
|
||||||
|
up = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--0.75 for perfect distance to not be too easy, and not be too hard
|
||||||
|
local multiplier = 0.75
|
||||||
|
|
||||||
|
-- check if tool already has specific knockback value
|
||||||
|
local knockback_enchant = mcl_enchanting.get_enchantment(hitter:get_wielded_item(), "knockback")
|
||||||
|
if knockback_enchant and knockback_enchant > 0 then
|
||||||
|
multiplier = knockback_enchant + 1 --(starts from 1, 1 would be no change)
|
||||||
|
end
|
||||||
|
|
||||||
|
--do this to sure you can punch a mob back when
|
||||||
|
--it's coming for you
|
||||||
|
if self.hostile then
|
||||||
|
multiplier = multiplier + 2
|
||||||
|
end
|
||||||
|
dir = vector.multiply(dir,multiplier)
|
||||||
|
dir.y = up
|
||||||
|
--add the velocity
|
||||||
|
self.object:add_velocity(dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--do internal per mob projectile calculations
|
||||||
|
mobs.shoot_projectile = function(self)
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
--add mob eye height
|
||||||
|
pos1.y = pos1.y + self.eye_height
|
||||||
|
|
||||||
|
local pos2 = self.attacking:get_pos()
|
||||||
|
--add player eye height
|
||||||
|
pos2.y = pos2.y + self.attacking:get_properties().eye_height
|
||||||
|
|
||||||
|
--get direction
|
||||||
|
local dir = vector.direction(pos1,pos2)
|
||||||
|
|
||||||
|
--call internal shoot_arrow function
|
||||||
|
self.shoot_arrow(self,pos1,dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.update_tag = function(self)
|
||||||
|
self.object:set_properties({
|
||||||
|
nametag = self.nametag,
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,150 @@
|
||||||
|
local minetest_add_particlespawner = minetest.add_particlespawner
|
||||||
|
|
||||||
|
mobs.death_effect = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local yaw = self.object:get_yaw()
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
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]}
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest_add_particlespawner({
|
||||||
|
amount = 50,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-0.5,0.5,-0.5),
|
||||||
|
maxvel = vector.new(0.5,1,0.5),
|
||||||
|
minexptime = 1.1,
|
||||||
|
maxexptime = 1.5,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_mob_death.png", -- this particle looks strange
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.critical_effect = function(self)
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local yaw = self.object:get_yaw()
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
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]}
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest_add_particlespawner({
|
||||||
|
amount = 10,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-1,1,-1),
|
||||||
|
maxvel = vector.new(1,3,1),
|
||||||
|
minexptime = 0.7,
|
||||||
|
maxexptime = 1,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "heart.png^[colorize:black:255",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--when feeding a mob
|
||||||
|
mobs.feed_effect = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local yaw = self.object:get_yaw()
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
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]}
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest_add_particlespawner({
|
||||||
|
amount = 10,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-1,1,-1),
|
||||||
|
maxvel = vector.new(1,3,1),
|
||||||
|
minexptime = 0.7,
|
||||||
|
maxexptime = 1,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "heart.png^[colorize:gray:255",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--hearts when tamed
|
||||||
|
mobs.tamed_effect = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local yaw = self.object:get_yaw()
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
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]}
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest_add_particlespawner({
|
||||||
|
amount = 30,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-1,1,-1),
|
||||||
|
maxvel = vector.new(1,3,1),
|
||||||
|
minexptime = 0.7,
|
||||||
|
maxexptime = 1,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "heart.png",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
--hearts when breeding
|
||||||
|
mobs.breeding_effect = function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local yaw = self.object:get_yaw()
|
||||||
|
local collisionbox = self.object:get_properties().collisionbox
|
||||||
|
|
||||||
|
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]}
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest_add_particlespawner({
|
||||||
|
amount = 2,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = vector.add(pos, min),
|
||||||
|
maxpos = vector.add(pos, max),
|
||||||
|
minvel = vector.new(-1,1,-1),
|
||||||
|
maxvel = vector.new(1,3,1),
|
||||||
|
minexptime = 0.7,
|
||||||
|
maxexptime = 1,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "heart.png",
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,390 @@
|
||||||
|
-- localize math functions
|
||||||
|
local math = math
|
||||||
|
local HALF_PI = math.pi / 2
|
||||||
|
local DOUBLE_PI = math.pi * 2
|
||||||
|
|
||||||
|
-- localize vector functions
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
local minetest_yaw_to_dir = minetest.yaw_to_dir
|
||||||
|
local minetest_dir_to_yaw = minetest.dir_to_yaw
|
||||||
|
|
||||||
|
local DEFAULT_JUMP_HEIGHT = 5
|
||||||
|
local DEFAULT_FLOAT_SPEED = 4
|
||||||
|
local DEFAULT_CLIMB_SPEED = 3
|
||||||
|
|
||||||
|
mobs.stick_in_cobweb = function(self)
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = vector.multiply(vector.normalize(current_velocity), 0.4)
|
||||||
|
|
||||||
|
goal_velocity.y = -0.5
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is a generic float function
|
||||||
|
mobs.float = function(self)
|
||||||
|
|
||||||
|
local acceleration = self.object:get_acceleration()
|
||||||
|
|
||||||
|
if not acceleration then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if acceleration.y ~= 0 then
|
||||||
|
self.object:set_acceleration({x=0, y=0, z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = 0,
|
||||||
|
y = DEFAULT_FLOAT_SPEED,
|
||||||
|
z = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity, current_velocity)
|
||||||
|
|
||||||
|
new_velocity_addition.x = 0
|
||||||
|
new_velocity_addition.z = 0
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--this is a generic climb function
|
||||||
|
mobs.climb = function(self)
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = 0,
|
||||||
|
y = DEFAULT_CLIMB_SPEED,
|
||||||
|
z = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
new_velocity_addition.x = 0
|
||||||
|
new_velocity_addition.z = 0
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
_ _
|
||||||
|
| | | |
|
||||||
|
| | __ _ _ __ __| |
|
||||||
|
| | / _` | '_ \ / _` |
|
||||||
|
| |___| (_| | | | | (_| |
|
||||||
|
\_____/\__,_|_| |_|\__,_|
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
-- move mob in facing direction
|
||||||
|
--this has been modified to be internal
|
||||||
|
--internal = lua (self.yaw)
|
||||||
|
--engine = c++ (self.object:get_yaw())
|
||||||
|
mobs.set_velocity = function(self, v)
|
||||||
|
|
||||||
|
local yaw = (self.yaw or 0)
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = (math.sin(yaw) * -v),
|
||||||
|
y = 0,
|
||||||
|
z = (math.cos(yaw) * v),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
if vector.length(new_velocity_addition) > vector.length(goal_velocity) then
|
||||||
|
vector.multiply(new_velocity_addition, (vector.length(goal_velocity) / vector.length(new_velocity_addition)))
|
||||||
|
end
|
||||||
|
|
||||||
|
new_velocity_addition.y = 0
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- calculate mob velocity
|
||||||
|
mobs.get_velocity = function(self)
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
|
||||||
|
v.y = 0
|
||||||
|
|
||||||
|
if v then
|
||||||
|
return vector.length(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--make mobs jump
|
||||||
|
mobs.jump = function(self, velocity)
|
||||||
|
|
||||||
|
if self.object:get_velocity().y ~= 0 or not self.old_velocity or (self.old_velocity and self.old_velocity.y > 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--fallback velocity to allow modularity
|
||||||
|
velocity = velocity or DEFAULT_JUMP_HEIGHT
|
||||||
|
|
||||||
|
self.object:add_velocity(vector.new(0,velocity,0))
|
||||||
|
end
|
||||||
|
|
||||||
|
--make mobs fall slowly
|
||||||
|
mobs.mob_fall_slow = function(self)
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = 0,
|
||||||
|
y = -2,
|
||||||
|
z = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
new_velocity_addition.x = 0
|
||||||
|
new_velocity_addition.z = 0
|
||||||
|
|
||||||
|
if vector.length(new_velocity_addition) > vector.length(goal_velocity) then
|
||||||
|
vector.multiply(new_velocity_addition, (vector.length(goal_velocity) / vector.length(new_velocity_addition)))
|
||||||
|
end
|
||||||
|
|
||||||
|
new_velocity_addition.x = 0
|
||||||
|
new_velocity_addition.z = 0
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
_____ _
|
||||||
|
/ ___| (_)
|
||||||
|
\ `--.__ ___ _ __ ___
|
||||||
|
`--. \ \ /\ / / | '_ ` _ \
|
||||||
|
/\__/ /\ V V /| | | | | | |
|
||||||
|
\____/ \_/\_/ |_|_| |_| |_|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--make mobs flop
|
||||||
|
mobs.flop = function(self, velocity)
|
||||||
|
|
||||||
|
if self.object:get_velocity().y ~= 0 or not self.old_velocity or (self.old_velocity and self.old_velocity.y > 0) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs.set_velocity(self, 0)
|
||||||
|
|
||||||
|
--fallback velocity to allow modularity
|
||||||
|
velocity = velocity or DEFAULT_JUMP_HEIGHT
|
||||||
|
|
||||||
|
--create a random direction (2d yaw)
|
||||||
|
local dir = DOUBLE_PI * math.random()
|
||||||
|
|
||||||
|
--create a random force value
|
||||||
|
local force = math.random(0,3) + math.random()
|
||||||
|
|
||||||
|
--convert the yaw to a direction vector then multiply it times the force
|
||||||
|
local final_additional_force = vector.multiply(minetest_yaw_to_dir(dir), force)
|
||||||
|
|
||||||
|
--place in the "flop" velocity to make the mob flop
|
||||||
|
final_additional_force.y = velocity
|
||||||
|
|
||||||
|
self.object:add_velocity(final_additional_force)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- move mob in facing direction
|
||||||
|
--this has been modified to be internal
|
||||||
|
--internal = lua (self.yaw)
|
||||||
|
--engine = c++ (self.object:get_yaw())
|
||||||
|
mobs.set_swim_velocity = function(self, v)
|
||||||
|
|
||||||
|
local yaw = (self.yaw or 0)
|
||||||
|
local pitch = (self.pitch or 0)
|
||||||
|
|
||||||
|
if v == 0 then
|
||||||
|
pitch = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = (math.sin(yaw) * -v),
|
||||||
|
y = pitch,
|
||||||
|
z = (math.cos(yaw) * v),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
if vector.length(new_velocity_addition) > vector.length(goal_velocity) then
|
||||||
|
vector.multiply(new_velocity_addition, (vector.length(goal_velocity) / vector.length(new_velocity_addition)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
______ _
|
||||||
|
| ___| |
|
||||||
|
| |_ | |_ _
|
||||||
|
| _| | | | | |
|
||||||
|
| | | | |_| |
|
||||||
|
\_| |_|\__, |
|
||||||
|
__/ |
|
||||||
|
|___/
|
||||||
|
]]--
|
||||||
|
|
||||||
|
-- move mob in facing direction
|
||||||
|
--this has been modified to be internal
|
||||||
|
--internal = lua (self.yaw)
|
||||||
|
--engine = c++ (self.object:get_yaw())
|
||||||
|
mobs.set_fly_velocity = function(self, v)
|
||||||
|
|
||||||
|
local yaw = (self.yaw or 0)
|
||||||
|
local pitch = (self.pitch or 0)
|
||||||
|
|
||||||
|
if v == 0 then
|
||||||
|
pitch = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = (math.sin(yaw) * -v),
|
||||||
|
y = pitch,
|
||||||
|
z = (math.cos(yaw) * v),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
if vector.length(new_velocity_addition) > vector.length(goal_velocity) then
|
||||||
|
vector.multiply(new_velocity_addition, (vector.length(goal_velocity) / vector.length(new_velocity_addition)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--a quick and simple pitch calculation between two vector positions
|
||||||
|
mobs.calculate_pitch = function(pos1, pos2)
|
||||||
|
|
||||||
|
if pos1 == nil or pos2 == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest_dir_to_yaw(vector.new(vector.distance(vector.new(pos1.x,0,pos1.z),vector.new(pos2.x,0,pos2.z)),0,pos1.y - pos2.y)) + HALF_PI
|
||||||
|
end
|
||||||
|
|
||||||
|
--make mobs fly up or down based on their y difference
|
||||||
|
mobs.set_pitch_while_attacking = function(self)
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
local pos2 = self.attacking:get_pos()
|
||||||
|
|
||||||
|
local pitch = mobs.calculate_pitch(pos2,pos1)
|
||||||
|
|
||||||
|
self.pitch = pitch
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
___
|
||||||
|
|_ |
|
||||||
|
| |_ _ _ __ ___ _ __
|
||||||
|
| | | | | '_ ` _ \| '_ \
|
||||||
|
/\__/ / |_| | | | | | | |_) |
|
||||||
|
\____/ \__,_|_| |_| |_| .__/
|
||||||
|
| |
|
||||||
|
|_|
|
||||||
|
]]--
|
||||||
|
|
||||||
|
--special mob jump movement
|
||||||
|
mobs.jump_move = function(self, velocity)
|
||||||
|
|
||||||
|
if self.object:get_velocity().y ~= 0 or not self.old_velocity or (self.old_velocity and self.old_velocity.y > 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--make the mob stick for a split second
|
||||||
|
mobs.set_velocity(self,0)
|
||||||
|
|
||||||
|
--fallback velocity to allow modularity
|
||||||
|
local jump_height = DEFAULT_JUMP_HEIGHT
|
||||||
|
|
||||||
|
local yaw = (self.yaw or 0)
|
||||||
|
|
||||||
|
local current_velocity = self.object:get_velocity()
|
||||||
|
|
||||||
|
local goal_velocity = {
|
||||||
|
x = (math.sin(yaw) * -velocity),
|
||||||
|
y = jump_height,
|
||||||
|
z = (math.cos(yaw) * velocity),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local new_velocity_addition = vector.subtract(goal_velocity,current_velocity)
|
||||||
|
|
||||||
|
if vector.length(new_velocity_addition) > vector.length(goal_velocity) then
|
||||||
|
vector.multiply(new_velocity_addition, (vector.length(goal_velocity) / vector.length(new_velocity_addition)))
|
||||||
|
end
|
||||||
|
|
||||||
|
--smooths out mobs a bit
|
||||||
|
if vector.length(new_velocity_addition) >= 0.0001 then
|
||||||
|
self.object:add_velocity(new_velocity_addition)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--make it so mobs do not glitch out and freak out
|
||||||
|
--when moving around over nodes
|
||||||
|
mobs.swap_auto_step_height_adjust = function(self)
|
||||||
|
local y_vel = self.object:get_velocity().y
|
||||||
|
|
||||||
|
if y_vel == 0 and self.stepheight ~= self.stepheight_backup then
|
||||||
|
self.stepheight = self.stepheight_backup
|
||||||
|
elseif y_vel ~= 0 and self.stepheight ~= 0 then
|
||||||
|
self.stepheight = 0
|
||||||
|
end
|
||||||
|
end
|