forked from VoxeLibre/VoxeLibre
Compare commits
606 Commits
Author | SHA1 | Date |
---|---|---|
Nils Dagsson Moskopp | 4926c0560d | |
Nils Dagsson Moskopp | 23ca11c8e1 | |
Elias Fleckenstein | 88e084cbca | |
Lizzy Fleckenstein | be86b603f8 | |
iliekprogrammar | f4803a35c4 | |
Lizzy Fleckenstein | ce4c0ed4c1 | |
Lizzy Fleckenstein | 4a3a8841cd | |
Lizzy Fleckenstein | 649f481b51 | |
Lizzy Fleckenstein | b937b38b1c | |
Lizzy Fleckenstein | f9e7f58492 | |
Lizzy Fleckenstein | a77e79d985 | |
Lizzy Fleckenstein | 24ca8252a9 | |
Lizzy Fleckenstein | c6e9d763d6 | |
Lizzy Fleckenstein | 61d0dc8182 | |
Lizzy Fleckenstein | 46d1dd42d4 | |
Lizzy Fleckenstein | 39f66eb4a0 | |
Lizzy Fleckenstein | 18dd1cabd0 | |
Lizzy Fleckenstein | e70161501f | |
Lizzy Fleckenstein | bb6fe65aa5 | |
Lizzy Fleckenstein | cdf6533e0a | |
Lizzy Fleckenstein | d690797011 | |
Lizzy Fleckenstein | 6000c29171 | |
Lizzy Fleckenstein | faff9316e0 | |
Lizzy Fleckenstein | 7c1777c53a | |
Lizzy Fleckenstein | c2f0f0297b | |
Lizzy Fleckenstein | 6473494cbc | |
Lizzy Fleckenstein | 4d93e13f80 | |
Lizzy Fleckenstein | 47340386e2 | |
Lizzy Fleckenstein | 25f467c8ab | |
Elias Fleckenstein | c17fc81d1f | |
Lizzy Fleckenstein | 8f9f5cc936 | |
Elias Fleckenstein | 4af5d8e5e8 | |
Lizzy Fleckenstein | 643ded06da | |
Lizzy Fleckenstein | a4e73886d5 | |
Lizzy Fleckenstein | 148575a05b | |
iliekprogrammar | 0baffeced3 | |
Nils Dagsson Moskopp | 2607d40f1f | |
Artem Arbatsky | db696d0e2b | |
NO11 | f09923ded9 | |
NO11 | 0b9cbdb20a | |
NO11 | c52fda154b | |
NO11 | 28b7304211 | |
NO11 | 1518eb81cb | |
NO11 | 4e0bb0d120 | |
NO11 | 5b37f56005 | |
NO11 | aaee3b6de7 | |
NO11 | 6025a3d7d4 | |
Lizzy Fleckenstein | bbdd8f55eb | |
Lizzy Fleckenstein | 6fd8ff8865 | |
Lizzy Fleckenstein | 11e364b3ec | |
Lizzy Fleckenstein | 4db9952a84 | |
Lizzy Fleckenstein | fb2a501a9c | |
Lizzy Fleckenstein | 938911e7e3 | |
Lizzy Fleckenstein | c1934c4f3a | |
Lizzy Fleckenstein | 61dccfb9e5 | |
Lizzy Fleckenstein | 90796ec7b4 | |
Lizzy Fleckenstein | d30e014233 | |
NO11 | 19689dd857 | |
NO11 | b78e7b3e4d | |
Lizzy Fleckenstein | 835076ea4b | |
Lizzy Fleckenstein | 797da20fa7 | |
Lizzy Fleckenstein | c315d155e1 | |
Lizzy Fleckenstein | 0c567c7921 | |
Lizzy Fleckenstein | 64ebdd0f18 | |
Lizzy Fleckenstein | 1bd972bff7 | |
Lizzy Fleckenstein | ea0f52763c | |
Lizzy Fleckenstein | 70425e9f30 | |
Lizzy Fleckenstein | a0789e72f0 | |
Lizzy Fleckenstein | e341b2a6fe | |
Lizzy Fleckenstein | 4e8e6fbb51 | |
Lizzy Fleckenstein | efbe3c93b4 | |
Lizzy Fleckenstein | 0369465630 | |
Lizzy Fleckenstein | 30f7c638f3 | |
NO11 | eccba76732 | |
Lizzy Fleckenstein | f3d16d264c | |
Lizzy Fleckenstein | da5e703675 | |
Lizzy Fleckenstein | 6466061796 | |
Lizzy Fleckenstein | a80438d58e | |
Lizzy Fleckenstein | 47fbb0c176 | |
Lizzy Fleckenstein | fba30eccd6 | |
Lizzy Fleckenstein | 6e7827902c | |
Lizzy Fleckenstein | a877c615a5 | |
Lizzy Fleckenstein | 7f43ba6e36 | |
Lizzy Fleckenstein | 5edf27ac88 | |
Lizzy Fleckenstein | c998788435 | |
Lizzy Fleckenstein | 7707c3132c | |
Lizzy Fleckenstein | 756d28e2c6 | |
Lizzy Fleckenstein | 3a422e3afc | |
Lizzy Fleckenstein | 362de4c920 | |
Lizzy Fleckenstein | 6580bcab5a | |
Lizzy Fleckenstein | a6def5e9bb | |
NO11 | 6d22262c53 | |
NO11 | 7489010152 | |
Lizzy Fleckenstein | d224236388 | |
Lizzy Fleckenstein | 27f35fe422 | |
Lizzy Fleckenstein | 4b1606eaee | |
Lizzy Fleckenstein | 4776487f83 | |
Lizzy Fleckenstein | 664244d25c | |
Lizzy Fleckenstein | b9999195ec | |
Elias Fleckenstein | b18e077ba3 | |
Elias Fleckenstein | f3693138c8 | |
Elias Fleckenstein | 6e94550a12 | |
NO11 | d85f143fde | |
NO11 | dafe860e56 | |
Lizzy Fleckenstein | 832e634e3b | |
Lizzy Fleckenstein | 5c55ddff1e | |
Lizzy Fleckenstein | a28bfebb9b | |
Lizzy Fleckenstein | e5c5a78553 | |
epCode | 1c458a2e72 | |
NO11 | 94e1d82b45 | |
NO11 | 9ccf8de606 | |
NO11 | 19b5a5aac7 | |
NO11 | 6803d22dbd | |
NO11 | 0584d16569 | |
NO11 | 03829dd518 | |
NO11 | 72ea9069bd | |
NO11 | 9188467a6a | |
NO11 | fe91d7f3e0 | |
NO11 | 463fe2af5f | |
NO11 | e4af02ea52 | |
NO11 | 8e3f9d2169 | |
NO11 | a410d17306 | |
AFCMS | e9437e9e1e | |
AFCMS | e40ed91674 | |
NO11 | 16c73c1cb6 | |
NO11 | 03be736566 | |
NO11 | 8697b38d60 | |
NO11 | e83438e42c | |
NO11 | 71f448537c | |
NO11 | df1d8077e6 | |
NO11 | 89e90b13eb | |
AFCMS | b0cf07a020 | |
AFCMS | e36a8c5acf | |
AFCMS | b2af00db22 | |
AFCMS | e8134345d4 | |
AFCMS | e0c7e938de | |
AFCMS | 3e54acce9d | |
AFCMS | 47fff66a41 | |
ztianyang | ac8383dd4b | |
NO11 | c6003398b5 | |
NO11 | f10d579d9c | |
NO11 | fed43586f2 | |
NO11 | 18a83fa5d7 | |
NO11 | 0e15accada | |
NO11 | 4194a76db0 | |
Lizzy Fleckenstein | 27e4bd6d09 | |
Nils Dagsson Moskopp | 256de2bc36 | |
sfan5 | 693a5317ef | |
Nils Dagsson Moskopp | 95c4d6472b | |
Wuzzy | 1c192f4fbb | |
NO11 | c22c2063ad | |
NO11 | 410b7c7844 | |
NO11 | 9f9799d96e | |
NO11 | cd84c472dc | |
NO11 | 0bc9ab9233 | |
NO11 | 89ff666a6a | |
NO11 | 1a5339e907 | |
NO11 | aa930dc2b8 | |
NO11 | 79463738f6 | |
NO11 | 79d8593d34 | |
NO11 | dc8436fdf9 | |
NO11 | f41cea71fd | |
NO11 | 5fedd914fb | |
Elias Fleckenstein | a8d09338a9 | |
NO11 | 872e3e74d3 | |
NO11 | bf62eb33fa | |
NO11 | 0da7b3fbda | |
Elias Fleckenstein | 3669321828 | |
NO11 | ca086109bf | |
NO11 | 6c6d40eb9d | |
NO11 | 2b322a451f | |
NO11 | f0af15fcd8 | |
AFCMS | af8e88f44c | |
AFCMS | 2d1a43396f | |
AFCMS | df4b8e64cc | |
AFCMS | 40898d3e9d | |
AFCMS | 58a292a4f3 | |
AFCMS | fe62189019 | |
NO11 | 4802b610c3 | |
NO11 | cb55c36863 | |
NO11 | f8dcf05670 | |
NO11 | 7e0bb036f4 | |
NO11 | 664c238137 | |
Elias Fleckenstein | efab5a1cf5 | |
NO11 | d76a7daf2d | |
Emily | 5bb57a81ad | |
Emily | df0c1f1dd1 | |
NO11 | 5c563d6ffd | |
NO11 | 4aabd7d9e7 | |
Elias Fleckenstein | 88f253bf66 | |
NO11 | 289ba826ba | |
NO11 | 4846076c8f | |
NO11 | 5c5c405ccf | |
NO11 | 65d33b935a | |
NO11 | c05e57efb1 | |
NO11 | e44e9eaf62 | |
NO11 | 09a68443cd | |
NO11 | 75b425ffd7 | |
NO11 | 0a9ea7e46a | |
NO11 | a44d9643ae | |
AFCMS | a47630035d | |
AFCMS | 667ef5cad5 | |
AFCMS | fef23d0b6f | |
NO11 | a0d52010bf | |
AFCMS | 44063bca93 | |
AFCMS | e58bb6d859 | |
NO11 | 31d3ea8a87 | |
AFCMS | 13536baed8 | |
AFCMS | 6069bb4a38 | |
AFCMS | c31c852a6e | |
AFCMS | b5f7ae5458 | |
AFCMS | 9254bab971 | |
AFCMS | 999b82c94a | |
NO11 | a83dd8548f | |
Tianyang Zhang | 559cf85c94 | |
NO11 | 96e8e6a86f | |
NO11 | 801d9a2571 | |
NO11 | f2a4d6bd56 | |
NO11 | 48166625d4 | |
NO11 | a2e0cf83bc | |
AFCMS | b364faa7c7 | |
Tianyang Zhang | 60fcafeee4 | |
SmallJoker | 9d9e213012 | |
AFCMS | 49bde37a5e | |
AFCMS | dc17cc91a3 | |
AFCMS | 873a1e73dc | |
AFCMS | 8fff20eec9 | |
AFCMS | ec6086d8e6 | |
AFCMS | 6d7aafe0d4 | |
AFCMS | 91adfcbb1d | |
AFCMS | 8958aa7b12 | |
AFCMS | cf5703d528 | |
AFCMS | 88e59d3592 | |
AFCMS | cd08df175c | |
AFCMS | ca277b6769 | |
AFCMS | b0127fc1c3 | |
AFCMS | d26b1b1402 | |
NO11 | 549bdeb6e9 | |
NO11 | ecfb615c89 | |
epCode | 960b653979 | |
NO11 | 5ceb48fcb1 | |
NO11 | f894153b8a | |
NO11 | 848f1489e8 | |
NO11 | a0d3f51745 | |
NO11 | 46d48ccf2f | |
NO11 | f46c4ebad7 | |
NO11 | b7e6db537b | |
NO11 | 357474e32f | |
NO11 | b6eb0ab66c | |
1F616EMO | d2f7d31360 | |
AFCMS | 8e931e92f5 | |
AFCMS | cbfec5c5c8 | |
NO11 | 509568b4b0 | |
Lizzy Fleckenstein | 6e1758400e | |
cora | c558e30ea5 | |
Lizzy Fleckenstein | fb01e61946 | |
Lizzy Fleckenstein | 8445a9ddec | |
cora | 3b2a25a6cb | |
Lizzy Fleckenstein | 8f584be235 | |
Elias Fleckenstein | 94cbb2b802 | |
NO11 | 1471521709 | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | 2f22ce79cc | |
My favourite Minetest cheat clients are Dragonfire and Waspsaliva. | ae2c5ede30 | |
Elias Fleckenstein | f4a30959ce | |
Lizzy Fleckenstein | b22e4ae99d | |
Elias Fleckenstein | 27f4c210c7 | |
NO11 | 091622f855 | |
NO11 | 5cc4fe955f | |
NO11 | 4afdea56db | |
Nils Dagsson Moskopp | 0a474ee578 | |
NO11 | d45e1c07bd | |
NO11 | 21992dc265 | |
AFCMS | a9ceeabc4b | |
AFCMS | 16d79c38ce | |
kay27 | 7ff476b9b9 | |
kay27 | c6b662ce7a | |
kay27 | 586c18b00f | |
AFCMS | 61b0220951 | |
AFCMS | ef1c06e3d1 | |
AFCMS | a9ae6aa9c8 | |
NO11 | b03cd1fc8e | |
NO11 | cf3a2a42bf | |
NO11 | ff8176e252 | |
NO11 | a62d3ff2ee | |
Elias Fleckenstein | 8bae5dd874 | |
NO11 | 2361c1c797 | |
NO11 | 3b3922b2d6 | |
NO11 | 4987636913 | |
NO11 | c542ff998e | |
NO11 | f28a5b90fc | |
AFCMS | 7a130ff2b9 | |
pitchum | 73927cb0cb | |
AFCMS | 2624343c30 | |
AFCMS | 30e2e0d70a | |
Elias Fleckenstein | eea9fb7670 | |
Elias Fleckenstein | 64d3a28578 | |
basxto | 822071c66b | |
NO11 | eafb1a2797 | |
AFCMS | 40f4287ff2 | |
AFCMS | f70dbb8cfa | |
AFCMS | e11dc4b58e | |
AFCMS | e81da3c4ff | |
AFCMS | a896d7f5e4 | |
NO11 | 3577585d74 | |
NO11 | c350b5158b | |
NO11 | ee2fa60cae | |
NO11 | 0795862c5c | |
NO11 | 99ccd9ea4c | |
Elias Fleckenstein | 4324e97db0 | |
Elias Fleckenstein | 7ae4fb6ae7 | |
AFCMS | 2603c4768b | |
AFCMS | c642f9e2f7 | |
NO11 | 1e0ffed26b | |
NO11 | ee21a24fb6 | |
Elias Fleckenstein | 580f6f9629 | |
1F616EMO | 2fac67898e | |
NO11 | 28bfb3f141 | |
NO11 | f3322fd7cb | |
AFCMS | 881f0b0a71 | |
Elias Fleckenstein | e5bdf230d5 | |
Elias Fleckenstein | 0c21a51c45 | |
FinishedFragment | 9706c56964 | |
Elias Fleckenstein | 7ae777a01d | |
FinishedFragment | d391d50eb8 | |
NO11 | 92c25ef691 | |
NO11 | 7464b8ff48 | |
AFCMS | a21b14e707 | |
NO11 | dfd9737d18 | |
Marcin Serwin | 198d3bff53 | |
Marcin Serwin | 522553fcd0 | |
Marcin Serwin | 753a8ce488 | |
Marcin Serwin | f9dbfd8eb5 | |
Marcin Serwin | 0132d0569f | |
Marcin Serwin | a0d3e92b70 | |
marcin-serwin | cbe8583cef | |
Marcin Serwin | 6715aecd63 | |
Marcin Serwin | b06bc276dc | |
AFCMS | b9fd1ac227 | |
AFCMS | 7e64470f70 | |
Marcin Serwin | 06984fa486 | |
Marcin Serwin | 4bd49f7eb0 | |
Marcin Serwin | a4fc38ffcc | |
AFCMS | c8102838cb | |
AFCMS | 6b53dda79b | |
AFCMS | 640b0dc485 | |
Marcin Serwin | 7804de4230 | |
Marcin Serwin | 22bda9c770 | |
Marcin Serwin | 21bdf914bf | |
NO11 | 75e263debc | |
NO11 | a65db15b5c | |
AFCMS | 95627f4304 | |
AFCMS | cd33d406b2 | |
NO11 | ec542a142a | |
NO11 | 91ae3208bd | |
NO11 | 0d619ec6a8 | |
Marcin Serwin | 8ad953ab17 | |
Marcin Serwin | 362965f8fb | |
Marcin Serwin | 1de0009cc6 | |
Marcin Serwin | 7da32dcc36 | |
Marcin Serwin | 1929df492b | |
Marcin Serwin | 69f85a1aa6 | |
Marcin Serwin | c260ef8c6e | |
AFCMS | 5f82e76862 | |
AFCMS | 3bedc81068 | |
AFCMS | b8782d0af1 | |
AFCMS | f1c6c3b285 | |
AFCMS | f8ee9c79c2 | |
AFCMS | 00063cde73 | |
AFCMS | 0119793d7a | |
epCode | 138a3cd154 | |
Lizzy Fleckenstein | c57ead6985 | |
Lizzy Fleckenstein | 772f48ed58 | |
Lizzy Fleckenstein | 079cedaa34 | |
Lizzy Fleckenstein | 1b0d6a2c2d | |
NO11 | 656891ec61 | |
NO11 | 90bab9026b | |
NO11 | 5b43303873 | |
NO11 | 211b078d3a | |
NO11 | bb77443892 | |
AFCMS | 5fc3256930 | |
AFCMS | ee9577b625 | |
AFCMS | fd387a65af | |
AFCMS | 190d5cad33 | |
AFCMS | 4c9eeed499 | |
Marcin Serwin | 425c7505ec | |
Marcin Serwin | 1ad1bbef13 | |
Marcin Serwin | 5ef0c5bcb7 | |
Marcin Serwin | 4a396fde2c | |
Marcin Serwin | a4c21de153 | |
Marcin Serwin | a27833a0e1 | |
Marcin Serwin | d83c29f0c4 | |
Marcin Serwin | 2873c7f7ce | |
Marcin Serwin | e797a4ec9b | |
NO11 | 6105a9ef22 | |
Marcin Serwin | 37b1191c0a | |
Marcin Serwin | dcceafad41 | |
Marcin Serwin | f421e07d1c | |
AFCMS | 49044ac15e | |
AFCMS | 9e074af07f | |
NO11 | 0ea72ed1f1 | |
NO11 | 47b6a8d7f8 | |
AFCMS | 388ab6d8df | |
AFCMS | 981f3d5d74 | |
AFCMS | 3f3abbbee1 | |
AFCMS | 42d3830c48 | |
AFCMS | 8592670f9b | |
AFCMS | d3a237384d | |
AFCMS | 79cf98c8e3 | |
AFCMS | 00f4cfa5b5 | |
AFCMS | 0cc11d0bbb | |
AFCMS | f61102279f | |
AFCMS | f5a91f7c24 | |
AFCMS | 540508638c | |
AFCMS | 03c9c0b830 | |
AFCMS | 36f3c26139 | |
AFCMS | 0d13e1ffdd | |
AFCMS | be9fff4fe2 | |
AFCMS | ad16fe7072 | |
AFCMS | dd3b5a98ae | |
AFCMS | fddaacdeae | |
AFCMS | 8286fd2a4d | |
AFCMS | f76b66eec6 | |
NO11 | aeb8fa5b88 | |
NO11 | ba38f787e2 | |
cora | 66f132a645 | |
NO11 | 8b11e2fec8 | |
NO11 | 9b9ec13967 | |
NO11 | d5a2e5e514 | |
NO11 | dddaed42b6 | |
NO11 | 973a8201ad | |
AFCMS | 91ac70cf28 | |
AFCMS | b6dd8d5c44 | |
AFCMS | 335405f131 | |
AFCMS | 470f7b70bc | |
AFCMS | b0a3cffad0 | |
AFCMS | 9f41c6fc64 | |
AFCMS | 36dc18e9da | |
AFCMS | 5f8d79b37a | |
AFCMS | 21b7647731 | |
AFCMS | 4fd0bf2c3f | |
AFCMS | 5975b20cff | |
AFCMS | 58510b00ee | |
AFCMS | c19c05443e | |
AFCMS | ce9258b764 | |
AFCMS | 8536e2c033 | |
AFCMS | 8baea02f24 | |
AFCMS | 52b1473657 | |
AFCMS | 3fbcacac3b | |
AFCMS | d43b2149e6 | |
AFCMS | b814fe47f9 | |
AFCMS | 962ae359af | |
AFCMS | ddc157acbf | |
AFCMS | 4fd0ea8a88 | |
AFCMS | e6f72e0c60 | |
AFCMS | dc10448c6e | |
AFCMS | 43f418c6c0 | |
AFCMS | 21fdf492e2 | |
AFCMS | c580c1ccad | |
AFCMS | e75a2e3a67 | |
AFCMS | a9c49853aa | |
AFCMS | 4bc473bc29 | |
AFCMS | 8f6a97eefe | |
AFCMS | be5eb621fd | |
AFCMS | 203c7e2c84 | |
AFCMS | 892f4b96c9 | |
AFCMS | b7bf566190 | |
AFCMS | fdfb586b16 | |
AFCMS | ac459d3914 | |
AFCMS | 621a5a53a8 | |
AFCMS | 60736d04b8 | |
AFCMS | df1eeba6b6 | |
AFCMS | 8ac5d32416 | |
AFCMS | 402a1d0088 | |
AFCMS | b8488ce55e | |
AFCMS | f9a86947e5 | |
AFCMS | 30e83088d4 | |
AFCMS | b3c0e7c1bd | |
AFCMS | bbde3b551f | |
AFCMS | 56310a3624 | |
AFCMS | fcc2087b97 | |
AFCMS | a1acf6d3c1 | |
AFCMS | 82396bd994 | |
AFCMS | 45ab5c8e25 | |
AFCMS | b58e61b988 | |
AFCMS | a200252ab8 | |
AFCMS | 0cac8f3f44 | |
AFCMS | 9edcc60ba2 | |
AFCMS | 0113d290b7 | |
AFCMS | e91ee174a4 | |
AFCMS | c1a717238b | |
AFCMS | a71a0e9161 | |
AFCMS | 1870a89af0 | |
AFCMS | 1b3763654e | |
AFCMS | 84a800f22b | |
AFCMS | 17202115fa | |
AFCMS | 5d65c8a3aa | |
AFCMS | e9f38c6b90 | |
Lizzy Fleckenstein | fb60bd0253 | |
AFCMS | a9bf923df9 | |
Elias Åström | 067affcabb | |
Elias Åström | 4d13eddcd6 | |
Elias Åström | f7052943ec | |
Lizzy Fleckenstein | 04436ea5f7 | |
NO11 | cb82e3c9c5 | |
AFCMS | 4992a05c2b | |
kay27 | 9ae1119438 | |
NO11 | 9e095980c8 | |
NO11 | be9fb5be47 | |
NO11 | f4c302e582 | |
Code-Sploit | bcea5a35ef | |
AFCMS | 86d1e0ac12 | |
AFCMS | 866c097d56 | |
AFCMS | d5ec2ccbd8 | |
AFCMS | 0ad03acf46 | |
AFCMS | 3097df731d | |
jordan4ibanez | ad4e86deca | |
jordan4ibanez | 052bb540ba | |
AFCMS | 074e8c8389 | |
NO11 | 7e132866bf | |
NO11 | 11d700b785 | |
NO11 | 9fa51dc6b9 | |
NO11 | 6ffb7f525a | |
talamh | 2c434495c1 | |
talamh | e6b61a1551 | |
talamh | f1229c5401 | |
Marcin Serwin | e19d39f065 | |
Marcin Serwin | 397a05b651 | |
Marcin Serwin | 5b3a156c09 | |
Marcin Serwin | 604af04849 | |
Marcin Serwin | 0e8c002665 | |
Marcin Serwin | 77597f9bac | |
Marcin Serwin | 65846cb6d8 | |
Marcin Serwin | 97e50714fe | |
Marcin Serwin | 9666076cb4 | |
Marcin Serwin | 7f1cb7fc54 | |
Marcin Serwin | 87b5a7b7ad | |
Marcin Serwin | d74e1fd162 | |
Marcin Serwin | 0e52e09cc8 | |
Marcin Serwin | ffd28c259d | |
Marcin Serwin | eeaf4e09c6 | |
Marcin Serwin | 29001086c3 | |
Marcin Serwin | 1f07f5ba17 | |
Marcin Serwin | 095128b6b3 | |
Marcin Serwin | bcf5eef010 | |
Marcin Serwin | 3917bb51a2 | |
Marcin Serwin | dd0e381e39 | |
Marcin Serwin | 4975a516f5 | |
Marcin Serwin | c0ce511ad0 | |
Marcin Serwin | b93d3cf93c | |
Marcin Serwin | a038908acd | |
Marcin Serwin | a613da65c5 | |
Marcin Serwin | 18903d5a09 | |
Marcin Serwin | c6a07b8007 | |
Marcin Serwin | 67780f2159 | |
Marcin Serwin | 7e9eb927f1 | |
Marcin Serwin | ae7ea8edef | |
Marcin Serwin | 0cf58ce98d | |
Marcin Serwin | 0ebf6b8f92 | |
Marcin Serwin | 0a8b6a63cb | |
Marcin Serwin | 2a06be02b3 | |
Marcin Serwin | c6de8c0669 | |
Marcin Serwin | 3a88ff7cb3 | |
Marcin Serwin | 0de2d3d707 | |
Marcin Serwin | 8934c65564 | |
Marcin Serwin | 51711385a1 | |
Marcin Serwin | 9472f86025 | |
Marcin Serwin | 6fb94fb411 | |
Marcin Serwin | 883bd0f437 | |
Marcin Serwin | 42d2dfed19 | |
Marcin Serwin | 33f22f4eed | |
Marcin Serwin | cec4011de2 | |
Marcin Serwin | 38d788998d | |
Marcin Serwin | 9e41d5874a | |
Marcin Serwin | 1be74d21d3 | |
Marcin Serwin | 32e33d3b21 | |
Marcin Serwin | 7d674e6e72 | |
Marcin Serwin | ec2e5f7430 | |
Marcin Serwin | 7a4d009648 | |
Marcin Serwin | 8f9f4f6093 | |
Marcin Serwin | 38741754e0 | |
Marcin Serwin | f1ce4f0df6 | |
Marcin Serwin | a598d94afd | |
Marcin Serwin | d553aa5e8c | |
Marcin Serwin | 23e52975fd | |
Marcin Serwin | baed298ce6 | |
Marcin Serwin | 6c5c4b0080 | |
Marcin Serwin | b14b08dc30 | |
Marcin Serwin | 2b6f31fe3b | |
Marcin Serwin | 321caa0632 | |
Marcin Serwin | 6f3bd95106 | |
Marcin Serwin | a3b46be64a | |
Marcin Serwin | 8e4ba0d3e1 | |
Marcin Serwin | a7c8bfc0d7 | |
Marcin Serwin | 504a0e5d2c | |
Marcin Serwin | 99c20421f4 | |
Marcin Serwin | cd8e695ca5 | |
Marcin Serwin | 2bf2766688 | |
Marcin Serwin | 7b4eb28160 | |
Marcin Serwin | 2221f89696 | |
Marcin Serwin | 50b209a945 | |
Marcin Serwin | 034a29edc0 | |
Marcin Serwin | 958fee0a73 | |
Marcin Serwin | d50e63d935 | |
Marcin Serwin | 1fd869b5fb | |
Marcin Serwin | 9f7f1f2c89 | |
Marcin Serwin | ee28bda893 |
|
@ -0,0 +1,2 @@
|
||||||
|
# Text Editor TMP Files
|
||||||
|
*.swp
|
12
.luacheckrc
12
.luacheckrc
|
@ -40,4 +40,16 @@ read_globals = {
|
||||||
"factorial"
|
"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.
|
426
CONTRIBUTING.md
426
CONTRIBUTING.md
|
@ -1,83 +1,405 @@
|
||||||
# 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 three persons. Namely, kay27, EliasFleckenstein and jordan4ibanez. You can find us
|
## MineClone2's development target is to...
|
||||||
in the Minetest forums (forums.minetest.net), in IRC in the #mineclone2
|
- 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.
|
||||||
|
|
||||||
By sending us patches or asking us to include your changes in this game,
|
## Links
|
||||||
you agree that they fall under the terms of the LGPLv2.1, which basically
|
* [Mesehub](https://git.minetest.land/MineClone2/MineClone2)
|
||||||
means they will become part of a free software.
|
* [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)
|
||||||
|
|
||||||
## The suggested workflow
|
## Using git
|
||||||
We don't **dictate** your workflow, but in order to work with us in an efficient
|
MineClone2 is developed using the version control system
|
||||||
way, you can follow these suggestions:
|
[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.
|
||||||
|
|
||||||
For small and medium changes:
|
## How you can help as a non-programmer
|
||||||
|
|
||||||
* Fork the repository
|
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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### Crediting
|
||||||
|
If you opened or have contributed to an issue, you receive the
|
||||||
|
`Community` role on our Discord (after asking for it).
|
||||||
|
|
||||||
|
## 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
|
||||||
* Create a pull request to get your changes merged into master
|
* Create a pull request to get your changes merged into master
|
||||||
|
* 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.
|
|
||||||
|
|
||||||
If your code causes bugs and crashes, it is your responsibility to fix them as soon as possible.
|
### 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:
|
||||||
|
|
||||||
We mostly use plain merging rather than rebasing or squash merging.
|
* Minecraft code (Name the source file and line, however DONT post any
|
||||||
|
proprietary code). You can use
|
||||||
|
[MCP](https://minecraft.fandom.com/wiki/Programs_and_editors/Mod_Coder_Pack)
|
||||||
|
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)
|
||||||
|
|
||||||
Your commit names should be relatively descriptive, e.g. when saying "Fix #issueid", the commit message should also contain the title of the issue.
|
### Stick to our guidelines
|
||||||
|
|
||||||
Contributors will be credited in `CREDITS.md`.
|
#### Git Guidelines
|
||||||
|
* 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)
|
||||||
|
|
||||||
## Features > 1.12
|
#### Code Guidelines
|
||||||
|
* Each mod must provide `mod.conf`.
|
||||||
|
* Mod names are snake case, and newly added mods start with `mcl_`, e.g.
|
||||||
|
`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.
|
||||||
|
|
||||||
If you want to make a feature that was added in a Minecraft version later than 1.12, you should fork MineClone5 (mineclone5 branch in the repository) and add your changes to this.
|
```lua
|
||||||
|
mcl_example = {}
|
||||||
|
|
||||||
## What we accept
|
function mcl_example.do_something()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
* Every MC features up to version 1.12 JE.
|
```
|
||||||
* Every already finished and working good features from versions above (only when making a MineClone5 PR / Contribution).
|
|
||||||
* Except features which couldn't be done easily and bugfree because of Minetest engine limitations. Eg. we CAN extend world boundaries by playing with map chunks, just teleporting player onto next layer after 31000 , but it would cost too much (time, code, bugs, performance, stability, etc).
|
|
||||||
* Some features, approved by the rest of the community, I mean maybe some voting and really missing any negative feedback.
|
|
||||||
|
|
||||||
## What we reject
|
* Public functions should not use self references but rather just access
|
||||||
|
the table directly, e.g.
|
||||||
|
|
||||||
* Any features which cause critical bugs, sending them to rework/fix or trying to fix immediately.
|
```lua
|
||||||
* Some small portions of big entirely missing features which just definitely break gamplay balance give nothing useful
|
-- bad
|
||||||
* Controversial features, which some people support while others do not should be discussed well, with publishing forum announcements, at least during the week. In case if there are still doubts - send them into the mod.
|
function mcl_example:do_something()
|
||||||
|
end
|
||||||
|
|
||||||
## Reporting bugs
|
-- good
|
||||||
Report all bugs and missing Minecraft features here:
|
function mcl_example.do_something()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
* Use modern Minetest API, e.g. no usage of `minetest.env`
|
||||||
|
* Tabs should be used for indent, spaces for alignment, e.g.
|
||||||
|
|
||||||
## Direct discussion
|
```lua
|
||||||
We have an IRC channel! Join us on #mineclone2 in freenode.net.
|
|
||||||
|
|
||||||
<ircs://irc.freenode.net:6697/#mineclone2>
|
-- use tabs for indent
|
||||||
|
|
||||||
## Creating releases
|
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.
|
||||||
|
|
30
CREDITS.md
30
CREDITS.md
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
* Fleckenstein
|
* Fleckenstein
|
||||||
|
* Nicu
|
||||||
* kay27
|
* kay27
|
||||||
* jordan4ibanez
|
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
* bzoss
|
* bzoss
|
||||||
|
@ -19,9 +19,11 @@
|
||||||
* iliekprogrammar
|
* iliekprogrammar
|
||||||
* MysticTempest
|
* MysticTempest
|
||||||
* Rootyjr
|
* Rootyjr
|
||||||
* Nicu
|
|
||||||
* aligator
|
* aligator
|
||||||
* Code-Sploit
|
* Code-Sploit
|
||||||
|
* NO11
|
||||||
|
* cora
|
||||||
|
* jordan4ibanez
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
* Laurent Rocher
|
* Laurent Rocher
|
||||||
|
@ -40,7 +42,6 @@
|
||||||
* Jared Moody
|
* Jared Moody
|
||||||
* Li0n
|
* Li0n
|
||||||
* Midgard
|
* Midgard
|
||||||
* NO11
|
|
||||||
* Saku Laesvuori
|
* Saku Laesvuori
|
||||||
* Yukitty
|
* Yukitty
|
||||||
* ZedekThePD
|
* ZedekThePD
|
||||||
|
@ -48,8 +49,24 @@
|
||||||
* dBeans
|
* dBeans
|
||||||
* nickolas360
|
* nickolas360
|
||||||
* yutyo
|
* yutyo
|
||||||
* ztianyang
|
* Tianyang Zhang
|
||||||
* j45
|
* j45
|
||||||
|
* Marcin Serwin
|
||||||
|
* erlehmann
|
||||||
|
* E
|
||||||
|
* Benjamin Schötz
|
||||||
|
* Doloment
|
||||||
|
* Sydney Gems
|
||||||
|
* talamh
|
||||||
|
* Emily2255
|
||||||
|
* Emojigit
|
||||||
|
* FinishedFragment
|
||||||
|
* sfan5
|
||||||
|
* Blue Blancmange
|
||||||
|
* Jared Moody
|
||||||
|
* SmallJoker
|
||||||
|
* Sven792
|
||||||
|
* aldum
|
||||||
|
|
||||||
## MineClone5
|
## MineClone5
|
||||||
* kay27
|
* kay27
|
||||||
|
@ -74,7 +91,6 @@
|
||||||
* Rochambeau
|
* Rochambeau
|
||||||
* rubenwardy
|
* rubenwardy
|
||||||
* stu
|
* stu
|
||||||
* jordan4ibanez
|
|
||||||
* 4aiman
|
* 4aiman
|
||||||
* Kahrl
|
* Kahrl
|
||||||
* Krock
|
* Krock
|
||||||
|
@ -102,6 +118,8 @@
|
||||||
* leorockway
|
* leorockway
|
||||||
* xMrVizzy
|
* xMrVizzy
|
||||||
* yutyo
|
* yutyo
|
||||||
|
* NO11
|
||||||
|
* kay27
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
* Wuzzy
|
* Wuzzy
|
||||||
|
@ -109,6 +127,8 @@
|
||||||
* wuniversales
|
* wuniversales
|
||||||
* kay27
|
* kay27
|
||||||
* pitchum
|
* pitchum
|
||||||
|
* todoporlalibertad
|
||||||
|
* Marcin Serwin
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
* celeron55 for creating Minetest
|
* celeron55 for creating Minetest
|
||||||
|
|
|
@ -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
|
||||||
|
|
56
README.md
56
README.md
|
@ -1,6 +1,4 @@
|
||||||
# (Currently in feature freeze)
|
# MineClone2
|
||||||
|
|
||||||
# MineClone 2
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -71,36 +69,38 @@ an explanation.
|
||||||
This game requires [Minetest](http://minetest.net) to run (version 5.3.0 or
|
This game requires [Minetest](http://minetest.net) to run (version 5.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.
|
||||||
|
|
||||||
## Reporting bugs
|
## Useful links
|
||||||
Please report all bugs and missing Minecraft features here:
|
The MineClone2 repository is hosted at Mesehub. To contribute or report issues, head there.
|
||||||
|
|
||||||
<https://git.minetest.land/MineClone2/MineClone2/issues>
|
* 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>
|
||||||
|
|
||||||
## Chating with the community
|
## Target
|
||||||
Join our discord server at:
|
- Crucially, create a stable, moddable, free/libre clone of Minecraft
|
||||||
|
based on the Minetest engine with polished features, usable in both
|
||||||
<https://discord.gg/84GKcxczG3>
|
singleplayer and multiplayer. Currently, most of **Minecraft Java
|
||||||
|
Edition 1.12.2** features are already implemented and polishing existing
|
||||||
## Project description
|
features are prioritized over new feature requests.
|
||||||
The main goal of **MineClone 2** is to be a clone of Minecraft and to be released as free software.
|
- With lessened priority yet strictly, implement features targetting
|
||||||
|
**Minecraft version 1.17 + OptiFine** (OptiFine only as far as supported
|
||||||
* **Target of development: Minecraft, PC Edition, version 1.12** (later known as “Java Edition”)
|
by the Minetest Engine). This means features in parity with the listed
|
||||||
* MineClone2 also includes Optifine features supported by the Minetest
|
Minecraft experiences are prioritized over those that don't fulfill this
|
||||||
* In general, Minecraft is aimed to be cloned as good as possible
|
scope.
|
||||||
* Cloning the gameplay has highest priority
|
- Optionally, create a performant experience that will run relatively
|
||||||
* MineClone 2 will use different assets, but with a similar style
|
well on really low spec computers. Unfortunately, due to Minecraft's
|
||||||
* Limitations found in Minetest will be documented in the course of development
|
mechanisms and Minetest engine's limitations along with a very small
|
||||||
* Features of later Minecraft versions are collected in the mineclone5 branch
|
playerbase on low spec computers, optimizations are hard to investigate.
|
||||||
|
|
||||||
## Using features from newer versions of Minecraft
|
|
||||||
For > 1.12 features, checkout MineClone5. It includes features from newer Minecraft versions.
|
|
||||||
Download it here: https://git.minetest.land/MineClone2/MineClone2/src/branch/mineclone5
|
|
||||||
|
|
||||||
## Completion status
|
## Completion status
|
||||||
This game is currently in **beta** stage.
|
This game is currently in **beta** stage.
|
||||||
|
@ -187,7 +187,7 @@ Technical differences from Minecraft:
|
||||||
* Different engine (Minetest)
|
* Different engine (Minetest)
|
||||||
* Different easter eggs
|
* Different easter eggs
|
||||||
|
|
||||||
… and finally, MineClone 2 is free software (“free” as in “freedom”)!
|
… and finally, MineClone2 is free software (“free” as in “freedom”)!
|
||||||
|
|
||||||
## Other readme files
|
## Other readme files
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -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
|
||||||
|
@ -271,7 +275,7 @@ end
|
||||||
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||||
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
||||||
--
|
--
|
||||||
-- NOTE:
|
-- NOTE:
|
||||||
-- This function can only be called after mod initialization. Otherwise a mod
|
-- This function can only be called after mod initialization. Otherwise a mod
|
||||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||||
-- loading order.
|
-- loading order.
|
||||||
|
@ -288,7 +292,7 @@ end
|
||||||
-- toolname - Name of the tool used
|
-- toolname - Name of the tool used
|
||||||
-- diggroup - The name of the diggroup the tool is used on
|
-- diggroup - The name of the diggroup the tool is used on
|
||||||
--
|
--
|
||||||
-- NOTE:
|
-- NOTE:
|
||||||
-- This function can only be called after mod initialization. Otherwise a mod
|
-- This function can only be called after mod initialization. Otherwise a mod
|
||||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||||
-- loading order.
|
-- loading order.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ mcl_damage = {
|
||||||
starve = {bypasses_armor = true, bypasses_magic = true},
|
starve = {bypasses_armor = true, bypasses_magic = true},
|
||||||
cactus = {},
|
cactus = {},
|
||||||
fall = {bypasses_armor = true},
|
fall = {bypasses_armor = true},
|
||||||
fly_into_wall = {bypasses_armor = true},
|
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true},
|
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true},
|
||||||
generic = {bypasses_armor = true},
|
generic = {bypasses_armor = true},
|
||||||
magic = {is_magic = true, bypasses_armor = true},
|
magic = {is_magic = true, bypasses_armor = true},
|
||||||
|
@ -28,7 +28,7 @@ mcl_damage = {
|
||||||
fireball = {is_projectile = true, is_fire = true},
|
fireball = {is_projectile = true, is_fire = true},
|
||||||
thorns = {is_magic = true},
|
thorns = {is_magic = true},
|
||||||
explosion = {is_explosion = true},
|
explosion = {is_explosion = true},
|
||||||
cramming = {bypasses_armor = true},
|
cramming = {bypasses_armor = true}, -- unused
|
||||||
fireworks = {is_explosion = true}, -- unused
|
fireworks = {is_explosion = true}, -- unused
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,11 +74,11 @@ function mcl_damage.from_punch(mcl_reason, object)
|
||||||
mcl_reason.direct = object
|
mcl_reason.direct = object
|
||||||
local luaentity = mcl_reason.direct:get_luaentity()
|
local luaentity = mcl_reason.direct:get_luaentity()
|
||||||
if luaentity then
|
if luaentity then
|
||||||
if luaentity.is_arrow then
|
if luaentity._is_arrow then
|
||||||
mcl_reason.type = "arrow"
|
mcl_reason.type = "arrow"
|
||||||
elseif luaentity.is_fireball then
|
elseif luaentity._is_fireball then
|
||||||
mcl_reason.type = "fireball"
|
mcl_reason.type = "fireball"
|
||||||
elseif luaentity.is_mob then
|
elseif luaentity._cmi_is_mob then
|
||||||
mcl_reason.type = "mob"
|
mcl_reason.type = "mob"
|
||||||
end
|
end
|
||||||
mcl_reason.source = mcl_reason.source or luaentity._source_object
|
mcl_reason.source = mcl_reason.source or luaentity._source_object
|
||||||
|
|
|
@ -12,10 +12,12 @@ under the LGPLv2.1 license.
|
||||||
|
|
||||||
mcl_explosions = {}
|
mcl_explosions = {}
|
||||||
|
|
||||||
local mod_fire = minetest.get_modpath("mcl_fire") ~= nil
|
local mod_fire = minetest.get_modpath("mcl_fire")
|
||||||
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
|
||||||
|
@ -26,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 = {}
|
||||||
|
@ -66,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
|
||||||
|
@ -176,14 +177,11 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
|
|
||||||
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 = {}
|
||||||
|
|
||||||
|
@ -247,7 +245,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
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')) and obj:get_hp() > 0 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, direct, sourc
|
||||||
|
|
||||||
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)
|
||||||
|
@ -363,9 +361,9 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
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
|
||||||
|
@ -407,8 +405,7 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
||||||
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.
|
||||||
|
|
|
@ -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,11 +66,11 @@ 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
|
||||||
|
@ -69,61 +80,52 @@ end
|
||||||
|
|
||||||
-- 3 exptime variants because the animation is not tied to particle expiration time.
|
-- 3 exptime variants because the animation is not tied to particle expiration time.
|
||||||
-- 3 colorized variants to imitate minecraft's
|
-- 3 colorized variants to imitate minecraft's
|
||||||
|
local smoke_pdef_cached = {}
|
||||||
|
|
||||||
function mcl_particles.get_smoke_def(def_base)
|
function mcl_particles.spawn_smoke(pos, name, smoke_pdef_base)
|
||||||
local defs = {}
|
local new_minpos = vector.add(pos, smoke_pdef_base.minrelpos)
|
||||||
|
local new_maxpos = vector.add(pos, smoke_pdef_base.maxrelpos)
|
||||||
|
|
||||||
local def = table.copy(def_base)
|
-- populate the cache
|
||||||
def.amount = def.amount / 9
|
if smoke_pdef_cached[name] then
|
||||||
def.time = 0
|
for i, smoke_pdef in ipairs(smoke_pdef_cached[name]) do
|
||||||
def.animation = {
|
smoke_pdef.minpos = new_minpos
|
||||||
type = "vertical_frames",
|
smoke_pdef.maxpos = new_maxpos
|
||||||
aspect_w = 8,
|
add_node_particlespawner(pos, smoke_pdef, "high")
|
||||||
aspect_h = 8,
|
end
|
||||||
-- length = 3 exptime variants
|
-- cache already populated
|
||||||
}
|
else
|
||||||
def.collisiondetection = true
|
smoke_pdef_cached[name] = {}
|
||||||
|
|
||||||
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
|
local smoke_pdef = table.copy(smoke_pdef_base)
|
||||||
-- to have varying exptime for each variant.
|
smoke_pdef.amount = smoke_pdef_base.amount / 9
|
||||||
local exptimes = {0.175, 0.375, 1.0}
|
smoke_pdef.time = 0
|
||||||
local colorizes = {"199", "209", "243"} -- round(78%, 82%, 90% of 256) - 1
|
smoke_pdef.animation = {
|
||||||
for _, exptime in ipairs(exptimes) do
|
type = "vertical_frames",
|
||||||
for _, colorize in ipairs(colorizes) do
|
aspect_w = 8,
|
||||||
def.maxexptime = exptime * def_base.maxexptime
|
aspect_h = 8,
|
||||||
def.animation.length = exptime + 0.1
|
-- length = 3 exptime variants
|
||||||
-- minexptime must be set such that the last frame is actully rendered,
|
}
|
||||||
-- even if its very short. Larger exptime -> larger range
|
smoke_pdef.collisiondetection = true
|
||||||
def.minexptime = math.min(exptime, (7.0 / 8.0 * (exptime + 0.1) + 0.1))
|
smoke_pdef.minpos = new_minpos
|
||||||
def.texture = "mcl_particles_smoke_anim.png^[colorize:#000000:" .. colorize
|
smoke_pdef.maxpos = new_maxpos
|
||||||
|
|
||||||
table.insert(defs, table.copy(def))
|
-- 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
|
end
|
||||||
|
end
|
||||||
return defs
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_particles.add_node_smoke_particlespawner(pos, defs)
|
|
||||||
local minpos = vector.add(pos, defs[1].minrelpos)
|
|
||||||
local maxpos = vector.add(pos, defs[1].maxrelpos)
|
|
||||||
|
|
||||||
for i, def in ipairs(defs) do
|
|
||||||
def.minpos = minpos
|
|
||||||
def.maxpos = maxpos
|
|
||||||
def.attached = nil
|
|
||||||
mcl_particles.add_node_particlespawner(pos, def, "high")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_particles.add_object_smoke_particlespawner(obj, defs)
|
|
||||||
local minpos = defs[1].minrelpos
|
|
||||||
local maxpos = defs[1].maxrelpos
|
|
||||||
|
|
||||||
for i, def in ipairs(defs) do
|
|
||||||
def.minpos = def.minrelpos
|
|
||||||
def.maxpos = def.maxrelpos
|
|
||||||
def.attached = obj
|
|
||||||
minetest.add_particlespawner(def)
|
|
||||||
end
|
|
||||||
end
|
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 148 B |
Binary file not shown.
After Width: | Height: | Size: 154 B |
Binary file not shown.
After Width: | Height: | Size: 155 B |
Binary file not shown.
After Width: | Height: | Size: 165 B |
|
@ -17,8 +17,6 @@ Glass breaking sounds (CC BY 3.0):
|
||||||
|
|
||||||
default_tool_breaks.ogg by EdgardEdition (CC BY 3.0), http://www.freesound.org/people/EdgardEdition
|
default_tool_breaks.ogg by EdgardEdition (CC BY 3.0), http://www.freesound.org/people/EdgardEdition
|
||||||
|
|
||||||
mcl_sounds_poof.ogg by Planman (CC 0), https://freesound.org/people/Planman/sounds/208111/
|
|
||||||
|
|
||||||
Mito551 (sounds) (CC BY-SA 3.0):
|
Mito551 (sounds) (CC BY-SA 3.0):
|
||||||
default_dig_choppy.ogg
|
default_dig_choppy.ogg
|
||||||
default_dig_cracky.ogg
|
default_dig_cracky.ogg
|
||||||
|
|
|
@ -150,7 +150,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
|
||||||
|
@ -212,7 +212,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
|
||||||
|
@ -456,14 +456,7 @@ function mcl_util.calculate_durability(itemstack)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not uses then
|
uses = uses or (next(itemstack:get_tool_capabilities().groupcaps) or {}).uses
|
||||||
local toolcaps = itemstack:get_tool_capabilities()
|
|
||||||
local groupcaps = toolcaps.groupcaps
|
|
||||||
for _, v in pairs(groupcaps) do
|
|
||||||
uses = v.uses
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return uses or 0
|
return uses or 0
|
||||||
|
@ -477,22 +470,32 @@ end
|
||||||
function mcl_util.deal_damage(target, damage, mcl_reason)
|
function mcl_util.deal_damage(target, damage, mcl_reason)
|
||||||
local luaentity = target:get_luaentity()
|
local luaentity = target:get_luaentity()
|
||||||
|
|
||||||
if luaentity and luaentity.deal_damage then
|
if luaentity then
|
||||||
luaentity:deal_damage(damage, mcl_reason or {type = "generic"})
|
if luaentity.deal_damage then
|
||||||
else
|
luaentity:deal_damage(damage, mcl_reason or {type = "generic"})
|
||||||
local hp = target:get_hp()
|
return
|
||||||
|
elseif luaentity._cmi_is_mob then
|
||||||
if hp > 0 then
|
-- local puncher = mcl_reason and mcl_reason.direct or target
|
||||||
target:set_hp(hp - damage, {_mcl_reason = mcl_reason})
|
-- 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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local hp = target:get_hp()
|
||||||
|
|
||||||
|
if hp > 0 then
|
||||||
|
target:set_hp(hp - damage, {_mcl_reason = mcl_reason})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_util.get_hp(obj)
|
function mcl_util.get_hp(obj)
|
||||||
local luaentity = obj:get_luaentity()
|
local luaentity = obj:get_luaentity()
|
||||||
|
|
||||||
if luaentity and luaentity.is_mob then
|
if luaentity and luaentity._cmi_is_mob then
|
||||||
return luaentity.data.health
|
return luaentity.health
|
||||||
else
|
else
|
||||||
return obj:get_hp()
|
return obj:get_hp()
|
||||||
end
|
end
|
||||||
|
@ -535,3 +538,12 @@ function mcl_util.get_object_name(object)
|
||||||
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -38,18 +38,32 @@ function image:encode_header()
|
||||||
self.data = self.data
|
self.data = self.data
|
||||||
.. string.char(0) -- image id
|
.. string.char(0) -- image id
|
||||||
.. string.char(0) -- color map type
|
.. string.char(0) -- color map type
|
||||||
.. string.char(2) -- image type (uncompressed true-color image = 2)
|
.. string.char(10) -- image type (RLE RGB = 10)
|
||||||
self:encode_colormap_spec() -- color map specification
|
self:encode_colormap_spec() -- color map specification
|
||||||
self:encode_image_spec() -- image specification
|
self:encode_image_spec() -- image specification
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode_data()
|
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 _, row in ipairs(self.pixels) do
|
||||||
for _, pixel in ipairs(row) do
|
for _, pixel in ipairs(row) do
|
||||||
self.data = self.data
|
current_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
||||||
.. 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
|
||||||
end
|
end
|
||||||
|
packets[#packets +1] = rle_packet
|
||||||
|
self.data = self.data .. table.concat(packets)
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode_footer()
|
function image:encode_footer()
|
||||||
|
|
|
@ -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,6 +1,8 @@
|
||||||
--Dripping Water Mod
|
--Dripping Water Mod
|
||||||
--by kddekadenz
|
--by kddekadenz
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
|
||||||
-- License of code, textures & sounds: CC0
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
--Drop entities
|
--Drop entities
|
||||||
|
@ -20,26 +22,21 @@ minetest.register_entity("drippingwater:drop_water", {
|
||||||
spritediv = {x=1, y=1},
|
spritediv = {x=1, y=1},
|
||||||
initial_sprite_basepos = {x=0, y=0},
|
initial_sprite_basepos = {x=0, y=0},
|
||||||
static_save = false,
|
static_save = false,
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
on_activate = function(self, staticdata)
|
||||||
self.object:set_sprite({x=0,y=0}, 1, 1, true)
|
self.object:set_sprite({x=0,y=0}, 1, 1, true)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
local k = math.random(1,222)
|
local k = math.random(1,222)
|
||||||
local ownpos = self.object:get_pos()
|
local ownpos = self.object:get_pos()
|
||||||
|
if k==1 then
|
||||||
if k==1 then
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
end
|
||||||
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})
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
end
|
||||||
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
|
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
minetest.sound_play({name="drippingwater_drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
minetest.sound_play({name="drippingwater_drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
@ -61,27 +58,21 @@ minetest.register_entity("drippingwater:drop_lava", {
|
||||||
spritediv = {x=1, y=1},
|
spritediv = {x=1, y=1},
|
||||||
initial_sprite_basepos = {x=0, y=0},
|
initial_sprite_basepos = {x=0, y=0},
|
||||||
static_save = false,
|
static_save = false,
|
||||||
|
|
||||||
on_activate = function(self, staticdata)
|
on_activate = function(self, staticdata)
|
||||||
self.object:set_sprite({x=0,y=0}, 1, 0, true)
|
self.object:set_sprite({x=0,y=0}, 1, 0, true)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
local k = math.random(1,222)
|
local k = math.random(1,222)
|
||||||
local ownpos = self.object:get_pos()
|
local ownpos = self.object:get_pos()
|
||||||
|
if k == 1 then
|
||||||
if k==1 then
|
self.object:set_acceleration({x=0, y=-5, z=0})
|
||||||
self.object:set_acceleration({x=0, y=-5, z=0})
|
end
|
||||||
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})
|
||||||
if minetest.get_node({x=ownpos.x, y=ownpos.y +0.5, z=ownpos.z}).name == "air" then
|
end
|
||||||
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
|
if minetest.get_node({x=ownpos.x, y=ownpos.y -0.5, z=ownpos.z}).name ~= "air" then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
minetest.sound_play({name="drippingwater_lavadrip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
minetest.sound_play({name="drippingwater_lavadrip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
@ -90,36 +81,34 @@ minetest.register_entity("drippingwater:drop_lava", {
|
||||||
|
|
||||||
--Create drop
|
--Create drop
|
||||||
|
|
||||||
minetest.register_abm(
|
minetest.register_abm({
|
||||||
{
|
|
||||||
label = "Create water drops",
|
label = "Create water drops",
|
||||||
nodenames = {"group:opaque", "group:leaves"},
|
nodenames = {"group:opaque", "group:leaves"},
|
||||||
neighbors = {"group:water"},
|
neighbors = {"group:water"},
|
||||||
interval = 2,
|
interval = 2,
|
||||||
chance = 22,
|
chance = 22,
|
||||||
action = function(pos)
|
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
|
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "water") ~= 0
|
||||||
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
and minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
||||||
local i = math.random(-45,45) / 100
|
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")
|
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_water")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
--Create lava drop
|
--Create lava drop
|
||||||
|
|
||||||
minetest.register_abm(
|
minetest.register_abm({
|
||||||
{
|
|
||||||
label = "Create lava drops",
|
label = "Create lava drops",
|
||||||
nodenames = {"group:opaque"},
|
nodenames = {"group:opaque"},
|
||||||
neighbors = {"group:lava"},
|
neighbors = {"group:lava"},
|
||||||
interval = 2,
|
interval = 2,
|
||||||
chance = 22,
|
chance = 22,
|
||||||
action = function(pos)
|
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
|
if minetest.get_item_group(minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name, "lava") ~= 0
|
||||||
minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
and minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == "air" then
|
||||||
local i = math.random(-45,45) / 100
|
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")
|
minetest.add_entity({x=pos.x + i, y=pos.y - 0.501, z=pos.z + i}, "drippingwater:drop_lava")
|
||||||
end
|
end
|
||||||
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
|
||||||
|
@ -12,40 +12,51 @@ local function is_group(pos, group)
|
||||||
return minetest.get_item_group(nn, group) ~= 0
|
return minetest.get_item_group(nn, group) ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_velocity(v, yaw, y)
|
|
||||||
return vector.add(vector.new(0, y, 0), vector.multiply(minetest.yaw_to_dir(yaw), v))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_v(v)
|
|
||||||
return math.sqrt(v.x ^ 2 + v.z ^ 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_sign(i)
|
|
||||||
if not i or i == 0 then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return i / math.abs(i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_water = flowlib.is_water
|
local is_water = flowlib.is_water
|
||||||
|
|
||||||
local function is_ice(pos)
|
local function is_ice(pos)
|
||||||
return is_group(pos, "ice")
|
return is_group(pos, "ice")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_sign(i)
|
||||||
|
if i == 0 then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return i / math.abs(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_velocity(v, yaw, y)
|
||||||
|
local x = -math.sin(yaw) * v
|
||||||
|
local z = math.cos(yaw) * v
|
||||||
|
return {x = x, y = y, z = z}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_v(v)
|
||||||
|
return math.sqrt(v.x ^ 2 + v.z ^ 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function check_object(obj)
|
||||||
|
return obj and (obj:is_player() or obj:get_luaentity()) and obj
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_visual_size(obj)
|
||||||
|
return obj:is_player() and {x = 1, y = 1, z = 1} or obj:get_luaentity()._old_visual_size or obj:get_properties().visual_size
|
||||||
|
end
|
||||||
|
|
||||||
local function set_attach(boat)
|
local function set_attach(boat)
|
||||||
boat._driver:set_attach(boat.object, "", vector.new(0, 0.42, -1), vector.new(0, 0, 0))
|
boat._driver:set_attach(boat.object, "",
|
||||||
|
{x = 0, y = 0.42, z = -1}, {x = 0, y = 0, z = 0})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_double_attach(boat)
|
local function set_double_attach(boat)
|
||||||
boat._driver:set_attach(boat.object, "", vector.new(0, 0.42, 0.8), vector.new(0, 0, 0))
|
boat._driver:set_attach(boat.object, "",
|
||||||
boat._passenger:set_attach(boat.object, "", vector.new(0, 0.42, -2.2), vector.new(0, 0, 0))
|
{x = 0, y = 0.42, z = 0.8}, {x = 0, y = 0, z = 0})
|
||||||
|
boat._passenger:set_attach(boat.object, "",
|
||||||
|
{x = 0, y = 0.42, z = -2.2}, {x = 0, y = 0, z = 0})
|
||||||
end
|
end
|
||||||
|
|
||||||
local function enter_boat(self, obj)
|
local function attach_object(self, obj)
|
||||||
mcl_mount.mount(obj, self.object)
|
|
||||||
|
|
||||||
if self._driver then
|
if self._driver then
|
||||||
if self._driver:is_player() then
|
if self._driver:is_player() then
|
||||||
self._passenger = obj
|
self._passenger = obj
|
||||||
|
@ -58,6 +69,39 @@ local function enter_boat(self, obj)
|
||||||
self._driver = obj
|
self._driver = obj
|
||||||
set_attach(self)
|
set_attach(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local visual_size = get_visual_size(obj)
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
obj:set_properties({visual_size = vector.divide(visual_size, boat_visual_size)})
|
||||||
|
|
||||||
|
if obj:is_player() then
|
||||||
|
local name = obj:get_player_name()
|
||||||
|
mcl_player.player_attached[name] = true
|
||||||
|
minetest.after(0.2, function(name)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
if player then
|
||||||
|
mcl_player.player_set_animation(player, "sit" , 30)
|
||||||
|
end
|
||||||
|
end, name)
|
||||||
|
obj:set_look_horizontal(yaw)
|
||||||
|
mcl_title.set(obj, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
||||||
|
else
|
||||||
|
obj:get_luaentity()._old_visual_size = visual_size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function detach_object(obj, change_pos)
|
||||||
|
obj:set_detach()
|
||||||
|
obj:set_properties({visual_size = get_visual_size(obj)})
|
||||||
|
if obj:is_player() then
|
||||||
|
mcl_player.player_attached[obj:get_player_name()] = false
|
||||||
|
mcl_player.player_set_animation(obj, "stand" , 30)
|
||||||
|
else
|
||||||
|
obj:get_luaentity()._old_visual_size = nil
|
||||||
|
end
|
||||||
|
if change_pos then
|
||||||
|
obj:set_pos(vector.add(obj:get_pos(), vector.new(0, 0.2, 0)))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -87,13 +131,16 @@ local boat = {
|
||||||
_damage_anim = 0,
|
_damage_anim = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minetest.register_on_respawnplayer(detach_object)
|
||||||
|
|
||||||
function boat.on_rightclick(self, clicker)
|
function boat.on_rightclick(self, clicker)
|
||||||
if self._passenger or not clicker or clicker:get_attach() then
|
if self._passenger or not clicker or clicker:get_attach() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
enter_boat(self, clicker)
|
attach_object(self, clicker)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function boat.on_activate(self, staticdata, dtime_s)
|
function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self.object:set_armor_groups({fleshy = 100})
|
self.object:set_armor_groups({fleshy = 100})
|
||||||
local data = minetest.deserialize(staticdata)
|
local data = minetest.deserialize(staticdata)
|
||||||
|
@ -125,10 +172,10 @@ function boat.on_death(self, killer)
|
||||||
minetest.add_item(self.object:get_pos(), self._itemstring)
|
minetest.add_item(self.object:get_pos(), self._itemstring)
|
||||||
end
|
end
|
||||||
if self._driver then
|
if self._driver then
|
||||||
mcl_mount.throw_off(self._driver)
|
detach_object(self._driver)
|
||||||
end
|
end
|
||||||
if self._passenger then
|
if self._passenger then
|
||||||
mcl_mount.throw_off(self._passenger)
|
detach_object(self._passenger)
|
||||||
end
|
end
|
||||||
self._driver = nil
|
self._driver = nil
|
||||||
self._passenger = nil
|
self._passenger = nil
|
||||||
|
@ -188,13 +235,19 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
|
|
||||||
local had_passenger = self._passenger
|
local had_passenger = self._passenger
|
||||||
|
|
||||||
self._driver = self._driver and self._driver:get_attach() == self.object and self._driver
|
self._driver = check_object(self._driver)
|
||||||
self._passenger = self._passenger and self._passenger:get_attach() == self.object and self._passenger
|
self._passenger = check_object(self._passenger)
|
||||||
|
|
||||||
if self._passenger then
|
if self._passenger then
|
||||||
if not self._driver then
|
if not self._driver then
|
||||||
self._driver = self._passenger
|
self._driver = self._passenger
|
||||||
self._passenger = nil
|
self._passenger = nil
|
||||||
|
else
|
||||||
|
local ctrl = self._passenger:get_player_control()
|
||||||
|
if ctrl and ctrl.sneak then
|
||||||
|
detach_object(self._passenger, true)
|
||||||
|
self._passenger = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -203,8 +256,13 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
set_attach(self)
|
set_attach(self)
|
||||||
end
|
end
|
||||||
local ctrl = self._driver:get_player_control()
|
local ctrl = self._driver:get_player_control()
|
||||||
|
if ctrl and ctrl.sneak then
|
||||||
|
detach_object(self._driver, true)
|
||||||
|
self._driver = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
local yaw = self.object:get_yaw()
|
local yaw = self.object:get_yaw()
|
||||||
if ctrl and ctrl.up then
|
if ctrl.up then
|
||||||
-- Forwards
|
-- Forwards
|
||||||
self._v = self._v + 0.1 * v_factor
|
self._v = self._v + 0.1 * v_factor
|
||||||
|
|
||||||
|
@ -213,7 +271,7 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
self.object:set_animation({x=0, y=40}, paddling_speed, 0, true)
|
self.object:set_animation({x=0, y=40}, paddling_speed, 0, true)
|
||||||
self._animation = 1
|
self._animation = 1
|
||||||
end
|
end
|
||||||
elseif ctrl and ctrl.down then
|
elseif ctrl.down then
|
||||||
-- Backwards
|
-- Backwards
|
||||||
self._v = self._v - 0.1 * v_factor
|
self._v = self._v - 0.1 * v_factor
|
||||||
|
|
||||||
|
@ -251,8 +309,8 @@ function boat.on_step(self, dtime, moveresult)
|
||||||
|
|
||||||
for _, obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 1.3)) do
|
for _, obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 1.3)) do
|
||||||
local entity = obj:get_luaentity()
|
local entity = obj:get_luaentity()
|
||||||
if entity and entity.is_mob then
|
if entity and entity._cmi_is_mob then
|
||||||
enter_boat(self, obj)
|
attach_object(self, obj)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -270,10 +328,10 @@ 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)
|
||||||
|
@ -412,6 +470,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.=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ź
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,5 @@
|
||||||
local S = minetest.get_translator("mcl_burning")
|
|
||||||
|
|
||||||
function mcl_burning.get_storage(obj)
|
function mcl_burning.get_storage(obj)
|
||||||
if obj:is_player() then
|
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||||
return mcl_burning.storage[obj]
|
|
||||||
else
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
|
|
||||||
if luaentity.is_mob then
|
|
||||||
return luaentity.data
|
|
||||||
end
|
|
||||||
|
|
||||||
return luaentity
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_burning.is_burning(obj)
|
function mcl_burning.is_burning(obj)
|
||||||
|
@ -79,22 +67,29 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not storage.burn_time or burn_time >= storage.burn_time then
|
if not storage.burn_time or burn_time >= storage.burn_time then
|
||||||
if obj:is_player() and not storage.fire_hud_id then
|
if obj:is_player() then
|
||||||
storage.fire_hud_id = obj:hud_add({
|
mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames))
|
||||||
hud_elem_type = "image",
|
mcl_burning.channels[obj]:send_all("start")
|
||||||
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,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
storage.burn_time = burn_time
|
storage.burn_time = burn_time
|
||||||
storage.fire_damage_timer = 0
|
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, false, storage)
|
||||||
|
local obj_size = obj:get_properties().visual_size
|
||||||
|
|
||||||
|
local vertical_grow_factor = 1.2
|
||||||
|
local horizontal_grow_factor = 1.1
|
||||||
|
local grow_vector = vector.new(horizontal_grow_factor, vertical_grow_factor, horizontal_grow_factor)
|
||||||
|
|
||||||
|
local size = vector.subtract(maxp, minp)
|
||||||
|
size = vector.multiply(size, grow_vector)
|
||||||
|
size = vector.divide(size, obj_size)
|
||||||
|
local offset = vector.new(0, size.y * 10 / 2, 0)
|
||||||
|
|
||||||
|
fire_entity:set_properties({visual_size = size})
|
||||||
|
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
||||||
local fire_luaentity = fire_entity:get_luaentity()
|
local fire_luaentity = fire_entity:get_luaentity()
|
||||||
fire_luaentity:update_visual_size(obj, storage)
|
|
||||||
fire_luaentity:update_frame(obj, storage)
|
|
||||||
|
|
||||||
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
||||||
local other_luaentity = other:get_luaentity()
|
local other_luaentity = other:get_luaentity()
|
||||||
|
@ -110,9 +105,7 @@ function mcl_burning.extinguish(obj)
|
||||||
if mcl_burning.is_burning(obj) then
|
if mcl_burning.is_burning(obj) then
|
||||||
local storage = mcl_burning.get_storage(obj)
|
local storage = mcl_burning.get_storage(obj)
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
if storage.fire_hud_id then
|
mcl_burning.channels[obj]:send_all("stop")
|
||||||
obj:hud_remove(storage.fire_hud_id)
|
|
||||||
end
|
|
||||||
mcl_burning.storage[obj] = {}
|
mcl_burning.storage[obj] = {}
|
||||||
else
|
else
|
||||||
storage.burn_time = nil
|
storage.burn_time = nil
|
||||||
|
@ -133,7 +126,12 @@ function mcl_burning.tick(obj, dtime, storage)
|
||||||
|
|
||||||
if storage.fire_damage_timer >= 1 then
|
if storage.fire_damage_timer >= 1 then
|
||||||
storage.fire_damage_timer = 0
|
storage.fire_damage_timer = 0
|
||||||
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
|
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
|
||||||
|
if not luaentity or not luaentity.fire_damage_resistant then
|
||||||
|
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
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 = {},
|
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_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
|
||||||
local storage = mcl_burning.storage[player]
|
local storage = mcl_burning.storage[player]
|
||||||
if not mcl_burning.tick(player, dtime, storage) and not mcl_burning.is_affected_by_rain(player) then
|
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 nodes = mcl_burning.get_touching_nodes(player, {"group:puts_out_fire", "group:set_on_fire"}, storage)
|
||||||
|
@ -17,12 +22,12 @@ minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
for _, pos in pairs(nodes) do
|
for _, pos in pairs(nodes) do
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
if minetest.get_item_group(node.name, "puts_out_fire") > 0 then
|
if get_item_group(node.name, "puts_out_fire") > 0 then
|
||||||
burn_time = 0
|
burn_time = 0
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
local value = minetest.get_item_group(node.name, "set_on_fire")
|
local value = get_item_group(node.name, "set_on_fire")
|
||||||
if value > burn_time then
|
if value > burn_time then
|
||||||
burn_time = value
|
burn_time = value
|
||||||
end
|
end
|
||||||
|
@ -35,21 +40,6 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
mcl_damage.register_modifier(function(obj, damage, reason)
|
|
||||||
if reason.is_fire then
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if luaentity and luaentity.no_fire_damage then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end, -200)
|
|
||||||
|
|
||||||
mcl_damage.register_on_damage(function(obj, damage, reason)
|
|
||||||
if reason.direct and mcl_burning.is_burning(obj) then
|
|
||||||
mcl_burning.set_on_fire(obj, 5)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_respawnplayer(function(player)
|
minetest.register_on_respawnplayer(function(player)
|
||||||
mcl_burning.extinguish(player)
|
mcl_burning.extinguish(player)
|
||||||
end)
|
end)
|
||||||
|
@ -65,13 +55,11 @@ minetest.register_on_joinplayer(function(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_burning.storage[player] = storage
|
mcl_burning.storage[player] = storage
|
||||||
|
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
minetest.register_on_leaveplayer(function(player)
|
||||||
local storage = mcl_burning.storage[player]
|
player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player]))
|
||||||
storage.fire_hud_id = nil
|
|
||||||
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
|
|
||||||
|
|
||||||
mcl_burning.storage[player] = nil
|
mcl_burning.storage[player] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -80,28 +68,28 @@ minetest.register_entity("mcl_burning:fire", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
physical = false,
|
physical = false,
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||||
visual = "cube",
|
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,
|
pointable = false,
|
||||||
glow = -1,
|
glow = -1,
|
||||||
|
backface_culling = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
animation_frame = 0,
|
animation_frame = 0,
|
||||||
animation_timer = 0,
|
animation_timer = 0,
|
||||||
|
on_activate = function(self)
|
||||||
on_step = function(self, dtime)
|
self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames)
|
||||||
local parent, storage = self:sanity_check()
|
end,
|
||||||
|
on_step = function(self)
|
||||||
if parent then
|
if not self:sanity_check() then
|
||||||
self.animation_timer = self.animation_timer + dtime
|
|
||||||
if self.animation_timer >= 0.1 then
|
|
||||||
self.animation_timer = 0
|
|
||||||
self.animation_frame = self.animation_frame + 1
|
|
||||||
if self.animation_frame > mcl_burning.animation_frames - 1 then
|
|
||||||
self.animation_frame = 0
|
|
||||||
end
|
|
||||||
self:update_frame(parent, storage)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -109,42 +97,15 @@ minetest.register_entity("mcl_burning:fire", {
|
||||||
local parent = self.object:get_attach()
|
local parent = self.object:get_attach()
|
||||||
|
|
||||||
if not parent then
|
if not parent then
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local storage = mcl_burning.get_storage(parent)
|
local storage = mcl_burning.get_storage(parent)
|
||||||
|
|
||||||
if not storage or not storage.burn_time then
|
if not storage or not storage.burn_time then
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return parent, storage
|
return true
|
||||||
end,
|
|
||||||
update_frame = function(self, parent, storage)
|
|
||||||
local frame_overlay = "^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. self.animation_frame
|
|
||||||
local fire_texture = "mcl_burning_entity_flame_animated.png" .. frame_overlay
|
|
||||||
self.object:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
|
|
||||||
if parent:is_player() then
|
|
||||||
parent:hud_change(storage.fire_hud_id, "text", "mcl_burning_hud_flame_animated.png" .. frame_overlay)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
update_visual_size = function(self, parent, storage)
|
|
||||||
parent = parent or self.object:get_attach()
|
|
||||||
storage = storage or mcl_burning.get_storage(parent)
|
|
||||||
|
|
||||||
local minp, maxp = mcl_burning.get_collisionbox(parent, false, storage)
|
|
||||||
local obj_size = parent:get_properties().visual_size
|
|
||||||
|
|
||||||
local vertical_grow_factor = 1.2
|
|
||||||
local horizontal_grow_factor = 1.1
|
|
||||||
local grow_vector = vector.new(horizontal_grow_factor, vertical_grow_factor, horizontal_grow_factor)
|
|
||||||
|
|
||||||
local size = vector.subtract(maxp, minp)
|
|
||||||
size = vector.multiply(size, grow_vector)
|
|
||||||
size = vector.divide(size, obj_size)
|
|
||||||
local offset = vector.new(0, size.y * 10 / 2, 0)
|
|
||||||
|
|
||||||
self.object:set_properties({visual_size = size})
|
|
||||||
self.object:set_attach(parent, "", offset)
|
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
name = mcl_burning
|
name = mcl_burning
|
||||||
description = Burning Objects for MineClone2
|
description = Burning Objects for MineClone2
|
||||||
author = Fleckenstein
|
author = Fleckenstein
|
||||||
depends = mcl_damage
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
local S = minetest.get_translator("mcl_falling_nodes")
|
local function get_falling_depth(self)
|
||||||
local has_mcl_armor = minetest.get_modpath("mcl_armor")
|
|
||||||
|
|
||||||
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()
|
||||||
|
@ -9,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
|
||||||
|
@ -22,7 +19,10 @@ local deal_falling_damage = function(self, dtime)
|
||||||
end
|
end
|
||||||
self._hit = self._hit or {}
|
self._hit = self._hit or {}
|
||||||
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
|
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||||
if mcl_util.get_hp(obj) > 0 and not self._hit[obj] then
|
local entity = obj:get_luaentity()
|
||||||
|
if entity and entity.name == "__builtin:item" then
|
||||||
|
obj:remove()
|
||||||
|
elseif mcl_util.get_hp(obj) > 0 and not self._hit[obj] then
|
||||||
self._hit[obj] = true
|
self._hit[obj] = true
|
||||||
local way = self._startpos.y - pos.y
|
local way = self._startpos.y - pos.y
|
||||||
local damage = (way - 1) * 2
|
local damage = (way - 1) * 2
|
||||||
|
@ -38,7 +38,7 @@ local deal_falling_damage = function(self, dtime)
|
||||||
inv:set_stack("armor", 2, helmet)
|
inv:set_stack("armor", 2, helmet)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local deathmsg, dmg_type
|
local dmg_type
|
||||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||||
dmg_type = "anvil"
|
dmg_type = "anvil"
|
||||||
else
|
else
|
||||||
|
@ -60,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
|
||||||
|
@ -90,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.
|
||||||
|
@ -111,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})
|
||||||
|
|
||||||
|
@ -134,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()
|
||||||
|
@ -186,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
|
||||||
|
@ -256,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,
|
||||||
|
@ -426,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
|
||||||
|
@ -475,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,
|
||||||
|
@ -483,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)
|
||||||
|
@ -570,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
|
||||||
|
@ -580,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
|
||||||
|
@ -637,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
|
||||||
|
@ -648,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,9 +65,9 @@ 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
|
||||||
local left = {x=0, y=0, z=0}
|
local left = {x=0, y=0, z=0}
|
||||||
local right = {x=0, y=0, z=0}
|
local right = {x=0, y=0, z=0}
|
||||||
|
@ -78,7 +78,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
left.z = dir.x
|
left.z = dir.x
|
||||||
right.z = -dir.x
|
right.z = -dir.x
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctrl then
|
if ctrl then
|
||||||
if old_switch == 1 then
|
if old_switch == 1 then
|
||||||
left_check = false
|
left_check = false
|
||||||
|
@ -100,13 +100,13 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
right_check = true
|
right_check = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Normal
|
-- Normal
|
||||||
cur = mcl_minecarts:check_front_up_down(pos, dir, true, railtype)
|
cur = mcl_minecarts:check_front_up_down(pos, dir, true, railtype)
|
||||||
if cur then
|
if cur then
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Left, if not already checked
|
-- Left, if not already checked
|
||||||
if left_check then
|
if left_check then
|
||||||
cur = mcl_minecarts:check_front_up_down(pos, left, false, railtype)
|
cur = mcl_minecarts:check_front_up_down(pos, left, false, railtype)
|
||||||
|
@ -114,7 +114,7 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Right, if not already checked
|
-- Right, if not already checked
|
||||||
if right_check then
|
if right_check then
|
||||||
cur = mcl_minecarts:check_front_up_down(pos, right, false, railtype)
|
cur = mcl_minecarts:check_front_up_down(pos, right, false, railtype)
|
||||||
|
@ -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=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"},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,74 @@
|
||||||
# MineClone2 Mobs
|
|
||||||
|
|
||||||
This is a merged version of Mobs redo MineClone2 Edition (API) and mobs_mc (Mobs content).
|
Mobs Redo: MineClone 2 Edition
|
||||||
The API was rewritten by jordan4ibanez and later Fleckenstein from Mobs redo MineClone2 Edition and merged with mobs_mc by Fleckenstein.
|
|
||||||
Mobs redo MineClone2 Edition was built by Wuzzy2 and contributors from Mobs Redo. Mobs redo was built by TenPlus1 from Simple Mobs by PilzAdam and mobs_mc was built by maikerumine.
|
|
||||||
|
|
||||||
Seems like we've come a long way since 2012.
|
Based on Mobs Redo from TenPlus1
|
||||||
|
Built from PilzAdam's original Simple Mobs with additional mobs by KrupnoPavel, Zeg9, ExeterDad and AspireMint.
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
* Fleckenstein: Rewrite of jordan's work, merged mobs_mc and mcl_mobs
|
This mod contains the API only for adding your own mobs into the world, so please use the additional modpacks to add animals, monsters etc.
|
||||||
* jordan4ibanez: Rewrite of the mcl_mobs API
|
|
||||||
* [maikerumine](https://github.com/maikerumine): Creator of mobs_mc (Coding behaviour, spawning, drops, and misc)
|
|
||||||
* TenPlus1: Built the original Mobs Redo API
|
|
||||||
* PilzAdam: Created Simple Mobs which Mobs Redo is based on together with KrupnoPavel, Zeg9, ExeterDad and AspireMint
|
|
||||||
* [Wuzzy2](https://github.com/Wuzzy2): Zombies, husks, item textures, and code, created Mobs redo MineClone2 Edition
|
|
||||||
* [toby109tt](https://github.com/tobyplowy): Mapping fixes - better 2D planes
|
|
||||||
* [22i](https://github.com/22i): Models (done in Blender) and mob icons for spawn eggs
|
|
||||||
* [XSSheep](https://www.planetminecraft.com/member/xssheep/): Mob and item textures (from [Pixel Perfection](https://www.planetminecraft.com/texture_pack/131pixel-perfection/))
|
|
||||||
* MysticTempest: More mob textures
|
|
||||||
* See `LICENSE-MEDIA.md` for detailed credits about each file
|
|
||||||
|
|
||||||
## Licensing
|
|
||||||
|
|
||||||
* Media: MIT, CC0, CC BY 3.0 CC BY-SA 4.0, LGPLv2.1, GPLv3. See `LICENSE-MEDIA.md` for details
|
https://forum.minetest.net/viewtopic.php?f=11&t=9917
|
||||||
* License of mobs_mc code: GPLv3
|
|
||||||
* Mobs Redo: See `LICENSE-API.txt`
|
|
||||||
|
|
||||||
### Links
|
------------
|
||||||
|
Credits:
|
||||||
|
|
||||||
* [`mobs_mc`](https://github.com/maikerumine/mobs_mc)
|
mcl_mobs_mob_poof.ogg:
|
||||||
* [Blender models](https://github.com/22i/minecraft-voxel-blender-models)
|
- by Planman (license: Creative Commons Zero)
|
||||||
* [How to recreate mobs from textures with Blender and Gimp](http://imgur.com/a/Iqg88)
|
- Source: <https://freesound.org/people/Planman/sounds/208111/>
|
||||||
|
|
||||||
|
------------
|
||||||
|
|
||||||
|
Changelog from original Mobs Redo mod:
|
||||||
|
- 1.41- Mob pathfinding has been updated thanks to Elkien3
|
||||||
|
- 1.40- Updated to use newer functions, requires Minetest 0.4.16+ to work.
|
||||||
|
- 1.39- Added 'on_breed', 'on_grown' and 'do_punch' custom functions per mob
|
||||||
|
- 1.38- Better entity checking, nametag setting and on_spawn function added to mob registry, tweaked light damage
|
||||||
|
- 1.37- Added support for Raymoo's CMI (common mob interface) mod: https://forum.minetest.net/viewtopic.php?f=9&t=15448
|
||||||
|
- 1.36- Death check added, if mob dies in fire/lava/with lava pick then drops are cooked
|
||||||
|
- 1.35- Added owner_loyal flag for owned mobs to attack player enemies, also fixed group_attack
|
||||||
|
- 1.34- Added function to fly mob using directional movement (thanks D00Med for flying code)
|
||||||
|
- 1.33- Added functions to mount ride mobs (mobs.attach, mobs.detach, mobs.drive) many thanks to Blert2112
|
||||||
|
- 1.32- Added new spawn check to count specific mobs AND new minetest.conf setting to chance spawn chance and numbers, added ability to protect tamed mobs
|
||||||
|
- 1.31- Added 'attack_animals' and 'specific_attack' flags for custom monster attacks, also 'mob_difficulty' .conf setting to make mobs harder.
|
||||||
|
- 1.30- Added support for invisibility mod (mobs cant attack what they cant see), tweaked and tidied code
|
||||||
|
- 1.29- Split original Mobs Redo into a modpack to make it easier to disable mob sets (animal, monster, npc) or simply use the Api itself for your own mod
|
||||||
|
- 1.28- New damage system added with ability for mob to be immune to weapons or healed by them :)
|
||||||
|
- 1.27- Added new sheep, lava flan and spawn egg textures. New Lava Pick tool smelts what you dig. New atan checking function.
|
||||||
|
- 1.26- Pathfinding feature added thanks to rnd, when monsters attack they become scary smart in finding you :) also, beehive produces honey now :)
|
||||||
|
- 1.25- Mobs no longer spawn within 12 blocks of player or despawn within same range, spawners now have player detection, Code tidy and tweak.
|
||||||
|
- 1.24- Added feature where certain animals run away when punched (runaway = true in mob definition)
|
||||||
|
- 1.23- Added mob spawner block for admin to setup spawners in-game (place and right click to enter settings)
|
||||||
|
- 1.22- Added ability to name tamed animals and npc using nametags, also npc will attack anyone who punches them apart from owner
|
||||||
|
- 1.21- Added some more error checking to reduce serialize.h error and added height checks for falling off cliffs (thanks cmdskp)
|
||||||
|
- 1.20- Error checking added to remove bad mobs, out of map limit mobs and stop serialize.h error
|
||||||
|
- 1.19- Chickens now drop egg items instead of placing the egg, also throwing eggs result in 1/8 chance of spawning chick
|
||||||
|
- 1.18- Added docile_by_day flag so that monsters will not attack automatically during daylight hours unless hit first
|
||||||
|
- 1.17- Added 'dogshoot' attack type, shoots when out of reach, melee attack when in reach, also api tweaks and self.reach added
|
||||||
|
- 1.16- Mobs follow multiple items now, Npc's can breed
|
||||||
|
- 1.15- Added Feeding/Taming/Breeding function, right-click to pick up any sheep with X mark on them and replace with new one to fix compatibility.
|
||||||
|
- 1.14- All .self variables saved in staticdata, Fixed self.health bug
|
||||||
|
- 1.13- Added capture function (thanks blert2112) chance of picking up mob with hand; net; magic lasso, replaced some .x models with newer .b3d one's
|
||||||
|
- 1.12- Added animal ownership so that players cannot steal your tamed animals
|
||||||
|
- 1.11- Added flying mobs (and swimming), fly=true and fly_in="air" or "deafult:water_source" for fishy
|
||||||
|
- 1,10- Footstep removed (use replace), explosion routine added for exploding mobs.
|
||||||
|
- 1.09- reworked breeding routine, added mob rotation value, added footstep feature, added jumping mobs with sounds feature, added magic lasso for picking up animals
|
||||||
|
- 1.08- Mob throwing attack has been rehauled so that they can damage one another, also drops and on_die function added
|
||||||
|
- 1.07- Npc's can now be set to follow player or stand by using self.order and self.owner variables
|
||||||
|
- beta- Npc mob added, kills monsters, attacks player when punched, right click with food to heal or gold lump for drop
|
||||||
|
- 1.06- Changed recovery times after breeding, and time taken to grow up (can be sped up by feeding baby animal)
|
||||||
|
- 1.05- Added ExeterDad's bunny's which can be picked up and tamed with 4 carrots from farming redo or farming_plus, also shears added to get wool from sheep and lastly Jordach/BSD's kitten
|
||||||
|
- 1.04- Added mating for sheep, cows and hogs... feed animals to make horny and hope for a baby which is half size, will grow up quick though :)
|
||||||
|
- 1.03- Added mob drop/replace feature so that chickens can drop eggs, cow/sheep can eat grass/wheat etc.
|
||||||
|
- 1.02- Sheared sheep are remembered and spawn shaven, Warthogs will attack when threatened, Api additions
|
||||||
|
- 1.01- Mobs that suffer fall damage or die in water/lava/sunlight will now drop items
|
||||||
|
- 1.0 - more work on Api so that certain mobs can float in water while some sink like a brick :)
|
||||||
|
- 0.9 - Spawn eggs added for all mobs (admin only, cannot be placed in protected areas)... Api tweaked
|
||||||
|
- 0.8 - Added sounds to monster mobs (thanks Cyberpangolin for the sfx) and also chicken sound
|
||||||
|
- 0.7 - mobs.protected switch added to api.lua, when set to 1 mobs no longer spawn in protected areas, also bug fixes
|
||||||
|
- 0.6 - Api now supports multi-textured mobs, e.g oerkki, dungeon master, rats and chickens have random skins when spawning (sheep fix TODO), also new Honey block
|
||||||
|
- 0.5 - Mobs now float in water, die from falling, and some code improvements
|
||||||
|
- 0.4 - Dungeon Masters and Mese Monsters have much better aim due to shoot_offset, also they can both shoot through nodes that aren't walkable (flowers, grass etc) plus new sheep sound :)
|
||||||
|
- 0.3 - Added LOTT's Spider mob, made Cobwebs, added KPavel's Bee with Honey and Beehives (made texture), Warthogs now have sound and can be tamed, taming of shaved sheep or milked cow with 8 wheat so it will not despawn, many bug fixes :)
|
||||||
|
- 0.2 - Cooking bucket of milk into cheese now returns empty bucket
|
||||||
|
- 0.1 - Initial Release
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
|
|
||||||
Mobs Redo: MineClone 2 Edition
|
Mobs Redo: MineClone 2 Edition
|
||||||
API documentation
|
API documentation
|
||||||
|
|
||||||
IMPORTANT NOTE: This is completely outdated and needs to be rewritten
|
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Welcome to the world of mobs in Minetest and hopefully an easy guide to defining
|
Welcome to the world of mobs in Minetest and hopefully an easy guide to defining
|
||||||
|
@ -643,7 +641,7 @@ mobs:register_mob("mob_horse:horse", {
|
||||||
visual_size = {x = 1.20, y = 1.20},
|
visual_size = {x = 1.20, y = 1.20},
|
||||||
mesh = "mobs_horse.x",
|
mesh = "mobs_horse.x",
|
||||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4},
|
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4},
|
||||||
animation = {
|
animation = {
|
||||||
speed_normal = 15,
|
speed_normal = 15,
|
||||||
speed_run = 30,
|
speed_run = 30,
|
||||||
stand_start = 25,
|
stand_start = 25,
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
function mcl_mobs.mob:ai_step(dtime)
|
|
||||||
--[[
|
|
||||||
if self.has_head then
|
|
||||||
mobs.do_head_logic(self,dtime)
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
|
|
||||||
self:float_step()
|
|
||||||
|
|
||||||
if self.jump_only then
|
|
||||||
jump_state_switch(self, dtime)
|
|
||||||
jump_state_execution(self, dtime)
|
|
||||||
--swimming
|
|
||||||
elseif self.swim then
|
|
||||||
swim_state_switch(self, dtime)
|
|
||||||
swim_state_execution(self, dtime)
|
|
||||||
--flying
|
|
||||||
elseif self.fly then
|
|
||||||
fly_state_switch(self, dtime)
|
|
||||||
fly_state_execution(self, dtime)
|
|
||||||
--regular mobs that walk around
|
|
||||||
else
|
|
||||||
land_state_switch(self, dtime)
|
|
||||||
land_state_execution(self, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
--make it so mobs do not glitch out when walking around/jumping
|
|
||||||
self:swap_auto_step_height_adjust()
|
|
||||||
end
|
|
|
@ -1,74 +0,0 @@
|
||||||
--[[
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
self:look_at(self.attack)
|
|
||||||
|
|
||||||
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 ~= nil 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 ~= nil 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
|
|
||||||
--]]
|
|
|
@ -1,31 +0,0 @@
|
||||||
--[[
|
|
||||||
|
|
||||||
mobs.shoot_projectile_handling = function(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack, collectable, gravity)
|
|
||||||
local obj = mcl_bows.shoot_arrow(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, bow_stack, collectable, gravity, true)
|
|
||||||
|
|
||||||
--play custom shoot sound
|
|
||||||
if shooter ~= nil and shooter.shoot_sound then
|
|
||||||
minetest.sound_play(shooter.shoot_sound, {pos=pos, max_hear_distance=16}, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
return obj
|
|
||||||
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 + mcl_mobs.util.get_eye_height(self.attacking)
|
|
||||||
|
|
||||||
--get direction
|
|
||||||
local dir = vector.direction(pos1,pos2)
|
|
||||||
|
|
||||||
--call internal shoot_arrow function
|
|
||||||
self.shoot_arrow(self,pos1,dir)
|
|
||||||
end
|
|
||||||
]]--
|
|
|
@ -1,72 +0,0 @@
|
||||||
--[[
|
|
||||||
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
|
|
||||||
|
|
||||||
self:look_at(self.attack)
|
|
||||||
|
|
||||||
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
|
|
||||||
self:look_at(self.attack)
|
|
||||||
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
|
|
||||||
]]--
|
|
|
@ -1,52 +0,0 @@
|
||||||
--[[
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
self:look_at(self.attack)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
]]--
|
|
|
@ -1,87 +0,0 @@
|
||||||
--[[
|
|
||||||
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
|
|
||||||
|
|
||||||
self:look_at(self.attack)
|
|
||||||
|
|
||||||
--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
|
|
||||||
--]]
|
|
||||||
--[[
|
|
||||||
--integrate mob punching into collision detection
|
|
||||||
local check_for_attack = false
|
|
||||||
|
|
||||||
if self.attack_type == "punch" and self.hostile and self.attacking then
|
|
||||||
check_for_attack = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if check_for_attack and self.punch_timer <= 0 then
|
|
||||||
if object == self.attacking then
|
|
||||||
mobs.punch_attack(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
]]--
|
|
|
@ -1,22 +0,0 @@
|
||||||
--[[
|
|
||||||
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
|
|
||||||
]]
|
|
|
@ -1,13 +0,0 @@
|
||||||
function mcl_mobs.mob:float_step()
|
|
||||||
local vertical_speed
|
|
||||||
|
|
||||||
if self.node_type ~= self.last_node_type then
|
|
||||||
if self.node_type == "air" then
|
|
||||||
vertical_speed = self.def.float_in_air
|
|
||||||
elseif self.node_type == "water" then
|
|
||||||
vertical_speed = self.def.float_in_water
|
|
||||||
elseif self.node_type == "lava" then
|
|
||||||
vertical_speed = self.def.float_in_lava
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,214 +0,0 @@
|
||||||
|
|
||||||
--[[
|
|
||||||
______ _
|
|
||||||
| ___| |
|
|
||||||
| |_ | |_ _
|
|
||||||
| _| | | | | |
|
|
||||||
| | | | |_| |
|
|
||||||
\_| |_|\__, |
|
|
||||||
__/ |
|
|
||||||
|___/
|
|
||||||
]]--
|
|
||||||
|
|
||||||
--[[
|
|
||||||
-- state switching logic (stand, walk, run, attacks)
|
|
||||||
local fly_state_list_wandering = {"stand", "fly"}
|
|
||||||
|
|
||||||
local function fly_state_switch(self, dtime)
|
|
||||||
|
|
||||||
if self.hostile and self.attacking then
|
|
||||||
self.state = "attack"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.state_timer = self.state_timer - dtime
|
|
||||||
if self.state_timer <= 0 then
|
|
||||||
self.state_timer = math.random(4, 10) + math.random()
|
|
||||||
self.state = fly_state_list_wandering[math.random(1, #fly_state_list_wandering)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--check if a mob needs to turn while flying
|
|
||||||
local function fly_turn_check(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)
|
|
||||||
|
|
||||||
local test_dir = vector.add(pos,dir)
|
|
||||||
|
|
||||||
return minetest.get_item_group(minetest.get_node(test_dir).name, "solid") ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is to swap the built in engine acceleration modifier
|
|
||||||
local function fly_physics_swapper(self, inside_fly_node)
|
|
||||||
|
|
||||||
--should be flyming, gravity is applied, switch to floating
|
|
||||||
if inside_fly_node and self.object:get_acceleration().y ~= 0 then
|
|
||||||
self.object:set_acceleration(vector.new(0, 0, 0))
|
|
||||||
--not be fly, gravity isn't applied, switch to falling
|
|
||||||
elseif not inside_fly_node and self.object:get_acceleration().y == 0 then
|
|
||||||
self.pitch = 0
|
|
||||||
self.object:set_acceleration(vector.new(0, -self.gravity, 0))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local random_pitch_multiplier = {-1, 1}
|
|
||||||
-- states are executed here
|
|
||||||
local function fly_state_execution(self, dtime)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = pos.y + 0.1
|
|
||||||
local current_node = minetest.get_node(pos).name
|
|
||||||
local inside_fly_node = minetest.get_item_group(current_node, "solid") == 0
|
|
||||||
|
|
||||||
local float_now = false
|
|
||||||
--recheck if in water or lava
|
|
||||||
if minetest.get_item_group(current_node, "water") ~= 0 or minetest.get_item_group(current_node, "lava") ~= 0 then
|
|
||||||
inside_fly_node = false
|
|
||||||
float_now = true
|
|
||||||
end
|
|
||||||
|
|
||||||
--turn gravity on or off
|
|
||||||
fly_physics_swapper(self, inside_fly_node)
|
|
||||||
|
|
||||||
--fly properly if inside fly node
|
|
||||||
if inside_fly_node then
|
|
||||||
if self.state == "stand" then
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("stand")
|
|
||||||
|
|
||||||
self:set_fly_velocity(0)
|
|
||||||
|
|
||||||
if self.tilt_fly then
|
|
||||||
self:set_static_pitch()
|
|
||||||
end
|
|
||||||
|
|
||||||
self:lock_yaw()
|
|
||||||
|
|
||||||
elseif self.state == "fly" then
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("walk")
|
|
||||||
|
|
||||||
--do a quick turn to make mob continuously move
|
|
||||||
--if in a bird cage or something
|
|
||||||
if fly_turn_check(self, dtime) then
|
|
||||||
quick_rotate(self, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.tilt_fly then
|
|
||||||
self:set_dynamic_pitch()
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_fly_velocity(self.walk_velocity)
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
self:movement_rotation_lock()
|
|
||||||
|
|
||||||
elseif self.state == "attack" then
|
|
||||||
|
|
||||||
--execute mob attack type
|
|
||||||
--if self.attack_type == "explode" then
|
|
||||||
|
|
||||||
--mobs.explode_attack_fly(self, dtime)
|
|
||||||
|
|
||||||
--elseif self.attack_type == "punch" then
|
|
||||||
|
|
||||||
--mobs.punch_attack_fly(self,dtime)
|
|
||||||
|
|
||||||
if self.attack_type == "projectile" then
|
|
||||||
|
|
||||||
self:projectile_attack_fly(dtime)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
--make the mob float
|
|
||||||
if self.floats and float_now then
|
|
||||||
self:set_velocity(0)
|
|
||||||
|
|
||||||
self:float()
|
|
||||||
|
|
||||||
if self.tilt_fly then
|
|
||||||
self:set_static_pitch()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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
|
|
||||||
]]--
|
|
|
@ -1,31 +0,0 @@
|
||||||
function mcl_mobs.mob:check_following()
|
|
||||||
--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
|
|
|
@ -1,169 +0,0 @@
|
||||||
--[[
|
|
||||||
|
|
||||||
--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
|
|
||||||
|
|
||||||
--check if a mob needs to turn while jumping
|
|
||||||
local function jump_turn_check(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)
|
|
||||||
|
|
||||||
local test_dir = vector.add(pos,dir)
|
|
||||||
|
|
||||||
return minetest.get_item_group(minetest.get_node(test_dir).name, "solid") ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- state switching logic (stand, jump, run, attacks)
|
|
||||||
local jump_state_list_wandering = {"stand", "jump"}
|
|
||||||
|
|
||||||
local function jump_state_switch(self, dtime)
|
|
||||||
self.state_timer = self.state_timer - dtime
|
|
||||||
if self.state_timer <= 0 then
|
|
||||||
self.state_timer = math.random(4, 10) + math.random()
|
|
||||||
self.state = jump_state_list_wandering[math.random(1, #jump_state_list_wandering)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- states are executed here
|
|
||||||
local function jump_state_execution(self, dtime)
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local collisionbox = self.object:get_properties().collisionbox
|
|
||||||
--get the center of the mob
|
|
||||||
pos.y = pos.y + (collisionbox[2] + collisionbox[5] / 2)
|
|
||||||
local current_node = minetest.get_node(pos).name
|
|
||||||
|
|
||||||
local float_now = false
|
|
||||||
|
|
||||||
--recheck if in water or lava
|
|
||||||
if minetest.get_item_group(current_node, "water") ~= 0 or minetest.get_item_group(current_node, "lava") ~= 0 then
|
|
||||||
float_now = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.state == "stand" then
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("stand")
|
|
||||||
|
|
||||||
--set the velocity of the mob
|
|
||||||
self:set_velocity(0)
|
|
||||||
|
|
||||||
self:lock_yaw()
|
|
||||||
|
|
||||||
elseif self.state == "jump" then
|
|
||||||
|
|
||||||
self.walk_timer = self.walk_timer - dtime
|
|
||||||
|
|
||||||
--reset the jump timer
|
|
||||||
if self.walk_timer <= 0 then
|
|
||||||
|
|
||||||
--re-randomize the jump timer
|
|
||||||
self.walk_timer = math.random(1, 6) + math.random()
|
|
||||||
|
|
||||||
--set the mob into a random direction
|
|
||||||
self.yaw = (math.random() * (math.pi * 2))
|
|
||||||
end
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("walk")
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
self:movement_rotation_lock()
|
|
||||||
|
|
||||||
--jumping mobs are more loosey goosey
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
quick_rotate(self, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
--only move forward if path is clear
|
|
||||||
self:jump_move(self.walk_velocity)
|
|
||||||
|
|
||||||
elseif self.state == "run" then
|
|
||||||
|
|
||||||
print("run")
|
|
||||||
|
|
||||||
elseif self.state == "attack" then
|
|
||||||
|
|
||||||
print("attack")
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if float_now then
|
|
||||||
self:float()
|
|
||||||
end
|
|
||||||
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
|
|
||||||
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
|
|
||||||
|
|
||||||
]]--
|
|
|
@ -1,436 +0,0 @@
|
||||||
|
|
||||||
--[[
|
|
||||||
_ _
|
|
||||||
| | | |
|
|
||||||
| | __ _ _ __ __| |
|
|
||||||
| | / _` | '_ \ / _` |
|
|
||||||
| |___| (_| | | | | (_| |
|
|
||||||
\_____/\__,_|_| |_|\__,_|
|
|
||||||
]]--
|
|
||||||
|
|
||||||
--[[
|
|
||||||
--this is basically reverse jump_check
|
|
||||||
local function cliff_check(self, dtime)
|
|
||||||
--mobs will flip out if they are falling without this
|
|
||||||
if self.object:get_velocity().y ~= 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local dir = minetest.yaw_to_dir(self.yaw)
|
|
||||||
local collisionbox = self.properties.collisionbox
|
|
||||||
local radius = collisionbox[4] + 0.5
|
|
||||||
|
|
||||||
dir = vector.multiply(dir, radius)
|
|
||||||
|
|
||||||
local free_fall, blocker = minetest.line_of_sight(
|
|
||||||
{x = pos.x + dir.x, y = pos.y, z = pos.z + dir.z},
|
|
||||||
{x = pos.x + dir.x, y = pos.y - self.def.fear_height, z = pos.z + dir.z})
|
|
||||||
|
|
||||||
return free_fall
|
|
||||||
end
|
|
||||||
|
|
||||||
-- state switching logic (stand, walk, run, attacks)
|
|
||||||
local land_state_list_wandering = {"stand", "walk"}
|
|
||||||
|
|
||||||
local function land_state_switch(self, dtime)
|
|
||||||
|
|
||||||
--do math before sure not attacking, following, or running away so continue
|
|
||||||
--doing random walking for mobs if all states are not met
|
|
||||||
self.state_timer = self.state_timer - dtime
|
|
||||||
|
|
||||||
--only run away
|
|
||||||
if self.def.skittish and self.state == "run" then
|
|
||||||
self.run_timer = self.run_timer - dtime
|
|
||||||
if self.run_timer > 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
--continue
|
|
||||||
end
|
|
||||||
|
|
||||||
--ignore everything else if breeding
|
|
||||||
if self.breed_lookout_timer and self.breed_lookout_timer > 0 then
|
|
||||||
self.state = "breed"
|
|
||||||
return
|
|
||||||
--reset the state timer to get the mob out of
|
|
||||||
--the breed state
|
|
||||||
elseif self.state == "breed" then
|
|
||||||
self.state_timer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--ignore everything else if following
|
|
||||||
if self:check_following() and self.breed_lookout_timer == 0 and self.breed_timer == 0 then
|
|
||||||
self.state = "follow"
|
|
||||||
return
|
|
||||||
--reset the state timer to get the mob out of
|
|
||||||
--the follow state - not the cleanest option
|
|
||||||
--but the easiest
|
|
||||||
elseif self.state == "follow" then
|
|
||||||
self.state_timer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--only attack
|
|
||||||
if self.def.hostile and self.attacking then
|
|
||||||
self.state = "attack"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--if finally reached here then do random wander
|
|
||||||
if self.state_timer <= 0 then
|
|
||||||
self.state_timer = math.random(4, 10) + math.random()
|
|
||||||
self.state = land_state_list_wandering[math.random(1, #land_state_list_wandering)]
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- states are executed here
|
|
||||||
local function land_state_execution(self, dtime)
|
|
||||||
|
|
||||||
--[[ -- this is a debug which shows the timer and makes mobs breed 100 times faster
|
|
||||||
print(self.breed_timer)
|
|
||||||
if self.breed_timer > 0 then
|
|
||||||
self.breed_timer = self.breed_timer - (dtime * 100)
|
|
||||||
if self.breed_timer <= 0 then
|
|
||||||
self.breed_timer = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
] ]--
|
|
||||||
|
|
||||||
--timer to time out looking for mate
|
|
||||||
if self.breed_lookout_timer > 0 then
|
|
||||||
self.breed_lookout_timer = self.breed_lookout_timer - dtime
|
|
||||||
--looking for mate failed
|
|
||||||
if self.breed_lookout_timer < 0 then
|
|
||||||
self.breed_lookout_timer = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--cool off after breeding
|
|
||||||
if self.breed_timer > 0 then
|
|
||||||
self.breed_timer = self.breed_timer - dtime
|
|
||||||
--do this to skip the first check, using as switch
|
|
||||||
if self.breed_timer <= 0 then
|
|
||||||
self.breed_timer = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local collisionbox = self.properties.collisionbox
|
|
||||||
--get the center of the mob
|
|
||||||
pos.y = pos.y + (collisionbox[2] + collisionbox[5] / 2)
|
|
||||||
local current_node = minetest.get_node(pos).name
|
|
||||||
local float_now = false
|
|
||||||
|
|
||||||
--recheck if in water or lava
|
|
||||||
if minetest.get_item_group(current_node, "water") ~= 0 or minetest.get_item_group(current_node, "lava") ~= 0 then
|
|
||||||
float_now = true
|
|
||||||
end
|
|
||||||
|
|
||||||
--calculate fall damage
|
|
||||||
if self.fall_damage then
|
|
||||||
self:calculate_fall_damage()
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.state == "stand" then
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("stand")
|
|
||||||
|
|
||||||
--set the velocity of the mob
|
|
||||||
self:set_velocity(0)
|
|
||||||
|
|
||||||
--animation fixes for explosive mobs
|
|
||||||
if self.attack_type == "explode" then
|
|
||||||
self:reverse_explosion_animation(dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:lock_yaw()
|
|
||||||
elseif self.state == "follow" then
|
|
||||||
|
|
||||||
--always look at players
|
|
||||||
self:look_at(self.following_person)
|
|
||||||
|
|
||||||
--check distance
|
|
||||||
local distance_from_follow_person = vector.distance(self.object:get_pos(), self.following_person:get_pos())
|
|
||||||
local distance_2d = mobs.get_2d_distance(self.object:get_pos(), self.following_person:get_pos())
|
|
||||||
|
|
||||||
--don't push the player if too close
|
|
||||||
--don't spin around randomly
|
|
||||||
if self.follow_distance < distance_from_follow_person and self.minimum_follow_distance < distance_2d then
|
|
||||||
self:set_animation("run")
|
|
||||||
self:set_velocity(self.run_velocity)
|
|
||||||
|
|
||||||
if self:jump_check() == 1 then
|
|
||||||
self:jump(self)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:set_mob_animation("stand")
|
|
||||||
self:set_velocity(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif self.state == "walk" then
|
|
||||||
|
|
||||||
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))
|
|
||||||
end
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("walk")
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
self:movement_rotation_lock()
|
|
||||||
|
|
||||||
--check for nodes to jump over
|
|
||||||
local node_in_front_of = self:jump_check()
|
|
||||||
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
|
|
||||||
self:jump()
|
|
||||||
|
|
||||||
--turn if on the edge of cliff
|
|
||||||
--(this is written like this because unlike
|
|
||||||
--jump_check which simply tells the mob to jump
|
|
||||||
--this requires a mob to turn, removing the
|
|
||||||
--ease of a full implementation for it in a single
|
|
||||||
--function)
|
|
||||||
elseif node_in_front_of == 2 or (self.fear_height ~= 0 and cliff_check(self, dtime)) then
|
|
||||||
--turn 45 degrees if so
|
|
||||||
quick_rotate(self,dtime)
|
|
||||||
--stop the mob so it doesn't fall off
|
|
||||||
self:set_velocity(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
--only move forward if path is clear
|
|
||||||
if node_in_front_of == 0 or node_in_front_of == 1 then
|
|
||||||
--set the velocity of the mob
|
|
||||||
self:set_velocity(self.walk_velocity)
|
|
||||||
end
|
|
||||||
|
|
||||||
--animation fixes for explosive mobs
|
|
||||||
if self.attack_type == "explode" then
|
|
||||||
self:reverse_explosion_animation(dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif self.state == "run" then
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("run")
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
self:movement_rotation_lock()
|
|
||||||
|
|
||||||
--check for nodes to jump over
|
|
||||||
local node_in_front_of = self:jump_check()
|
|
||||||
|
|
||||||
if node_in_front_of == 1 then
|
|
||||||
|
|
||||||
self:jump()
|
|
||||||
|
|
||||||
--turn if on the edge of cliff
|
|
||||||
--(this is written like this because unlike
|
|
||||||
--jump_check which simply tells the mob to jump
|
|
||||||
--this requires a mob to turn, removing the
|
|
||||||
--ease of a full implementation for it in a single
|
|
||||||
--function)
|
|
||||||
elseif node_in_front_of == 2 or (self.fear_height ~= 0 and cliff_check(self, dtime)) then
|
|
||||||
--turn 45 degrees if so
|
|
||||||
quick_rotate(self, dtime)
|
|
||||||
--stop the mob so it doesn't fall off
|
|
||||||
self:set_velocity(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
--only move forward if path is clear
|
|
||||||
if node_in_front_of == 0 or node_in_front_of == 1 then
|
|
||||||
--set the velocity of the mob
|
|
||||||
self:set_velocity(self.run_velocity)
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif self.state == "attack" then
|
|
||||||
|
|
||||||
--execute mob attack type
|
|
||||||
if self.attack_type == "explode" then
|
|
||||||
|
|
||||||
self:explode_attack_walk(dtime)
|
|
||||||
|
|
||||||
elseif self.attack_type == "punch" then
|
|
||||||
|
|
||||||
self:punch_attack_walk(dtime)
|
|
||||||
|
|
||||||
elseif self.attack_type == "projectile" then
|
|
||||||
|
|
||||||
self:projectile_attack_walk(dtime)
|
|
||||||
|
|
||||||
end
|
|
||||||
elseif self.state == "breed" then
|
|
||||||
|
|
||||||
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",
|
|
||||||
})
|
|
||||||
|
|
||||||
local mate = self:look_for_mate()
|
|
||||||
|
|
||||||
--found a mate
|
|
||||||
if mate then
|
|
||||||
self:look_at(mate)
|
|
||||||
self:set_velocity(self.walk_velocity)
|
|
||||||
|
|
||||||
--smoosh together basically
|
|
||||||
if vector.distance(self.object:get_pos(), mate:get_pos()) <= self.breed_distance then
|
|
||||||
self:set_animation("stand")
|
|
||||||
if self.special_breed_timer == 0 then
|
|
||||||
self.special_breed_timer = 2 --breeding takes 2 seconds
|
|
||||||
end
|
|
||||||
|
|
||||||
self.special_breed_timer = self.special_breed_timer - dtime
|
|
||||||
if self.special_breed_timer <= 0 then
|
|
||||||
|
|
||||||
--pop a baby out, it's a miracle!
|
|
||||||
local baby_pos = vector.divide(vector.add(self.object:get_pos(), mate:get_pos()), 2)
|
|
||||||
local baby_mob = minetest.add_entity(pos, self.name, minetest.serialize({baby = true, grow_up_timer = self.grow_up_goal, bred = true}))
|
|
||||||
|
|
||||||
self:play_sound_specific("item_drop_pickup")
|
|
||||||
|
|
||||||
self.special_breed_timer = 0
|
|
||||||
self.breed_lookout_timer = 0
|
|
||||||
self.breed_timer = self.breed_timer_cooloff
|
|
||||||
|
|
||||||
local mate_entity = mate:get_luaentity()
|
|
||||||
mate_entity.special_breed_timer = 0
|
|
||||||
mate_entity.breed_lookout_timer = 0
|
|
||||||
mate_entity.breed_timer = self.breed_timer_cooloff -- can reuse because it's the same mob
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:set_animation("walk")
|
|
||||||
end
|
|
||||||
--couldn't find a mate, just stand there until the player pushes it towards one
|
|
||||||
--or the timer runs out
|
|
||||||
else
|
|
||||||
self:set_mob_animation("stand")
|
|
||||||
self:set_velocity(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if float_now then
|
|
||||||
self:float()
|
|
||||||
else
|
|
||||||
local acceleration = self.object:get_acceleration()
|
|
||||||
if acceleration and acceleration.y == 0 then
|
|
||||||
self.object:set_acceleration(vector.new(0, -self.gravity, 0))
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
||||||
]]--
|
|
|
@ -1,9 +0,0 @@
|
||||||
function mcl_mobs.mob:swap_auto_step_height_adjust()
|
|
||||||
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
|
|
|
@ -1,206 +0,0 @@
|
||||||
|
|
||||||
--[[
|
|
||||||
_____ _
|
|
||||||
/ ___| (_)
|
|
||||||
\ `--.__ ___ _ __ ___
|
|
||||||
`--. \ \ /\ / / | '_ ` _ \
|
|
||||||
/\__/ /\ V V /| | | | | | |
|
|
||||||
\____/ \_/\_/ |_|_| |_| |_|
|
|
||||||
]]--
|
|
||||||
|
|
||||||
--[[
|
|
||||||
|
|
||||||
-- state switching logic (stand, walk, run, attacks)
|
|
||||||
local swim_state_list_wandering = {"stand", "swim"}
|
|
||||||
|
|
||||||
local function swim_state_switch(self, dtime)
|
|
||||||
self.state_timer = self.state_timer - dtime
|
|
||||||
if self.state_timer <= 0 then
|
|
||||||
self.state_timer = math.random(4,10) + math.random()
|
|
||||||
self.state = swim_state_list_wandering[math.random(1, #swim_state_list_wandering)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--check if a mob needs to turn while swimming
|
|
||||||
local swim_turn_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)
|
|
||||||
|
|
||||||
local test_dir = vector.add(pos,dir)
|
|
||||||
|
|
||||||
return minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is to swap the built in engine acceleration modifier
|
|
||||||
local function swim_physics_swapper(self, inside_swim_node)
|
|
||||||
|
|
||||||
--should be swimming, gravity is applied, switch to floating
|
|
||||||
if inside_swim_node and self.object:get_acceleration().y ~= 0 then
|
|
||||||
self.object:set_acceleration(vector.new(0, 0, 0))
|
|
||||||
--not be swim, gravity isn't applied, switch to falling
|
|
||||||
elseif not inside_swim_node and self.object:get_acceleration().y == 0 then
|
|
||||||
self.pitch = 0
|
|
||||||
self.object:set_acceleration(vector.new(0, -self.gravity, 0))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local random_pitch_multiplier = {-1,1}
|
|
||||||
-- states are executed here
|
|
||||||
local function swim_state_execution(self, dtime)
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
pos.y = pos.y + self.object:get_properties().collisionbox[5]
|
|
||||||
local current_node = minetest_get_node(pos).name
|
|
||||||
local inside_swim_node = false
|
|
||||||
|
|
||||||
--quick scan everything to see if inside swim node
|
|
||||||
for _,id in pairs(self.swim_in) do
|
|
||||||
if id == current_node then
|
|
||||||
inside_swim_node = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--turn gravity on or off
|
|
||||||
swim_physics_swapper(self, inside_swim_node)
|
|
||||||
|
|
||||||
--swim properly if inside swim node
|
|
||||||
if inside_swim_node then
|
|
||||||
|
|
||||||
if self.state == "stand" then
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("stand")
|
|
||||||
|
|
||||||
self:set_swim_velocity(0)
|
|
||||||
|
|
||||||
if self.tilt_swim then
|
|
||||||
self:set_static_pitch()
|
|
||||||
end
|
|
||||||
|
|
||||||
self:lock_yaw()
|
|
||||||
|
|
||||||
elseif self.state == "swim" then
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
--do animation
|
|
||||||
self:set_animation("walk")
|
|
||||||
|
|
||||||
--do a quick turn to make mob continuously move
|
|
||||||
--if in a fish tank or something
|
|
||||||
if swim_turn_check(self, dtime) then
|
|
||||||
quick_rotate(self, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_swim_velocity(self.walk_velocity)
|
|
||||||
|
|
||||||
--only enable tilt swimming if enabled
|
|
||||||
if self.tilt_swim then
|
|
||||||
self:set_dynamic_pitch()
|
|
||||||
end
|
|
||||||
|
|
||||||
--enable rotation locking
|
|
||||||
self:movement_rotation_lock()
|
|
||||||
end
|
|
||||||
--flop around if not inside swim node
|
|
||||||
else
|
|
||||||
--do animation
|
|
||||||
self:set_mob_animation("stand")
|
|
||||||
|
|
||||||
self:flop()
|
|
||||||
|
|
||||||
if self.tilt_swim then
|
|
||||||
self:set_static_pitch()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--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
|
|
||||||
]]--
|
|
|
@ -1,205 +0,0 @@
|
||||||
--[[
|
|
||||||
Implementation of the Minecraft 1.16 Anger System (copied from https://www.minecraft.net/ru-ru/article/nether-update-java, with modifications):
|
|
||||||
|
|
||||||
Forgive dead players
|
|
||||||
|
|
||||||
If this gamerule is disabled, then angered mobs will stay angry even if the targeted player dies
|
|
||||||
If both forgiveDeadPlayers and universalAnger are enabled, an angered neutral mob will stop being angry when their target dies. They won't seek any new targets after that
|
|
||||||
|
|
||||||
Neutral mob anger
|
|
||||||
|
|
||||||
When hurt by a player, the neutral mob will target that player and try to kill it
|
|
||||||
The mob will stay angry until the player is dead or out of sight for a while
|
|
||||||
Anger is persistent, so a player can't escape by temporarily logging out or switching dimension
|
|
||||||
If a targeted player dies near the angered mob, it will stop being angry (unless forgiveDeadPlayers is disabled)
|
|
||||||
Neutral mobs also get angry at other mobs who hurt them. However, that anger is not persistent
|
|
||||||
Angered neutral mobs will only attack the offending player, not innocent bystanders
|
|
||||||
Some mobs spread anger (wolf, Zombie Pigman). If a player attacks one, all nearby mobs of the same type will get angry at that player
|
|
||||||
|
|
||||||
Universal anger
|
|
||||||
|
|
||||||
Universal anger is basically guilt by association. A neutral mob attacked by players will be angry at players in general, regardless of who attacked them. More specifically:
|
|
||||||
|
|
||||||
A neutral mob attacked by a player will target the nearest player, even if that player wasn't the attacker
|
|
||||||
Every time the neutral mob is hit by a player it will update its attack target to the nearest player
|
|
||||||
Players can use this to make neutral mobs attack other players. Who would ever do something that devious?
|
|
||||||
Universal anger does not apply when a neutral mob is attacked by another mob - only when it is attacked by a player
|
|
||||||
Universal anger is persistent. The angered mob will stay angry even if the player logs out and logs in, or jumps through a portal and back
|
|
||||||
mcl_mobs.mobs that spread anger will also spread universal anger. So if a player attacks a Zombie Pigman, all other Zombie Pigmen within sight will be universally angry and attack their nearest player
|
|
||||||
An angered neutral mob will stop being angry if it can't see any eligible target for a while
|
|
||||||
--]]
|
|
||||||
|
|
||||||
function mcl_mobs.mob:anger_on_staticdata()
|
|
||||||
if self.anger_persistent then
|
|
||||||
self.data.anger_target_name = self.anger_target_name
|
|
||||||
self.data.anger_hurt_timestamp = self.anger_hurt_timestamp
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:anger_on_activate()
|
|
||||||
if self.data.anger_target_name then
|
|
||||||
self.anger = true
|
|
||||||
self.anger_persistent = true
|
|
||||||
self.anger_target_name = self.data.anger_target_name
|
|
||||||
self.anger_hurt_timestamp = self.data.anger_hurt_timestamp
|
|
||||||
|
|
||||||
self.data.anger_target_name = nil
|
|
||||||
self.data.anger_hurt_timestamp = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_anger_attack_target()
|
|
||||||
if not self.anger then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if the mob is universally angry and the current target is unreachable, search a new one
|
|
||||||
local search_new_target = self.anger_universal and (
|
|
||||||
not self.anger_current_target -- does a current target even exist?
|
|
||||||
or not self.anger_current_target:is_player() -- universal anger only applies to players, so this is just a check whether the ObjectRef is still valid
|
|
||||||
or not self:can_see(self.anger_current_target) -- dimension check is not done since it is covered by the view distance check
|
|
||||||
)
|
|
||||||
|
|
||||||
if search_new_target then
|
|
||||||
self.anger_current_target = self:get_player_in_sight()
|
|
||||||
if self.anger_current_target then
|
|
||||||
self:debug("found new universal anger target: " .. self.anger_current_target:get_player_name())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if the anger is not persistant (e.g. enderman provocation, angry at mobs)
|
|
||||||
if not self.anger_persistent then
|
|
||||||
-- calm down if either the target ObjectRef is invalid or changed its dimension
|
|
||||||
if not self.anger_target:is_player() and not self.anger_target:get_luaentity() or not self:same_dimension_as(self.anger_target) then
|
|
||||||
self:debug("non persistent anger target unreachable, calming down" .. (self.anger_target_name and "[anger_target_name = " .. self.anger_target_name .. "]" or ""))
|
|
||||||
return nil, true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if this is a player, special rules apply (don't use anger_target:is_player() since the player may have logged out so it's not a valid check)
|
|
||||||
if self.anger_target_name then
|
|
||||||
-- check if player logged out (if the player had already logged out in the last step anger_target will be nil, else it will be a dangling ObjectRef that can be validated by calling is_player())
|
|
||||||
if not self.anger_target or not self.anger_target:is_player() then
|
|
||||||
if self.anger_target then
|
|
||||||
self:debug("anger target logged out: " .. self.anger_target_name)
|
|
||||||
end
|
|
||||||
-- in case the player relogged (if the player did not relog anger_target becomes nil and this is run in the next step as well)
|
|
||||||
self.anger_target = minetest.get_player_by_name(self.anger_target_name)
|
|
||||||
if self.anger_target then
|
|
||||||
self:debug("anger target relogged: " .. self.anger_target_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- if forgiveDeadPlayers is true (it is by default)
|
|
||||||
if self.anger_target and minetest.settings:get_bool("mclForgiveDeadPlayers", true) then
|
|
||||||
-- check death timestamp of player and forget about the player in case it was killed
|
|
||||||
if self.anger_target:get_meta():get_int("mcl_mobs:last_death") >= self.hurt_timestamp then
|
|
||||||
self:debug("forgave " .. self.anger_target_name .. " since they died")
|
|
||||||
return nil, true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- the actual target we want to attack
|
|
||||||
local target
|
|
||||||
|
|
||||||
-- note: dont use a selfmade ternary expression (v = x and a or b, in other languages that have real ternary expressions this would be v = x ? a : b) here
|
|
||||||
-- because anger_current_target might be nil and we don't care about the original player if they are not in the area (anger_current_target is only nil if there is absolutely no player in the area)
|
|
||||||
if self.anger_universal then
|
|
||||||
target = self.anger_current_target
|
|
||||||
else
|
|
||||||
target = self.anger_target
|
|
||||||
-- if the target is out of reach, it counts as not existant in terms of the reset timer
|
|
||||||
if target and not self:can_see(target) then
|
|
||||||
target = nil
|
|
||||||
if not self.anger_calm_timer then
|
|
||||||
self:debug("cannot see anger target " .. self.anger_target_name .. " anymore")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not target and not self.anger_calm_timer then
|
|
||||||
-- start to calm down if noone to attack in sight
|
|
||||||
self:debug("anger target " .. (self.anger_universal and self.anger_target_name .. " " or "") .. "is not reachable anymore, starting calm timer")
|
|
||||||
self.anger_calm_timer = mcl_mobs.const.calm_down_timer
|
|
||||||
elseif target and self.anger_calm_timer then
|
|
||||||
-- stop calming down if there is someone in sight again
|
|
||||||
self:debug("anger target " .. (self.anger_universal and self.anger_target_name .. " " or "") .. "is reachable again, resetting calm timer")
|
|
||||||
self.anger_calm_timer = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if target then
|
|
||||||
return target
|
|
||||||
end
|
|
||||||
|
|
||||||
-- wait for the mob to calm down if there is no target, then clear variables
|
|
||||||
-- do_timer returns true if the timer has not elapsed yet
|
|
||||||
if not self:do_timer("anger_calm") then
|
|
||||||
self:debug("calmed down")
|
|
||||||
self.anger = nil
|
|
||||||
self.anger_target = nil
|
|
||||||
self.anger_target_name = nil
|
|
||||||
self.anger_universal = nil
|
|
||||||
self.anger_current_target = nil
|
|
||||||
self.anger_persistent = nil
|
|
||||||
self.anger_hurt_timestamp = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_angry_raw(target, target_name, timestamp, universal, persistent)
|
|
||||||
if self.owner == target_name then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
self:debug("getting angry at " .. (target_name or tostring(target))
|
|
||||||
.. " persistent: " .. (persistent and "yes" or "no")
|
|
||||||
.. " universal: " .. (universal and "yes" or "no")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.anger = true
|
|
||||||
self.anger_target = target -- even if universally angry, still remember the actual cause to apply forgiveDeadPlayers properly. anger_current_target is used to get the actual attack target
|
|
||||||
self.anger_target_name = target_name -- remember player name separately to work around the ObjectRef becoming invalid when the player logs out
|
|
||||||
-- the persistent field is used to optionally forget about the player when they log out or change dimension (e.g. provoking endermen by looking at them) or for when attacked by another mob
|
|
||||||
self.anger_persistent = persistent
|
|
||||||
self.anger_hurt_timestamp = timestamp
|
|
||||||
|
|
||||||
if universal then
|
|
||||||
self.anger_universal = true
|
|
||||||
-- set this to nil because then universal anger is enabled every mob will look for a new target everytime a provocation happens
|
|
||||||
self.anger_current_target = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_angry(target)
|
|
||||||
local timestamp = os.time()
|
|
||||||
local is_player = target:is_player()
|
|
||||||
|
|
||||||
local universal = is_player and minetest.settings:get_bool("mclUniversalAnger")
|
|
||||||
local target_name = is_player and target:get_player_name() or ""
|
|
||||||
local persistent = not is_player
|
|
||||||
|
|
||||||
self:debug("provoked by " .. (target_name or tostring(target))
|
|
||||||
.. " persistent: " .. (persistent and "yes" or "no")
|
|
||||||
.. " universal: " .. (universal and "yes" or "no")
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self:get_angry_raw(target, target_name, timestamp, universal, persistent) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.def.group_attack then
|
|
||||||
for _, obj in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), self.def.view_range)) do
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if luaentity and self.def.group_attack[luaentity.name] then
|
|
||||||
luaentity:get_angry_raw(target, target_name, timestamp, universal_anger, persistent)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_dieplayer(function(player)
|
|
||||||
player:get_meta():set_int("mcl_mobs:last_death", os.time())
|
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
--[[
|
|
||||||
function mcl_mobs.register_arrow(name, def)
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
--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
|
|
||||||
]]--
|
|
|
@ -1,26 +0,0 @@
|
||||||
function mcl_mobs.mob:baby_step()
|
|
||||||
if not self:do_timer("grow_up", true) then
|
|
||||||
self:baby_grow_up()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:baby_grow_up()
|
|
||||||
self:debug("growing up")
|
|
||||||
self.data.baby = nil
|
|
||||||
|
|
||||||
if self.def.on_grow_up then
|
|
||||||
self.def.on_grow_up(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:update_textures()
|
|
||||||
self:update_visual_size()
|
|
||||||
self:update_eye_height()
|
|
||||||
self:update_collisionbox()
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:boost()
|
|
||||||
self:debug("grow up boost")
|
|
||||||
self.data.grow_up_timer = self.data.grow_up_timer - self.data.grow_up_timer * mcl_mobs.const.grow_up_boost
|
|
||||||
-- ToDo: check whether the Minecraft wiki terminology is right about 10% or whether they actually mean 10 percent points
|
|
||||||
-- (10 percent would be 0.1 * self.data.grow_up_timer, 10 percent points would be 0.1 * self.def.grow_up_goal)
|
|
||||||
end
|
|
|
@ -1,27 +0,0 @@
|
||||||
function mcl_mobs.mob:start_breed_giveup_timer()
|
|
||||||
self.breed_giveup_timer = mcl_mobs.const.breed_giveup_timer
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:breeding_on_activate()
|
|
||||||
if self.data.breeding then
|
|
||||||
self:start_breed_giveup_timer()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:init_breeding()
|
|
||||||
self:debug("initializing breeding")
|
|
||||||
self.data.bred = true
|
|
||||||
self.data.breeding = true
|
|
||||||
self:start_breed_giveup_timer()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- looking for hot singles in the area
|
|
||||||
function mcl_mobs.mob:find_mate()
|
|
||||||
return self:get_near_object(self.def.view_range, function(self, obj)
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
return luaentity -- dont fook with hoomans
|
|
||||||
and luaentity.name == self.name -- this is MineClone, not Animal Crossing
|
|
||||||
and not luaentity.data.bred -- no polygamy pls
|
|
||||||
and not luaentity.data.baby -- no pedophila pls
|
|
||||||
end)
|
|
||||||
end
|
|
|
@ -1,115 +0,0 @@
|
||||||
function mcl_mobs.mob:debug(msg)
|
|
||||||
if mcl_mobs.const.debug then
|
|
||||||
minetest.log("[mcl_mobs] " .. tostring(self.object) .. "[" .. self.name .. "]: " .. msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:do_timer(name, persistent)
|
|
||||||
local k = name .. "_timer"
|
|
||||||
local t = persistent and self.data or self
|
|
||||||
local v = t[k]
|
|
||||||
|
|
||||||
if not v then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local r = true
|
|
||||||
|
|
||||||
v = v - self.dtime
|
|
||||||
if v <= 0 then
|
|
||||||
self:debug(k .. " elapsed")
|
|
||||||
v = nil
|
|
||||||
r = false
|
|
||||||
end
|
|
||||||
|
|
||||||
t[k] = v
|
|
||||||
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:same_dimension_as(obj)
|
|
||||||
return mcl_worlds.pos_to_dimension(obj:get_pos()) == mcl_worlds.pos_to_dimension(self.object:get_pos())
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:can_see(obj)
|
|
||||||
return vector.distance(obj:get_pos(), self.object:get_pos()) <= self.def.view_range
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_player_in_sight()
|
|
||||||
return self:get_near_player(self.def.view_range)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:is_player_near(radius)
|
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
|
||||||
if vector.distance(pos, player:get_pos()) < radius then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_near_player(radius, condition)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local eye_pos = vector.new(pos.x, pos.y + self.eye_height, pos.z)
|
|
||||||
|
|
||||||
local nearest_player
|
|
||||||
local nearest_distance = radius -- this is very big brain right there, I feel genious
|
|
||||||
|
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
|
||||||
if player:get_hp() > 0 then
|
|
||||||
local player_pos = obj:get_pos()
|
|
||||||
if vector.distance(pos, player_pos) < nearest_distance and (not condition or condition(self, player)) and minetest.line_of_sight(eye_pos, vector.new(player_pos.x, player_pos.y + player:get_properties().eye_height, player_pos.z)) then
|
|
||||||
nearest_player = player
|
|
||||||
nearest_distance = distance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nearest_player
|
|
||||||
end
|
|
||||||
|
|
||||||
-- I know this repeats some things from the get_near_player function but things need to be optimized so these 2 functions actually differ (believe me, even tho it looks ugly, it makes sense)
|
|
||||||
function mcl_mobs.mob:get_near_object(radius, condition)
|
|
||||||
local eye_pos = self.object:get_pos()
|
|
||||||
eye_pos.y = eye_pos.y + self.eye_height
|
|
||||||
|
|
||||||
for _, obj in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
|
|
||||||
if obj ~= self.object and mcl_util.get_hp(obj) > 0 and (not condition or condition(self, obj)) then
|
|
||||||
local obj_eye_pos = obj:get_pos()
|
|
||||||
obj_eye_pos.y = obj_eye_pos.y + mcl_mobs.util.get_eye_height(obj)
|
|
||||||
if minetest.line_of_sight(eye_pos, obj_eye_pos) then
|
|
||||||
return obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this function gets a definition field DYNAMICALLY (if the field is a function, call it and return the result, else return the field directly)
|
|
||||||
function mcl_mobs.mob:evaluate(key, ...)
|
|
||||||
local value = self.def[key]
|
|
||||||
|
|
||||||
if value then
|
|
||||||
if type(value) == "function" then
|
|
||||||
value = value(self, ...)
|
|
||||||
end
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
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 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
|
|
||||||
]]--
|
|
|
@ -1,80 +0,0 @@
|
||||||
function mcl_mobs.mob:deal_damage(damage, reason)
|
|
||||||
if self.dead or self.data.invulnerable then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if reason.flags.is_fire and self.def.fire_damage_resistant then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
damage = mcl_damage.run_modifiers(self.object, damage, reason)
|
|
||||||
|
|
||||||
if damage > 0 then
|
|
||||||
mcl_damage.run_damage_callbacks(self.object, damage, reason)
|
|
||||||
self.data.health = self.data.health - damage
|
|
||||||
self.stun_timer = mcl_mobs.const.stun_timer
|
|
||||||
self:update_movement()
|
|
||||||
self.object:set_texture_mod("^[colorize:red:120")
|
|
||||||
|
|
||||||
if self.data.health < 0 then
|
|
||||||
self:die(reason)
|
|
||||||
else
|
|
||||||
self:play_sound("damage")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return damage
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:on_punch(puncher, time_from_last_punch, tool_capabilities, direction, damage)
|
|
||||||
if damage < 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local reason = {}
|
|
||||||
mcl_damage.from_punch(reason, puncher)
|
|
||||||
mcl_damage.finish_reason(reason)
|
|
||||||
|
|
||||||
if self.def.on_punch then
|
|
||||||
local args = {puncher = puncher, time_from_last_punch = time_from_last_punch, tool_capabilities = tool_capabilities, direction = direction}
|
|
||||||
if self.def.on_punch(self, damage, reason, args) == false then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:get_angry(reason.source)
|
|
||||||
|
|
||||||
-- PANIC AND RUN
|
|
||||||
if self.def.skittish then
|
|
||||||
self.state = "run"
|
|
||||||
|
|
||||||
self.run_timer = mcl_mobs.const.run_timer
|
|
||||||
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
local pos2 = reason.source:get_pos()
|
|
||||||
pos2.y = 0
|
|
||||||
|
|
||||||
|
|
||||||
local dir = vector.direction(pos2, pos1)
|
|
||||||
|
|
||||||
self.yaw = minetest.dir_to_yaw(direction)
|
|
||||||
end
|
|
||||||
|
|
||||||
if reason.type == "player" then
|
|
||||||
mcl_hunger.exhaust(puncher:get_player_name(), mcl_hunger.EXHAUST_ATTACK)
|
|
||||||
end
|
|
||||||
|
|
||||||
damage = self:deal_damage(damage, reason)
|
|
||||||
|
|
||||||
if damage > 0 then
|
|
||||||
self:play_sound_specific("default_punch")
|
|
||||||
self:knockback(reason.source)
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:update_armor_groups()
|
|
||||||
self.object:set_armor_groups(self.def.armor_groups)
|
|
||||||
end
|
|
|
@ -1,57 +0,0 @@
|
||||||
function mcl_mobs.mob:get_staticdata()
|
|
||||||
if self.dead then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self:anger_on_staticdata()
|
|
||||||
|
|
||||||
if self.def.on_staticdata then
|
|
||||||
if self.def.on_staticdata(self) == false then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return minetest.serialize(self.data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:on_activate(staticdata, def, dtime)
|
|
||||||
self.is_mob = true
|
|
||||||
self.def = mcl_mobs.registered_mobs[self.name] -- just access the mob def instead of spamming the luaentity itself with a copy of every single definition field that is never mutated
|
|
||||||
self.description = def.description -- external mods might want to access this
|
|
||||||
|
|
||||||
self.data = minetest.deserialize(staticdata) or {}
|
|
||||||
|
|
||||||
self.data.health = self.data.health or math.random(self.def.health_min, self.def.health_max)
|
|
||||||
self.data.breath = self.data.breath or self.def.breath_max
|
|
||||||
self.data.yaw = self.data.yaw or 0
|
|
||||||
|
|
||||||
self:reload_properties()
|
|
||||||
self:backup_movement()
|
|
||||||
|
|
||||||
self:anger_on_activate()
|
|
||||||
self:despawn_on_activate()
|
|
||||||
self:breeding_on_activate()
|
|
||||||
|
|
||||||
self:set_animation("stand")
|
|
||||||
self:update_collisionbox()
|
|
||||||
self:update_eye_height()
|
|
||||||
self:update_mesh()
|
|
||||||
self:update_nametag()
|
|
||||||
self:update_roll()
|
|
||||||
self:update_textures()
|
|
||||||
self:update_visual_size()
|
|
||||||
|
|
||||||
if self.def.on_spawn and not self.data.on_spawn_run then
|
|
||||||
self.def.on_spawn(self)
|
|
||||||
self.data.on_spawn_run = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.def.on_activate then
|
|
||||||
if self.def.on_activate(self, staticdata, def, dtime) == false then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,49 +0,0 @@
|
||||||
function mcl_mobs.mob:die(reason)
|
|
||||||
self.dead = true
|
|
||||||
self.death_timer = mcl_mobs.const.death_timer
|
|
||||||
|
|
||||||
for _, obj in pairs(self.object:get_children()) do
|
|
||||||
mcl_mount.throw_off(obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.settings:get_bool("doMobDrops", true) then
|
|
||||||
self:drop_loot(reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:play_sound("death")
|
|
||||||
self:set_animation("death")
|
|
||||||
self:set_properties({pointable = false})
|
|
||||||
self:update_acceleration()
|
|
||||||
|
|
||||||
if self.def.on_death then
|
|
||||||
self.def.on_death(self, reason)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:death_step()
|
|
||||||
if self:do_timer("death") then
|
|
||||||
self:update_roll()
|
|
||||||
else
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 50,
|
|
||||||
time = 0.0001,
|
|
||||||
minpos = vector.add(pos, self.collisionbox.min),
|
|
||||||
maxpos = vector.add(pos, self.collisionbox.max),
|
|
||||||
minvel = vector.new(-0.5, 0.5, -0.5),
|
|
||||||
maxvel = vector.new(0.5, 1.0, 0.5),
|
|
||||||
minexptime = 1.1,
|
|
||||||
maxexptime = 1.5,
|
|
||||||
minsize = 1,
|
|
||||||
maxsize = 2,
|
|
||||||
collisiondetection = false,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_mob_death.png",
|
|
||||||
})
|
|
||||||
|
|
||||||
self:play_sound_specific("mcl_sounds_poof")
|
|
||||||
|
|
||||||
self.object:remove() -- RIP
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
function mcl_mobs.mob:despawn_on_activate()
|
|
||||||
self.data.can_despawn = self.data.can_despawn ~= false and self.def.can_despawn
|
|
||||||
if self.data.can_despawn then
|
|
||||||
self.life_timer = life_timer -- how much time is left until next despawn check
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:despawn_step()
|
|
||||||
if not self:do_timer("life") then
|
|
||||||
self.life_timer = life_timer
|
|
||||||
return not self:check_despawn()
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:check_despawn()
|
|
||||||
self:debug("checking for nearby players")
|
|
||||||
if not self:is_player_near(despawn_radius) then
|
|
||||||
self:debug("despawning")
|
|
||||||
self.object:remove()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
function mcl_mobs.mob:update_easteregg()
|
|
||||||
local old_easteregg = self.easteregg or {}
|
|
||||||
local eastereggs = table.key_value_swap(mcl_mobs.eastereggs)
|
|
||||||
|
|
||||||
local easteregg_name = eastereggs[self.data.nametag]
|
|
||||||
local easteregg = old_easteregg
|
|
||||||
|
|
||||||
if old_easteregg.name ~= easteregg_name then
|
|
||||||
easteregg = {
|
|
||||||
name = easteregg_name,
|
|
||||||
[easteregg_name] = true,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if easteregg.rainbow ~= old_easteregg.rainbow then
|
|
||||||
if easteregg.rainbow then
|
|
||||||
easteregg.hue = 0
|
|
||||||
end
|
|
||||||
elseif easteregg.upside_down ~= old_easteregg.upside_down then
|
|
||||||
self:update_roll()
|
|
||||||
self:update_collisionbox()
|
|
||||||
end
|
|
||||||
|
|
||||||
self.easteregg = easteregg
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:easteregg_step()
|
|
||||||
if self.easteregg.rainbow then
|
|
||||||
self.easteregg.hue = self.easteregg.hue + 60 * self.dtime
|
|
||||||
self:update_textures()
|
|
||||||
elseif self.easteregg.spin then
|
|
||||||
self.data.yaw = self.data.yaw + 180 * dtime
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,110 +0,0 @@
|
||||||
-- 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", "mcl_mobs.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
|
|
||||||
]]--
|
|
|
@ -1,22 +0,0 @@
|
||||||
function mcl_mobs.mob:breath_step()
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
|
|
||||||
pos.y = pos.y + self.eye_height
|
|
||||||
|
|
||||||
local node = minetest.get_node(pos).name
|
|
||||||
|
|
||||||
if minetest.get_item_group(node, "water") ~= 0 then
|
|
||||||
self.data.breath = self.data.breath - self.dtime
|
|
||||||
|
|
||||||
if self.data.breath <= 0 then
|
|
||||||
self:deal_damage(4, {type = "drowning"})
|
|
||||||
self.data.breath = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif self.data.breath < self.def.breath_max then
|
|
||||||
self.data.breath = self.data.breath + self.dtime
|
|
||||||
if self.data.breath > self.def.breath_max then
|
|
||||||
self.data.breath = self.def.breath_max
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,65 +0,0 @@
|
||||||
function mcl_mobs.mob:collision_step()
|
|
||||||
local own_box, own_pos, own_boundary = mcl_mobs.util.get_collision_data(self.object)
|
|
||||||
|
|
||||||
local radius = math.max(own_boundary, own_box[5])
|
|
||||||
local max_cramming = tonumber(minetest.settings:get("mclMaxEntityCramming")) or mcl_mobs.const.max_entity_cramming
|
|
||||||
local parent = self.object:get_attach()
|
|
||||||
|
|
||||||
for _, obj in pairs(minetest.get_objects_inside_radius(own_pos, radius * 1.25)) do
|
|
||||||
if obj ~= self.object and obj ~= parent and obj:get_attach() ~= self.object then
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
|
|
||||||
if not luaentity and obj:get_hp() > 0 or luaentity and luaentity.is_mob and not luaentity.dead then
|
|
||||||
max_cramming = max_cramming - 1
|
|
||||||
|
|
||||||
if max_cramming <= 0 then
|
|
||||||
local target, source = self.object, obj
|
|
||||||
-- hurt adults before babies
|
|
||||||
if self.data.baby and luaentity then
|
|
||||||
target, source = source, target -- how the turntables...
|
|
||||||
end
|
|
||||||
mcl_util.deal_damage(target, mcl_util.get_hp(target), {type = "cramming", source = source})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj_box, obj_pos, obj_boundary = mcl_mobs.util.get_collision_data(obj)
|
|
||||||
|
|
||||||
-- 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 = obj_pos.y + obj_box[5] - own_pos.y
|
|
||||||
local y_top_diff = own_pos.y + own_box[5] - obj_pos.y
|
|
||||||
|
|
||||||
local distance = vector.distance(
|
|
||||||
vector.new(own_pos.x, 0, own_pos.z),
|
|
||||||
vector.new(obj_pos.x, 0, obj_pos.z)
|
|
||||||
)
|
|
||||||
|
|
||||||
local combined_boundary = own_boundary + obj_boundary
|
|
||||||
|
|
||||||
if distance <= combined_boundary and y_base_diff >= 0 and y_top_diff >= 0 then
|
|
||||||
local dir = vector.direction(own_pos, obj_pos)
|
|
||||||
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 * (math.round(math.random()) * 2 - 1)
|
|
||||||
dir.z = dir.z + math.random() / 10 * (math.round(math.random()) * 2 - 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj_vel = vector.multiply(dir, 0.5 * (1 - distance / combined_boundary) * 1.5)
|
|
||||||
local own_vel = vector.multiply(obj_vel, -10)
|
|
||||||
|
|
||||||
if not luaentity then
|
|
||||||
obj_vel = vector.multiply(obj_vel, 2.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
obj:add_velocity(obj_vel)
|
|
||||||
self.object:add_velocity(own_vel)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
function mcl_mobs.mob:env_step()
|
|
||||||
self:fall_damage_step()
|
|
||||||
|
|
||||||
if not self.def.breathes_in_water then
|
|
||||||
self:breath_step()
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_burning.tick(self.object, dtime, self.data)
|
|
||||||
|
|
||||||
if self.dead then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.def.ignited_by_sunlight then
|
|
||||||
self:sunlight_step()
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.def.unpushable then
|
|
||||||
self:collision_step()
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
|
@ -1,8 +0,0 @@
|
||||||
function mcl_mobs.mob:fall_damage_step()
|
|
||||||
-- ToDo: fall damage based on distance, not velocity
|
|
||||||
local velocity = self.object:get_velocity()
|
|
||||||
|
|
||||||
if self.last_velocity.y < -7 and velocity.y == 0 then
|
|
||||||
self:deal_damage(math.abs(self.last_velocity.y + 7) * 2, {type = "fall"})
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +0,0 @@
|
||||||
function mcl_mobs.mob:sunlight_step()
|
|
||||||
if self.data.burn_time then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = pos.y + 0.1
|
|
||||||
|
|
||||||
if mcl_worlds.pos_to_dimension(pos) == "overworld" then
|
|
||||||
local ok, light = pcall(minetest.get_natural_light or minetest.get_node_light, pos, minetest.get_timeofday())
|
|
||||||
if ok and light >= minetest.LIGHT_MAX then
|
|
||||||
mcl_burning.set_on_fire(self.object, math.huge)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,64 +0,0 @@
|
||||||
-- set defined animation
|
|
||||||
function mcl_mobs.mob:set_animation(anim, fixed_frame)
|
|
||||||
|
|
||||||
if not self.animation or not anim then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if (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
|
|
||||||
|
|
||||||
--this is a helper function for mobs explosion animation
|
|
||||||
function mcl_mobs.mob:handle_explosion_animation()
|
|
||||||
|
|
||||||
--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
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
function mcl_mobs.mob:update_collisionbox()
|
|
||||||
local box = self.def.collisionbox
|
|
||||||
|
|
||||||
if self.baby and self.def.baby_size then
|
|
||||||
box = mcl_mobs.util.scale_size(box, self.def.baby_size)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.easteregg.upside_down then
|
|
||||||
box[2], box[5] = -box[5], -box[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
self.collisionbox = {
|
|
||||||
min = vector.new(box[1], box[2], box[3]),
|
|
||||||
max = vector.new(box[4], box[5], box[6]),
|
|
||||||
}
|
|
||||||
|
|
||||||
self:set_properties({collisionbox = box})
|
|
||||||
|
|
||||||
self.collisionbox_cache = nil
|
|
||||||
mcl_mount.update_children_visual_size(self.object)
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
function mcl_mobs.mob:update_eye_height()
|
|
||||||
local eye_height = self.def.eye_height
|
|
||||||
|
|
||||||
if self.data.baby and self.def.baby_size then
|
|
||||||
eye_height = eye_height * self.def.baby_size
|
|
||||||
end
|
|
||||||
|
|
||||||
self.eye_height = eye_height
|
|
||||||
end
|
|
|
@ -1,115 +0,0 @@
|
||||||
|
|
||||||
--[[
|
|
||||||
local vector.new = vector.new
|
|
||||||
|
|
||||||
|
|
||||||
--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
|
|
||||||
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
|
|
||||||
--]]
|
|
|
@ -1,6 +0,0 @@
|
||||||
function mcl_mobs.mob:update_mesh()
|
|
||||||
self:set_properties({
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = self.def.model,
|
|
||||||
})
|
|
||||||
end
|
|
|
@ -1,7 +0,0 @@
|
||||||
function mcl_mobs.mob:update_nametag()
|
|
||||||
self:update_easteregg()
|
|
||||||
|
|
||||||
self:set_properties({
|
|
||||||
nametag = self.data.nametag,
|
|
||||||
})
|
|
||||||
end
|
|
|
@ -1,110 +0,0 @@
|
||||||
-- this is used when a mob is following player and for when mobs breed
|
|
||||||
function mcl_mobs.mob:look_at(obj)
|
|
||||||
self:lock_yaw()
|
|
||||||
|
|
||||||
-- turn positions into pseudo 2d vectors
|
|
||||||
local pos1 = self.object:get_pos()
|
|
||||||
pos1.y = 0
|
|
||||||
|
|
||||||
local pos2 = obj: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 allows auto facedir rotation while making it so mobs
|
|
||||||
-- don't look like wet noodles flopping around
|
|
||||||
function mcl_mobs.mob:movement_rotation_lock()
|
|
||||||
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
|
|
||||||
|
|
||||||
local diff = math.abs(current_engine_yaw - current_lua_yaw)
|
|
||||||
|
|
||||||
if diff <= 0.05 then
|
|
||||||
self:lock_yaw()
|
|
||||||
elseif diff > 0.05 then
|
|
||||||
self:unlock_yaw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this is used to unlock a mob's yaw after attacking
|
|
||||||
function mcl_mobs.mob:unlock_yaw()
|
|
||||||
if not self.properties.automatic_face_movement_dir then
|
|
||||||
self:set_properties({automatic_face_movement_dir = self.def.rotate})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this is used to lock a mob's yaw when they're standing
|
|
||||||
function mcl_mobs.mob:lock_yaw()
|
|
||||||
if self.properties.automatic_face_movement_dir then
|
|
||||||
self:set_properties({automatic_face_movement_dir = false})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:calculate_pitch(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)) + math.pi / 2
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is a helper function used to make mobs pitch rotation dynamically flow when flying/swimming
|
|
||||||
function mcl_mobs.mob:set_dynamic_pitch()
|
|
||||||
local pitch = self:calculate_pitch()
|
|
||||||
|
|
||||||
if not pitch then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local rotation = self.object:get_rotation()
|
|
||||||
rotation.x = pitch
|
|
||||||
self.object:set_rotation(rotation)
|
|
||||||
|
|
||||||
self.dynamic_pitch = true
|
|
||||||
end
|
|
||||||
|
|
||||||
--this is a helper function used to make mobs pitch rotation reset when flying/swimming
|
|
||||||
function mcl_mobs.mob:set_static_pitch()
|
|
||||||
if not self.dynamic_pitch then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local current_rotation = self.object:get_rotation()
|
|
||||||
current_rotation.x = 0
|
|
||||||
self.object:set_rotation(current_rotation)
|
|
||||||
|
|
||||||
self.dynamic_pitch = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:quick_rotate()
|
|
||||||
self.yaw = self.yaw + math.pi * 2 * 0.03125
|
|
||||||
if self.yaw > math.pi * 2 then
|
|
||||||
self.yaw = self.yaw - math.pi * 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:update_roll()
|
|
||||||
local roll = 0
|
|
||||||
|
|
||||||
if self.dead then
|
|
||||||
roll = math.pi * math.min(0.5, 1 - self.death_timer / mcl_mobs.const.death_timer)
|
|
||||||
elseif self.easteregg.upside_down then
|
|
||||||
roll = math.pi
|
|
||||||
end
|
|
||||||
|
|
||||||
local rotation = self.object:get_rotation()
|
|
||||||
rotation.z = roll
|
|
||||||
self.object:set_rotation(rotation)
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
function mcl_mobs.mob:get_special_textures()
|
|
||||||
if self.baby then
|
|
||||||
return self:evaluate("baby_textures")
|
|
||||||
elseif self.gotten then
|
|
||||||
return self:evaluate("gotten_textures")
|
|
||||||
elseif self.easteregg.rainbow then
|
|
||||||
return self:evaluate("rainbow_textures", mcl_mobs.util.color_from_hue(self.easteregg.hue))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:get_textures()
|
|
||||||
return self:get_special_textures() or self:calculate_textures(self.def.textures)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:update_textures()
|
|
||||||
self:set_properties({textures = self:get_textures()})
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
function mcl_mobs.mob:update_visual_size()
|
|
||||||
local size = self.def.visual_size
|
|
||||||
|
|
||||||
if self.data.size then
|
|
||||||
mcl_mobs.util.scale_size(size, self.data.size)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.data.baby and self.def.baby_size then
|
|
||||||
mcl_mobs.util.scale_size(size, self.def.baby_size)
|
|
||||||
end
|
|
||||||
|
|
||||||
local parent = self.object:get_attach()
|
|
||||||
|
|
||||||
if parent then
|
|
||||||
size = vector.divide(size, parent:get_properties().visual_size)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_properties({visual_size = size})
|
|
||||||
|
|
||||||
mcl_mount.update_children_visual_size(self.object)
|
|
||||||
end
|
|
|
@ -1,59 +0,0 @@
|
||||||
function mcl_mobs.mob:on_rightclick(clicker)
|
|
||||||
local itemstack = clicker:get_wielded_item()
|
|
||||||
|
|
||||||
if self:on_rightclick_handler(clicker, itemstack) then
|
|
||||||
clicker:set_wielded_item(itemstack)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:feed(clicker, itemname)
|
|
||||||
if self.data.heal_with[itemname] and self.data.health < self.def.health_max then
|
|
||||||
self:heal()
|
|
||||||
elseif self.def.boost_with[itemname] and self.data.baby then
|
|
||||||
self:boost()
|
|
||||||
elseif self.def.breed_with[itemname] and not self.data.bred then
|
|
||||||
self:init_breeding()
|
|
||||||
elseif self.data.tame_with[itemname] and not self.def.tamed then
|
|
||||||
self:tame(clicker)
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mobs.mob:on_rightclick_handler(clicker, itemstack)
|
|
||||||
if self.dead then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.def.on_rightclick then
|
|
||||||
if self.def.on_rightclick(clicker, itemstack) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local itemname = itemstack:get_name()
|
|
||||||
|
|
||||||
if not self.def.ignores_nametag and itemname == "mcl_mobitems:nametag" then
|
|
||||||
local tag = item:get_meta():get_string("name")
|
|
||||||
if tag ~= "" then
|
|
||||||
self.data.nametag = tag
|
|
||||||
self:update_nametag()
|
|
||||||
|
|
||||||
return mcl_mobs.util.take_item(clicker, itemstack)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self:feed(clicker, itemstack) then
|
|
||||||
mcl_mobs.util.take_item(clicker, itemname)
|
|
||||||
self:play_sound_specific("mobs_mc_animal_eat_generic")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.data.gotten and self.def.get_with[itemname] and self.def.get(self, clicker, itemstack) then
|
|
||||||
self.data.gotten = true
|
|
||||||
self.data.gotten_timer = self:evaluate("gotten_cooldown")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,35 +0,0 @@
|
||||||
function mcl_mobs.mob:knockback(hitter)
|
|
||||||
if self.def.knockback_multiplier == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if hitter:get_attach() == self.object then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local velocity = self.object:get_velocity()
|
|
||||||
|
|
||||||
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 = mcl_mobs.const.knockback_up
|
|
||||||
|
|
||||||
if velocity.y ~= 0 then
|
|
||||||
up = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local multiplier = mcl_mobs.const.knockback
|
|
||||||
|
|
||||||
local knockback_level = mcl_enchanting.get_enchantment(mcl_util.get_wield_item(hitter), "knockback")
|
|
||||||
if knockback_level > 0 then
|
|
||||||
multiplier = multiplier + knockback_level * 3
|
|
||||||
end
|
|
||||||
|
|
||||||
dir = vector.multiply(dir, multiplier * self.def.knockback_multiplier)
|
|
||||||
dir.y = up * self.def.knockback_multiplier
|
|
||||||
|
|
||||||
self.object:add_velocity(dir)
|
|
||||||
end
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue