Compare commits
508 Commits
debiankaio
...
master
Author | SHA1 | Date |
---|---|---|
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | ee2418f91c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 56b915d51d | |
PrairieWind | ba47414a95 | |
3raven | 52d311119f | |
3raven | 3cee342f1f | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 15ac28d000 | |
FaceDeer | 7e0cfed1b5 | |
chmodsayshello | 19df0fb5d7 | |
FaceDeer | 5cc0288a03 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | a50255cb97 | |
Your Name | 841789f010 | |
Your Name | 7270094619 | |
Your Name | 3a77a97e2d | |
Your Name | 384e5ece67 | |
Your Name | da81f0b5b4 | |
Your Name | 6ac082aff7 | |
FaceDeer | eef5c0dc46 | |
Your Name | cdb881ff9a | |
Your Name | cce8dcce97 | |
Your Name | 71f0d9364a | |
Your Name | 2b39a1b9bf | |
Your Name | e6cb2c48b7 | |
FaceDeer | ae60960a00 | |
chmodsayshello | d4da512309 | |
chmodsayshello | 3a36593612 | |
chmodsayshello | a506ae9fff | |
chmodsayshello | 82cd2be6b7 | |
chmodsayshello | 368f891746 | |
chmodsayshello | c53055c472 | |
chmodsayshello | 5507303deb | |
chmodsayshello | e98be27138 | |
chmodsayshello | e308240fb6 | |
chmodsayshello | 82423cfb33 | |
kay27 | 4ade7dc769 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 6bbf3bc80c | |
kay27 | 85ca6e867a | |
cora | 70daeda500 | |
kay27 | 6c2bdf3156 | |
kay27 | 797a7d22e1 | |
kay27 | 40c17d288b | |
kay27 | bfc248ee9a | |
kay27 | 9540363363 | |
kay27 | 86913119ea | |
kay27 | ee35c09a94 | |
PrWalterB | 72d6b4de06 | |
PrWalterB | ca8c9ed1fc | |
kay27 | 951a99c7cf | |
PrWalterB | a492522fb5 | |
PrWalterB | 017ecf0cb4 | |
PrWalterB | 56f83240fc | |
PrWalterB | 5ea614311d | |
PrWalterB | 5e9f7129d1 | |
kay27 | 37350718c7 | |
PrWalterB | 924bf17be7 | |
PrWalterB | 24096875db | |
3raven | 1ea7937a69 | |
3raven | 0dfff81069 | |
3raven | c7efef2dcf | |
3raven | b532182dd8 | |
3raven | 54dd1b2cad | |
3raven | c53cd30c8d | |
3raven | 3ec699ce3b | |
3raven | 70b440859b | |
3raven | b1e03173fe | |
3raven | 40f4019d6e | |
3raven | b817c92c9d | |
3raven | c62eb202ab | |
3raven | 43e75c8017 | |
3raven | 87a0e7b28a | |
3raven | bb96f81d31 | |
3raven | 6e6a0104ef | |
3raven | 9836da42da | |
3raven | 47b3db0da3 | |
3raven | d3661ee60f | |
3raven | aa39f33404 | |
3raven | cca48ffa69 | |
3raven | bafed4615b | |
3raven | 6ff1856a58 | |
3raven | 58db95a7d3 | |
3raven | cae6aeecc3 | |
3raven | db71d5ac80 | |
3raven | 4c78fb4517 | |
3raven | 4b9f51a5f8 | |
3raven | 4c4b0636d0 | |
3raven | fdcb2f56e8 | |
3raven | 26157cb355 | |
3raven | 2b00261f40 | |
3raven | 597e4f827b | |
3raven | 1a270069f0 | |
3raven | e99a7b3e98 | |
3raven | 426cc085e4 | |
3raven | 497150f3fb | |
3raven | ea776acfda | |
3raven | ba080086be | |
3raven | 33fa0a18de | |
3raven | 3fb7f17fe1 | |
3raven | 49ba1c7c92 | |
3raven | 66eb8fa99a | |
3raven | 674ae230d3 | |
3raven | 71f926b851 | |
3raven | bee5d01a31 | |
3raven | 7fa256b480 | |
3raven | 91141c68fa | |
3raven | a07e825f84 | |
3raven | d41710b169 | |
kay27 | 98db03c82e | |
3raven | 154aac4c5c | |
3raven | 0adcab7850 | |
3raven | 523bca28af | |
kay27 | 85cfc95500 | |
kay27 | 79cca6b121 | |
3raven | ffa4a4af16 | |
3raven | aff9889c9b | |
3raven | 3175be742e | |
3raven | 447f26317b | |
3raven | dd95c75977 | |
3raven | 3ccb3b74dc | |
3raven | 4a060350a4 | |
3raven | 4981cfeb73 | |
3raven | 50036c33a3 | |
3raven | 9d227324d0 | |
3raven | c6ef8b615d | |
3raven | 380b7febc5 | |
3raven | 5ad3f1b84c | |
3raven | eaedaa91f8 | |
3raven | 13ebca9850 | |
3raven | 06d5f8490d | |
3raven | 9622eb89ba | |
3raven | 9852180e12 | |
3raven | d398c5d1f9 | |
3raven | 2129436ec3 | |
3raven | b7502fc5fa | |
3raven | b88091ee78 | |
kay27 | 03e20912b4 | |
3raven | d5c06e35ff | |
3raven | 866ee6f121 | |
3raven | aaaf0192ad | |
3raven | 974769abd8 | |
3raven | d54c4cdcb2 | |
3raven | 4210c19e7e | |
3raven | 9d85014222 | |
3raven | 3cd1dd07eb | |
3raven | dae236fe44 | |
3raven | cce9cc868c | |
3raven | 03cd73261a | |
3raven | 8af587751f | |
3raven | f6bc04142c | |
kay27 | c3e208dbb0 | |
3raven | 19b7f796c7 | |
3raven | 2f9df70ac4 | |
3raven | 5cabb7875a | |
3raven | 0248fa533d | |
3raven | c9431fbbdb | |
3raven | 7879e6bcb2 | |
3raven | 6994da8191 | |
3raven | 5f4327caf0 | |
3raven | 851bc14bbc | |
kay27 | ee5d45152d | |
3raven | 4db9cf7e9f | |
3raven | 4ba679b011 | |
3raven | c284e48143 | |
3raven | 9158201c51 | |
kay27 | e80f95710d | |
3raven | 4551df1d0c | |
3raven | b1ea2ad9c9 | |
kay27 | 3d656522f2 | |
3raven | 7afdc48d0d | |
3raven | 5495c67c7d | |
3raven | 329576f8e8 | |
3raven | e1436c6913 | |
3raven | 0d42106bc8 | |
3raven | 4f9b40b6ab | |
kay27 | d0cd3ad478 | |
kay27 | 14e1842820 | |
kay27 | 2772303fc0 | |
kay27 | 1cebafee80 | |
kay27 | 7f02781339 | |
kay27 | 69b66c2e3c | |
kay27 | 9210e05eba | |
kay27 | f4e44acb94 | |
kay27 | 08846bef2f | |
kay27 | ec00351041 | |
Sumyjkl | 3ec5a26aa1 | |
Sumyjkl | b2bebd1d8d | |
Sumyjkl | 0a3190887c | |
Sumyjkl | 56b1cc8d75 | |
kay27 | ea8d96389f | |
kay27 | f2ae6a9d02 | |
kay27 | 1b9af42521 | |
kay27 | 3bc3540ead | |
kay27 | 01b2f65309 | |
kay27 | eefaba3f49 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 4ca2fb25a7 | |
kay27 | 0ad52fd949 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 2905a21674 | |
kay27 | 98855596f9 | |
kay27 | b679fd8a13 | |
wallabra | 2caf2bf1b8 | |
wallabra | 6b78abd79c | |
kay27 | 7640edabcc | |
kay27 | 690b0782a5 | |
wallabra | 2eda3a6971 | |
wallabra | b8f89e5569 | |
wallabra | f3cc8f0f8a | |
wallabra | 5307908290 | |
wallabra | 588afc72f0 | |
kay27 | fa46332870 | |
wallabra | d57097baef | |
wallabra | 54e9477f37 | |
wallabra | 3502e504bf | |
wallabra | ff0e0dd36a | |
wallabra | 218990c114 | |
kay27 | 56ab442645 | |
kay27 | acda4deb3b | |
kay27 | 8b2cce4399 | |
PrairieWind | f93efc4843 | |
balazsszalab | 42fe7d8c73 | |
balazsszalab | 5b56acd415 | |
PrairieWind | 2b20f89d7b | |
balazsszalab | e2e4f7d2df | |
balazsszalab | 464649945c | |
PrairieWind | d33aba9355 | |
FlamingRCCars | 88935cbcbd | |
FlamingRCCars | 73d67792ee | |
FlamingRCCars | 5ceed2672e | |
FlamingRCCars | 5d2552eb97 | |
unknown | 047d75a78a | |
unknown | fb7390f61d | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 0334127372 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 087a665112 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 3c346170a6 | |
PrairieWind | 615eae97ee | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 6889c672c0 | |
kay27 | 7becc4e01b | |
PrairieAstronomer | 539ada1057 | |
PrairieAstronomer | de29e05a6f | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 6aef455fbc | |
PrairieAstronomer | 977a77f871 | |
PrairieAstronomer | 43e73e2856 | |
PrairieAstronomer | f17f51cce3 | |
PrairieAstronomer | ebcadc415d | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | da4b867314 | |
PrairieAstronomer | 6cca004dbe | |
PrairieWind | 6c84baac22 | |
PrairieAstronomer | 6ff27c0247 | |
3raven | 6dbbe99d68 | |
3raven | 7808c576a5 | |
3raven | f968d76caa | |
3raven | 7cab3b67e1 | |
3raven | b79017d86e | |
3raven | 6e9448c0b5 | |
3raven | c5655a40cd | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 34586bef62 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 836ef83bd1 | |
PrairieAstronomer | cd75d6ffd2 | |
PrairieAstronomer | 6c9fc8c963 | |
PrairieAstronomer | 4abc8181f7 | |
3raven | 36e671f439 | |
3raven | 5a3c1bb3d8 | |
3raven | 1b9c5e6e23 | |
3raven | 3b2483ff4f | |
3raven | b084071954 | |
3raven | ddcb7f141f | |
3raven | 36de9f53b7 | |
3raven | 493fb95e52 | |
3raven | 3ce0009181 | |
3raven | a97d6ec20d | |
3raven | 7b5cc25c6c | |
3raven | 7f1c09de59 | |
3raven | 80a7ab971a | |
3raven | 6df9027971 | |
3raven | 0e1b4d8fea | |
3raven | fa9a06a6d5 | |
3raven | cc81b51128 | |
3raven | caac96b50e | |
3raven | 50cbde6380 | |
3raven | 8d79e5f7f7 | |
PrairieAstronomer | 3a12b9f6c6 | |
PrairieAstronomer | d4828f8993 | |
PrairieAstronomer | 2a08c60602 | |
PrairieAstronomer | 53a1d7dd58 | |
PrairieAstronomer | 80416fef71 | |
PrairieAstronomer | f57cbcb0a0 | |
3raven | 8ccf1ae3a9 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 9c19395dd7 | |
cora | ee969531a8 | |
cora | a7d3faee47 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | ff31e5d480 | |
3raven | 499f698ba7 | |
kay27 | 8a0ce314a2 | |
kay27 | da3034cadd | |
kay27 | 0d8c9c8577 | |
kay27 | 0ed8976aae | |
AFCMS | 48c8eaa6ed | |
AFCMS | a4655a0c23 | |
AFCMS | 4af046500d | |
kay27 | 13dc68f40f | |
kay27 | 27230fff4a | |
balazsszalab | 6ef292e80e | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 00e54944fa | |
3raven | d17a50ecba | |
3raven | 49acbfed8e | |
kay27 | 69bc5dbb16 | |
kay27 | b8b9683d50 | |
kay27 | 76efcd4906 | |
3raven | ab22628afe | |
3raven | 010ffd695f | |
3raven | 6ffa01cac6 | |
3raven | 2f0351fe6d | |
kay27 | b70ae30fda | |
kay27 | d0a8848487 | |
kay27 | f39e0bca3d | |
kay27 | cd02080ca8 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | b80e511cae | |
kay27 | d476e26c60 | |
River River | 663c2bedbb | |
kay27 | 3a7363f11d | |
Nils Dagsson Moskopp | 033573ad63 | |
kay27 | c5e84a2704 | |
kay27 | f204470052 | |
kay27 | 721e721427 | |
kay27 | e4f218fded | |
CableGuy67 | 5432c6bd92 | |
3raven | a8be87f88d | |
3raven | 84320db3b7 | |
3raven | 85a6f216f0 | |
3raven | 01737dd551 | |
3raven | fec258cb99 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | c5ad0d5f33 | |
3raven | 9367c74e71 | |
3raven | 3dcb969d75 | |
3raven | ed29cd687b | |
3raven | f57135259c | |
3raven | c3ef5e93af | |
3raven | df8dab3fac | |
3raven | 04c62e475e | |
3raven | 44c2c26265 | |
3raven | 4850b914a4 | |
3raven | 1d331313d7 | |
3raven | 9010305df4 | |
3raven | 0200e25781 | |
3raven | 8153487290 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | b6a4c1a995 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | f0b8e8ea11 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | d0fcab11e9 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | b7f3b8edd5 | |
3raven | 940499ee7e | |
3raven | 9b04b285b7 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 1d315c569f | |
3raven | 64766109f5 | |
3raven | c6f09e30a5 | |
3raven | a47e0d2f17 | |
3raven | a1ca0c3431 | |
3raven | c72c960e8a | |
3raven | 0184925890 | |
3raven | 44d32b5d17 | |
3raven | 07d564b5a2 | |
3raven | 1e431814bc | |
3raven | 337b4e1d05 | |
3raven | f052e26e0c | |
3raven | c55aab9360 | |
3raven | 421c1e8d00 | |
3raven | 3591ef1fae | |
3raven | 47ed81490a | |
3raven | 609653eaf2 | |
3raven | f828ee6a19 | |
3raven | e14545a2b8 | |
3raven | c3111c99b2 | |
3raven | 140fd67686 | |
3raven | 700200958d | |
3raven | 8cd3b63fae | |
3raven | 4c957a3e7c | |
3raven | 2b87fb2cd2 | |
3raven | cbfac60742 | |
3raven | f46d093038 | |
3raven | cc300971b3 | |
3raven | 9dd85f9f7b | |
3raven | 7f34c979ff | |
3raven | 6afda0af92 | |
3raven | f60588cf25 | |
3raven | fd94fba22c | |
3raven | 99c4fbfc38 | |
3raven | e4865338fc | |
3raven | c1a7d3e504 | |
3raven | b7c521bedc | |
3raven | 8f64a1e18c | |
3raven | e017d4a724 | |
3raven | 69fc0228d6 | |
3raven | c0e83aa0cd | |
3raven | 5429a259ff | |
3raven | 3e8e74b633 | |
3raven | a2933ece62 | |
3raven | 820ceceffc | |
3raven | 11a9f6baea | |
3raven | 5d214ae4bf | |
3raven | 13e92cd5b3 | |
3raven | 5fa14070d3 | |
3raven | 3129379fe9 | |
3raven | 45c3889316 | |
3raven | b0392bbeb9 | |
3raven | 7db8d3cd5f | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 86328c0822 | |
3raven | 89f835ab7b | |
3raven | 5f2ea5b073 | |
3raven | 8e98239a57 | |
3raven | 521b890612 | |
kay27 | 8eb0ba5501 | |
kabou | ed963428c0 | |
kay27 | f5ab76592c | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | d424aaa5bc | |
kay27 | 9e6f1c78e6 | |
kay27 | be58b54104 | |
kay27 | b4bc3e70b3 | |
kay27 | 05fdda96ad | |
kay27 | bba0ef15cb | |
PrairieWind | 4444437f94 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | b99fbe8f69 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | ddaa96589e | |
PrairieAstronomer | 2ea868ba79 | |
PrairieAstronomer | c9470b8262 | |
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 | 1273d59995 | |
chmodsayshello | 25ff05635a | |
debiankaios | 0dfe211053 | |
debiankaios | 5b8d9cd2f0 | |
chmodsayshello | d467060897 | |
chmodsayshello | dc93dd87e2 | |
chmodsayshello | da7be430ce | |
chmodsayshello | fc50c81571 | |
Mikita Wiśniewski | fa4a554b28 | |
Mikita Wiśniewski | ddb618c2e3 | |
chmodsayshello | 650e2f5c60 | |
Mikita Wiśniewski | 297f9ba11a | |
kay27 | 86bef3e055 | |
kay27 | 833e32410e | |
kay27 | a7ca121f73 | |
CableGuy67 | 9e72966f91 | |
CableGuy67 | 7ad5f7c66e | |
chmodsayshello | 1c12f3da19 | |
chmodsayshello | 63123816f0 | |
kay27 | 0b4f28d8b3 | |
chmodsayshello | 6c41b8a265 | |
chmodsayshello | fc59d05679 | |
chmodsayshello | a32531c8f6 | |
chmodsayshello | fb6315ea62 | |
chmodsayshello | d97c7b29c6 | |
chmodsayshello | 1dd81decd5 | |
chmodsayshello | 57c099943a | |
chmodsayshello | 6521e1f22e | |
chmodsayshello | f49d7da655 | |
chmodsayshello | 7117ce55db | |
kay27 | a75c8d427e | |
kay27 | 44d8caf3b7 | |
kay27 | 6be3d2e0d4 | |
kay27 | 0c04ff93fd | |
kay27 | 7b0119ddd6 | |
Mark Roth | 6c196ae63a | |
kay27 | ed656a095d | |
Mark Roth | c282324661 | |
kay27 | 4f0dbec948 | |
kay27 | a6e4de2b6b | |
kay27 | 05e739390e | |
kay27 | cb4bb79224 | |
kay27 | 2f7bb481ad | |
kay27 | 7ca28d8a27 | |
kay27 | a392d59cab | |
kay27 | 2ea71e9367 | |
kay27 | 6d27d5b5af | |
kay27 | 37ff5f15bd | |
cora | ba5474f5d4 | |
kay27 | 89cbc1deed | |
kay27 | a637e4bdaf | |
kay27 | c5b9313428 | |
kay27 | 21f7738b4d | |
kay27 | 7ad310a848 | |
kay27 | 60693b2d01 | |
kay27 | 429984937c | |
kay27 | abfd2e692a | |
kay27 | 4b026a70e5 | |
kay27 | 4c671429c5 | |
kay27 | 430c3ce636 | |
kay27 | ef08bfa5d9 | |
kay27 | e2d2d4219b | |
kay27 | 1e8f5ffc74 | |
kay27 | 869154f86d | |
kay27 | 4360158ecf | |
kay27 | 887f358e1f | |
kay27 | 5119f07194 | |
CableGuy67 | 78c1f741d6 | |
CableGuy67 | 7c7ad3ef0a | |
kay27 | a605a7afe7 | |
kay27 | abe2bde998 | |
kay27 | 9d7f619a24 | |
kay27 | be3549fb8d | |
kay27 | c8aefd03ec | |
kay27 | 6f66be58b0 | |
kay27 | c055d88f59 | |
kay27 | 96bc98e716 | |
kay27 | a887708fb5 | |
kay27 | 54c06f3fe8 | |
kay27 | ed4e23902d | |
kay27 | 24b735ae53 | |
wallabra | 024904eadd | |
wallabra | 2c8194bd6d | |
wallabra | 0c6da14e1f | |
wallabra | 0af898fc6e | |
wallabra | 6002057839 | |
wallabra | eee07f56b5 | |
wallabra | 09854a9af4 |
|
@ -27,19 +27,41 @@ Any Pull Request that isn't a bug fix can be closed within a week unless it rece
|
|||
|
||||
Start coding!
|
||||
|
||||
Refer to Minetest Lua API, Developer Wiki and other documentation.
|
||||
Refer to [Minetest Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), [Developer Wiki](https://dev.minetest.net/), [MineClone 5 Wiki](https://git.minetest.land/MineClone5/MineClone5/wiki/) and other documentation.
|
||||
|
||||
Follow Lua code style guidelines. Use tabs, not spaces for indentation (tab size = 8). Never use `minetest.env`.
|
||||
Follow [Lua code style guidelines](https://dev.minetest.net/Lua_code_style_guidelines). Use tabs, not spaces for indentation (tab size = 8). Never use `minetest.env`.
|
||||
|
||||
If you do a translation, try detecting translational issues with `check_translate_files.py` - just run it from tools folder:
|
||||
```bash
|
||||
# python3 check_translate_files.py fr | less
|
||||
```
|
||||
(`fr` is a language code)
|
||||
|
||||
Check your code works as expected.
|
||||
|
||||
Commit & push your changes to a new branch (not master, one change per branch)
|
||||
Commit & push your changes to a new branch (not master, one change per a branch).
|
||||
|
||||
Commit messages should use the present tense and be descriptive.
|
||||
|
||||
Once you are happy with your changes, submit a pull request.
|
||||
|
||||
A pull-request is considered merge-able when:
|
||||
A pull-request is considered merge-able when it looks good to one person from the community.
|
||||
|
||||
Please invite other developers to review your contribution when you know they are online. If there is no any reaction during 24 hours after posting the invitation and pinging developers - you are welcome to do a self-review and merge the request.
|
||||
|
||||
If someone else's contribution looks good to you - you are free to merge it ASAP.
|
||||
|
||||
Different git branches are welcomed! Releases by different people are welcomed! Releases from different branches are welcomed! Frequent releases are welcomed!
|
||||
|
||||
It is nice not to block other developers by your work and don't dictate them what to do, unsless they really want that. Git branches and forks are recommended to avoid conflicts at development stage.
|
||||
|
||||
It is nice to try splitting big features into small steps.
|
||||
|
||||
It is nice to create an issue for any work and mention the issue in the commit text, like `#123 Fix blast resistance of cactus`, where `#123` is the issue number.
|
||||
|
||||
Actually, it looks like we all love what we do, so any stupid situations should be carefully discussed before merging into upstreams. But nothing prevents us from releasing controversial stuff through dedicated branches. Release your contribution when you need more feedback.
|
||||
|
||||
Feel free to break the rules if you're sure you have to.
|
||||
|
||||
#### Contributors
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
|||
* `coral_block=X`: Coral block (1 = alive, 2 = dead)
|
||||
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
||||
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
||||
* `compostability`: Amount from 1 to 100 that defines the percentage of likelyhood that the composter will advance a level.
|
||||
|
||||
#### Footnotes
|
||||
|
||||
|
@ -200,6 +201,8 @@ These groups are used mostly for informational purposes
|
|||
* `building_block=1`: Block is a building block
|
||||
* `deco_block=1`: Block is a decorational block
|
||||
|
||||
* `blast_furnace_smeltable=1` : Item or node is smeltable by a blast furnace
|
||||
* `smoker_cookable=1` : Food is cookable by a smoker.
|
||||
|
||||
## Fake item groups
|
||||
These groups put similar items together which should all be treated by the gameplay or the GUI as a single item.
|
||||
|
|
10
README.md
|
@ -70,7 +70,11 @@ an explanation.
|
|||
|
||||
## Installation
|
||||
This game requires latest stable [Minetest](http://minetest.net) to run, please install
|
||||
it first. Only stable versions of Minetest are officially supported.
|
||||
it first. Only latest stable version of Minetest is officially supported.
|
||||
There are lots of questions about Ubuntu, which has minetest-5.1.1 still.
|
||||
Please, first of all, visit this page, it should fix the problem: https://launchpad.net/~minetestdevs/+archive/ubuntu/stable
|
||||
Also, here is endless issue #123: https://git.minetest.land/MineClone5/MineClone5/issues/123 - really less preferable way.
|
||||
|
||||
There is no support for running MineClone 5 in development versions of Minetest.
|
||||
|
||||
To install MineClone 5 (if you haven't already), move this directory into the
|
||||
|
@ -188,3 +192,7 @@ Technical differences from Minecraft:
|
|||
* `API.md`: For Minetest modders who want to mod this game
|
||||
* `LEGAL.md`: Legal information
|
||||
* `CREDITS.md`: List of everyone who contributed
|
||||
|
||||
## Menu music
|
||||
|
||||
* horizonchris96 — 02_what_we_ll_build_next
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# mcl_bubble_column by j45
|
||||
|
||||
https://github.com/Minetest-j45/mcl_bubble_column/
|
||||
|
||||
Adds whirlpools and upwards bubble columns to Mineclone2/5
|
||||
|
||||
A bubble column is a block generated by placing magma blocks or soul sand in water (source).
|
||||
|
||||
Bubble columns push or pull entities and items in certain directions.
|
|
@ -1,195 +0,0 @@
|
|||
mcl_bubble_column = {}
|
||||
|
||||
minetest.register_abm{
|
||||
label = "bubbleColumnUpStop",
|
||||
nodenames = {"group:water"},
|
||||
interval = 0.05,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("bubbly") == 1 then--bubble column
|
||||
--check down if current needs to be deleted
|
||||
local downpos = vector.add(pos, {x = 0, y = -1, z = 0})
|
||||
local downposnode = minetest.get_node(downpos)
|
||||
local downmeta = minetest.get_meta(downpos)
|
||||
if (downmeta:get_int("bubbly") ~= 1 and downposnode.name ~= "mcl_nether:soul_sand") then
|
||||
meta:set_int("bubbly", 0)
|
||||
end
|
||||
--check up to see if needs to go up
|
||||
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
local upposnode = minetest.get_node(uppos)
|
||||
local upmeta = minetest.get_meta(uppos)
|
||||
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("bubbly") ~= 1) then
|
||||
upmeta:set_int("bubbly", 1)
|
||||
end
|
||||
elseif meta:get_int("whirly") == 1 then--whirlpool
|
||||
--check down if current needs to be deleted
|
||||
local downpos = vector.add(pos, {x = 0, y = -1, z = 0})
|
||||
local downposnode = minetest.get_node(downpos)
|
||||
local downmeta = minetest.get_meta(downpos)
|
||||
if (downmeta:get_int("whirly") ~= 1 and downposnode.name ~= "mcl_nether:magma") then
|
||||
meta:set_int("whirly", 0)
|
||||
end
|
||||
--check up to see if needs to go up
|
||||
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
local upposnode = minetest.get_node(uppos)
|
||||
local upmeta = minetest.get_meta(uppos)
|
||||
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("whirly") ~= 1) then
|
||||
upmeta:set_int("whirly", 1)
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
minetest.register_abm{
|
||||
label = "startBubbleColumn",
|
||||
nodenames = {"mcl_nether:soul_sand"},
|
||||
interval = 0.05,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
local upposnode = minetest.get_node(uppos)
|
||||
local upmeta = minetest.get_meta(uppos)
|
||||
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("bubbly") ~= 1) then
|
||||
upmeta:set_int("bubbly", 1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
minetest.register_abm{
|
||||
label = "startWhirlpool",
|
||||
nodenames = {"mcl_nether:magma"},
|
||||
interval = 0.05,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||
local upposnode = minetest.get_node(uppos)
|
||||
local upmeta = minetest.get_meta(uppos)
|
||||
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("whirly") ~= 1) then
|
||||
upmeta:set_int("whirly", 1)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
mcl_bubble_column.on_enter_bubble_column = function(self)
|
||||
local velocity = self:get_velocity()
|
||||
--[[if down.name == "mcl_nether:soul_sand" then
|
||||
self:add_velocity({x = 0, y = math.min(10, math.abs(velocity.y)+9.4), z = 0})
|
||||
else]]
|
||||
self:add_velocity({x = 0, y = math.min(3.6, math.abs(velocity.y)+3), z = 0})
|
||||
--end
|
||||
end
|
||||
|
||||
mcl_bubble_column.on_enter_whirlpool = function(self)
|
||||
local velocity = self:get_velocity()
|
||||
--self:add_velocity({x = 0, y = math.max(-3, (-math.abs(velocity.y))-2), z = 0})
|
||||
self:add_velocity({x = 0, y = math.max(-0.3, (-math.abs(velocity.y))-0.03), z = 0})
|
||||
end
|
||||
|
||||
mcl_bubble_column.on_enter_bubble_column_with_air_above = function(self)
|
||||
local velocity = self:get_velocity()
|
||||
--[[if down.name == "mcl_nether:soul_sand" then
|
||||
self:add_velocity({x = 0, y = math.min(4.3, math.abs(velocity.y)+2.8), z = 0})
|
||||
else]]
|
||||
self:add_velocity({x = 0, y = math.min(2.6, math.abs(velocity.y)+2), z = 0})
|
||||
--end
|
||||
end
|
||||
|
||||
mcl_bubble_column.on_enter_whirlpool_with_air_above = function(self)
|
||||
local velocity = self:get_velocity()
|
||||
--self:add_velocity({x = 0, y = math.max(-3.5, (-math.abs(velocity.y))-2), z = 0})
|
||||
self:add_velocity({x = 0, y = math.max(-0.9, (-math.abs(velocity.y))-0.03), z = 0})
|
||||
end
|
||||
|
||||
minetest.register_abm{
|
||||
label = "entGo",
|
||||
nodenames = {"group:water"},
|
||||
interval = 0.05,
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
--if not bubble column block return
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("bubbly") == 1 then
|
||||
local up = minetest.get_node(vector.add(pos, {x = 0, y = 1, z = 0}))
|
||||
for _,entity in pairs(minetest.get_objects_inside_radius(pos, 0.75)) do
|
||||
if up.name == "air" then
|
||||
mcl_bubble_column.on_enter_bubble_column_with_air_above(entity)
|
||||
else
|
||||
mcl_bubble_column.on_enter_bubble_column(entity)
|
||||
end
|
||||
end
|
||||
elseif meta:get_int("whirly") == 1 then
|
||||
local up = minetest.get_node(vector.add(pos, {x = 0, y = 1, z = 0}))
|
||||
for _,entity in pairs(minetest.get_objects_inside_radius(pos, 0.75)) do
|
||||
if up.name == "air" then
|
||||
mcl_bubble_column.on_enter_whirlpool_with_air_above(entity)
|
||||
else
|
||||
mcl_bubble_column.on_enter_whirlpool(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
minetest.register_globalstep(function()
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local ppos = player:get_pos()
|
||||
local eyepos = {x = ppos.x, y = ppos.y + player:get_properties().eye_height, z = ppos.z}
|
||||
local node = minetest.get_node(ppos)
|
||||
local eyenode = minetest.get_node(eyepos)
|
||||
local meta = minetest.get_meta(ppos)
|
||||
local eyemeta = minetest.get_meta(eyepos)
|
||||
|
||||
local eyemeta = minetest.get_meta(ppos)
|
||||
--if minetest.get_item_group(node.name, "water") == 3 and minetest.get_item_group(eyenode.name, "water") == 3 then return end
|
||||
if meta:get_int("bubbly") == 1 or eyemeta:get_int("bubbly") == 1 then
|
||||
local up = minetest.get_node(vector.add(eyepos, {x = 0, y = 1, z = 0}))
|
||||
if up.name == "air" then
|
||||
mcl_bubble_column.on_enter_bubble_column_with_air_above(player)
|
||||
else
|
||||
mcl_bubble_column.on_enter_bubble_column(player)
|
||||
end
|
||||
elseif meta:get_int("whirly") == 1 or eyemeta:get_int("whirly") == 1 then
|
||||
local up = minetest.get_node(vector.add(ppos, {x = 0, y = 1, z = 0}))
|
||||
if up.name == "air" then
|
||||
mcl_bubble_column.on_enter_whirlpool_with_air_above(player)
|
||||
else
|
||||
mcl_bubble_column.on_enter_whirlpool(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--abms to remove and replace old bubble columns/whirlpools
|
||||
minetest.register_abm{
|
||||
label = "removeOldFlowingColumns",
|
||||
nodenames = {"mcl_bubble_column:water_flowing_up", "mcl_bubble_column:water_flowing_down"},
|
||||
interval = 1,--reduce lag
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
end,
|
||||
}
|
||||
minetest.register_abm{
|
||||
label = "replaceBubbleColumns",
|
||||
nodenames = {"mcl_bubble_column:water_source_up"},
|
||||
interval = 1,--reduce lag
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
minetest.set_node(pos, {name = "mcl_core:water_source"})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_int("bubbly", 1)
|
||||
end,
|
||||
}
|
||||
minetest.register_abm{
|
||||
label = "replaceWhirlpools",
|
||||
nodenames = {"mcl_bubble_column:water_source_down"},
|
||||
interval = 1,--reduce lag
|
||||
chance = 1,
|
||||
action = function(pos)
|
||||
minetest.set_node(pos, {name = "mcl_core:water_source"})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_int("whirly", 1)
|
||||
end,
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
name = mcl_bubble_column
|
|
@ -0,0 +1,2 @@
|
|||
# textdomain:mcl_explosions
|
||||
@1 was caught in an explosion.=@1 es mòrt(a) dins una petarada.
|
|
@ -148,16 +148,48 @@ local chunk_scan_range = {
|
|||
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
|
||||
}
|
||||
|
||||
local EDGE_MIN = mcl_mapgen.EDGE_MIN
|
||||
local EDGE_MAX = mcl_mapgen.EDGE_MAX
|
||||
local function is_chunk_finished(minp)
|
||||
local center = vector.add(minp, HALF_CS_NODES)
|
||||
for check_x = center.x - CS_NODES, center.x + CS_NODES, CS_NODES do
|
||||
for check_y = center.y - CS_NODES, center.y + CS_NODES, CS_NODES do
|
||||
for check_z = center.z - CS_NODES, center.z + CS_NODES, CS_NODES do
|
||||
local pos = vector.new(check_x, check_y, check_z)
|
||||
if pos ~= center then
|
||||
minetest_get_voxel_manip():read_from_map(pos, pos)
|
||||
local node = minetest_get_node(pos)
|
||||
local center_x = minp.x + HALF_CS_NODES
|
||||
local center_y = minp.y + HALF_CS_NODES
|
||||
local center_z = minp.z + HALF_CS_NODES
|
||||
local from_x = center_x - CS_NODES
|
||||
local from_y = center_y - CS_NODES
|
||||
local from_z = center_z - CS_NODES
|
||||
local to_x = center_x + CS_NODES
|
||||
local to_y = center_y + CS_NODES
|
||||
local to_z = center_z + CS_NODES
|
||||
if from_x < EDGE_MIN then from_x = center_x end
|
||||
if from_y < EDGE_MIN then from_y = center_y end
|
||||
if from_z < EDGE_MIN then from_z = center_z end
|
||||
if to_x > EDGE_MAX then to_x = center_x end
|
||||
if to_y > EDGE_MAX then to_y = center_y end
|
||||
if to_z > EDGE_MAX then to_z = center_z end
|
||||
for check_x = from_x, to_x, CS_NODES do
|
||||
local are_we_in_central_chunk = check_x == center_x
|
||||
for check_y = from_y, to_y, CS_NODES do
|
||||
are_we_in_central_chunk = are_we_in_central_chunk and (check_y == center_y)
|
||||
for check_z = from_z, to_z, CS_NODES do
|
||||
are_we_in_central_chunk = are_we_in_central_chunk and (check_z == center_z)
|
||||
if not are_we_in_central_chunk then
|
||||
local check_pos = {x = check_x, y = check_y, z = check_z}
|
||||
minetest_get_voxel_manip():read_from_map(check_pos, check_pos)
|
||||
local node = minetest_get_node(check_pos)
|
||||
if node.name == "ignore" then
|
||||
-- return nil, means false, means, there is something to generate still,
|
||||
-- (because one of adjacent chunks is unfinished - "ignore" means that),
|
||||
-- means this chunk will be changed, at least one of its sides or corners
|
||||
-- means it's unsafe to place anything there right now, it might disappar,
|
||||
-- better to wait, see the diagram of conflict/ok areas per a single axis:
|
||||
|
||||
-- conflict| ok |conflict|conflict| ok |conflict|conflict| ok |conflict
|
||||
-- (_________Chunk1_________)|(_________Chunk2_________)|(_________Chunk3_________)
|
||||
-- [Block1]|[MidBlk]|[BlockN]|[Block1]|[MidBlk]|[BlockN]|[Block1]|[MidBlk]|[BlockN]
|
||||
-- \_____________Chunk2-with-shell____________/
|
||||
-- ...______Chunk1-with-shell________/ \________Chunk3-with-shell______...
|
||||
-- Generation of chunk 1 AFFECTS 2 ^^^ ^^^ Generation of chunk 3 affects 2
|
||||
-- ^^^^^^^^Chunk 2 gen. affects 1 and 3^^^^^^^^
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -325,7 +357,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
|
|||
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
|
||||
-- --
|
||||
for _, v in pairs(queue_chunks_lvm) do
|
||||
vm_context = v.f(vm_context)
|
||||
v.f(vm_context)
|
||||
end
|
||||
-- --
|
||||
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
|
||||
|
@ -362,7 +394,17 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
|
|||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_generated = mcl_mapgen.register_chunk_generator
|
||||
local register_on_generated_old = minetest.register_on_generated
|
||||
minetest.register_on_generated = function(...)
|
||||
minetest.log("warning", "minetest.register_on_generated() is being called by the mod '"
|
||||
.. minetest.get_current_modname() .. "'. MineClone5's map generation system avoids using "
|
||||
.. "this callback to work around engine map generation issues. If possible please read "
|
||||
.. "mods/CORE/mcl_mapgen/API.md and update " .. minetest.get_current_modname()
|
||||
.. " to use the method described from there. MineClone5 makes no promises that "
|
||||
.. "mapgen mods will be fully compatible with it, please test your server and use at "
|
||||
.. "your own risk.")
|
||||
return register_on_generated_old(...)
|
||||
end
|
||||
|
||||
function mcl_mapgen.get_far_node(p)
|
||||
local p = p
|
||||
|
@ -408,6 +450,21 @@ function mcl_mapgen.get_chunk_number(pos) -- unsigned int
|
|||
c.x + k_positive
|
||||
end
|
||||
|
||||
-- Components of this game should register functions here to update their internal
|
||||
-- state when external mods modify mapgen settings that they care about.
|
||||
local settings_changed_callbacks = {}
|
||||
function mcl_mapgen.register_on_settings_changed(callback)
|
||||
table.insert(settings_changed_callbacks, callback)
|
||||
end
|
||||
|
||||
-- this is to be called by external mods after modifying these settings
|
||||
-- to notify Mineclone that it needs to update local copies and whatever's based on them.
|
||||
function mcl_mapgen.on_settings_changed()
|
||||
for _, callback in pairs(settings_changed_callbacks) do
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
mcl_mapgen.minecraft_height_limit = 256
|
||||
|
||||
mcl_mapgen.bedrock_is_rough = normal
|
||||
|
@ -416,7 +473,7 @@ mcl_mapgen.bedrock_is_rough = normal
|
|||
overworld.min = -62
|
||||
if superflat then
|
||||
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
|
||||
overworld.min = ground - 3
|
||||
overworld.min = mcl_mapgen.ground - 3
|
||||
end
|
||||
-- if singlenode then mcl_mapgen.overworld.min = -66 end -- DONT KNOW WHY
|
||||
overworld.max = mcl_mapgen.EDGE_MAX
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
mcl_util = {}
|
||||
|
||||
local minetest_get_item_group = minetest.get_item_group
|
||||
local minetest_get_meta = minetest.get_meta
|
||||
local minetest_get_node = minetest.get_node
|
||||
local minetest_get_node_timer = minetest.get_node_timer
|
||||
local table_copy = table.copy
|
||||
|
||||
-- Updates all values in t using values from to*.
|
||||
function table.update(t, ...)
|
||||
for _, to in ipairs{...} do
|
||||
|
@ -33,36 +39,6 @@ function mcl_util.rotate_axis(itemstack, placer, pointed_thing)
|
|||
return itemstack
|
||||
end
|
||||
|
||||
-- Returns position of the neighbor of a double chest node
|
||||
-- or nil if node is invalid.
|
||||
-- This function assumes that the large chest is actually intact
|
||||
-- * pos: Position of the node to investigate
|
||||
-- * param2: param2 of that node
|
||||
-- * side: Which "half" the investigated node is. "left" or "right"
|
||||
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
||||
if side == "right" then
|
||||
if param2 == 0 then
|
||||
return {x=pos.x-1, y=pos.y, z=pos.z}
|
||||
elseif param2 == 1 then
|
||||
return {x=pos.x, y=pos.y, z=pos.z+1}
|
||||
elseif param2 == 2 then
|
||||
return {x=pos.x+1, y=pos.y, z=pos.z}
|
||||
elseif param2 == 3 then
|
||||
return {x=pos.x, y=pos.y, z=pos.z-1}
|
||||
end
|
||||
else
|
||||
if param2 == 0 then
|
||||
return {x=pos.x+1, y=pos.y, z=pos.z}
|
||||
elseif param2 == 1 then
|
||||
return {x=pos.x, y=pos.y, z=pos.z-1}
|
||||
elseif param2 == 2 then
|
||||
return {x=pos.x-1, y=pos.y, z=pos.z}
|
||||
elseif param2 == 3 then
|
||||
return {x=pos.x, y=pos.y, z=pos.z+1}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterates through all items in the given inventory and
|
||||
-- returns the slot of the first item which matches a condition.
|
||||
-- Returns nil if no item was found.
|
||||
|
@ -87,7 +63,7 @@ end
|
|||
|
||||
-- Returns true if itemstack is a shulker box
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -133,136 +109,116 @@ end
|
|||
--- source_stack_id (optional): The inventory position ID of the source inventory to take the item from (-1 for slot of the first valid item; -1 is default)
|
||||
--- destination_list (optional): List name of the destination inventory. Default is normally "main"; "src" for furnace
|
||||
-- Returns true on success and false on failure.
|
||||
local SHULKER_BOX = 3
|
||||
local FURNACE = 4
|
||||
local DOUBLE_CHEST_LEFT = 5
|
||||
local DOUBLE_CHEST_RIGHT = 6
|
||||
local CONTAINER_GROUP_TO_LIST = {
|
||||
[1] = "main",
|
||||
[2] = "main",
|
||||
[SHULKER_BOX] = "main",
|
||||
[FURNACE] = "dst",
|
||||
[DOUBLE_CHEST_LEFT] = "main",
|
||||
[DOUBLE_CHEST_RIGHT] = "main",
|
||||
}
|
||||
function mcl_util.move_item_container(source_pos, destination_pos, source_list, source_stack_id, destination_list)
|
||||
local dpos = table.copy(destination_pos)
|
||||
local spos = table.copy(source_pos)
|
||||
local snode = minetest.get_node(spos)
|
||||
local dnode = minetest.get_node(dpos)
|
||||
|
||||
local dctype = minetest.get_item_group(dnode.name, "container")
|
||||
local sctype = minetest.get_item_group(snode.name, "container")
|
||||
|
||||
-- Container type 7 does not allow any movement
|
||||
if sctype == 7 then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Normalize double container by forcing to always use the left segment first
|
||||
local function normalize_double_container(pos, node, ctype)
|
||||
if ctype == 6 then
|
||||
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||
if not pos then
|
||||
return false
|
||||
end
|
||||
node = minetest.get_node(pos)
|
||||
ctype = minetest.get_item_group(node.name, "container")
|
||||
-- The left segment seems incorrect? We better bail out!
|
||||
if ctype ~= 5 then
|
||||
return false
|
||||
end
|
||||
local spos = table_copy(source_pos)
|
||||
local snode = minetest_get_node(spos)
|
||||
local sctype = minetest_get_item_group(snode.name, "container")
|
||||
local default_source_list = CONTAINER_GROUP_TO_LIST[sctype]
|
||||
if not default_source_list then return end
|
||||
if sctype == DOUBLE_CHEST_RIGHT then
|
||||
local sparam2 = snode.param2
|
||||
if sparam2 == 0 then spos.x = spos.x - 1
|
||||
elseif sparam2 == 1 then spos.z = spos.z + 1
|
||||
elseif sparam2 == 2 then spos.x = spos.x + 1
|
||||
elseif sparam2 == 3 then spos.z = spos.z - 1
|
||||
end
|
||||
return pos, node, ctype
|
||||
snode = minetest_get_node(spos)
|
||||
sctype = minetest_get_item_group(snode.name, "container")
|
||||
if sctype ~= DOUBLE_CHEST_LEFT then return end
|
||||
end
|
||||
|
||||
spos, snode, sctype = normalize_double_container(spos, snode, sctype)
|
||||
dpos, dnode, dctype = normalize_double_container(dpos, dnode, dctype)
|
||||
if not spos or not dpos then return false end
|
||||
|
||||
local smeta = minetest.get_meta(spos)
|
||||
local dmeta = minetest.get_meta(dpos)
|
||||
|
||||
local smeta = minetest_get_meta(spos)
|
||||
local sinv = smeta:get_inventory()
|
||||
local source_list = source_list or default_source_list
|
||||
|
||||
local dpos = table_copy(destination_pos)
|
||||
local dnode = minetest_get_node(dpos)
|
||||
local dctype = minetest_get_item_group(dnode.name, "container")
|
||||
local default_destination_list = CONTAINER_GROUP_TO_LIST[sctype]
|
||||
if not default_destination_list then return end
|
||||
if dctype == DOUBLE_CHEST_RIGHT then
|
||||
local dparam2 = dnode.param2
|
||||
if dparam2 == 0 then dpos.x = dpos.x - 1
|
||||
elseif dparam2 == 1 then dpos.z = dpos.z + 1
|
||||
elseif dparam2 == 2 then dpos.x = dpos.x + 1
|
||||
elseif dparam2 == 3 then dpos.z = dpos.z - 1
|
||||
end
|
||||
dnode = minetest_get_node(dpos)
|
||||
dctype = minetest_get_item_group(dnode.name, "container")
|
||||
if dctype ~= DOUBLE_CHEST_LEFT then return end
|
||||
end
|
||||
local dmeta = minetest_get_meta(dpos)
|
||||
local dinv = dmeta:get_inventory()
|
||||
|
||||
-- Default source lists
|
||||
if not source_list then
|
||||
-- Main inventory for most container types
|
||||
if sctype == 2 or sctype == 3 or sctype == 5 or sctype == 6 or sctype == 7 then
|
||||
source_list = "main"
|
||||
-- Furnace: output
|
||||
elseif sctype == 4 then
|
||||
source_list = "dst"
|
||||
-- Unknown source container type. Bail out
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Automatically select stack slot ID if set to automatic
|
||||
if not source_stack_id then
|
||||
source_stack_id = -1
|
||||
end
|
||||
local source_stack_id = source_stack_id or -1
|
||||
if source_stack_id == -1 then
|
||||
local cond = nil
|
||||
-- Prevent shulker box inception
|
||||
if dctype == 3 then
|
||||
cond = is_not_shulker_box
|
||||
end
|
||||
if dctype == SHULKER_BOX then cond = is_not_shulker_box end
|
||||
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
||||
if not source_stack_id then
|
||||
-- Try again if source is a double container
|
||||
if sctype == 5 then
|
||||
spos = mcl_util.get_double_container_neighbor_pos(spos, snode.param2, "left")
|
||||
smeta = minetest.get_meta(spos)
|
||||
sinv = smeta:get_inventory()
|
||||
|
||||
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
||||
if not source_stack_id then
|
||||
return false
|
||||
if sctype == DOUBLE_CHEST_LEFT then
|
||||
local sparam2 = snode.param2
|
||||
if sparam2 == 0 then spos.x = spos.x + 1
|
||||
elseif sparam2 == 1 then spos.z = spos.z - 1
|
||||
elseif sparam2 == 2 then spos.x = spos.x - 1
|
||||
elseif sparam2 == 3 then spos.z = spos.z + 1
|
||||
end
|
||||
else
|
||||
return false
|
||||
snode = minetest_get_node(spos)
|
||||
sctype = minetest_get_item_group(snode.name, "container")
|
||||
if sctype ~= DOUBLE_CHEST_RIGHT then return end
|
||||
smeta = minetest_get_meta(spos)
|
||||
sinv = smeta:get_inventory()
|
||||
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
||||
end
|
||||
end
|
||||
if not source_stack_id then return end
|
||||
end
|
||||
|
||||
-- Abort transfer if shulker box wants to go into shulker box
|
||||
if dctype == 3 then
|
||||
if dctype == SHULKER_BOX then
|
||||
local stack = sinv:get_stack(source_list, source_stack_id)
|
||||
if stack and minetest.get_item_group(stack:get_name(), "shulker_box") == 1 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- Container type 7 does not allow any placement
|
||||
if dctype == 7 then
|
||||
return false
|
||||
if stack and minetest_get_item_group(stack:get_name(), "shulker_box") == 1 then return end
|
||||
end
|
||||
|
||||
-- If it's a container, put it into the container
|
||||
if dctype ~= 0 then
|
||||
-- Automatically select a destination list if omitted
|
||||
if not destination_list then
|
||||
-- Main inventory for most container types
|
||||
if dctype == 2 or dctype == 3 or dctype == 5 or dctype == 6 or dctype == 7 then
|
||||
destination_list = "main"
|
||||
-- Furnace source slot
|
||||
elseif dctype == 4 then
|
||||
destination_list = "src"
|
||||
local destination_list = destination_list or default_destination_list
|
||||
-- Move item
|
||||
local ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||
-- Try transfer to neighbor node if transfer failed and double container
|
||||
if not ok then
|
||||
if dctype == DOUBLE_CHEST_LEFT then
|
||||
local dparam2 = dnode.param2
|
||||
if dparam2 == 0 then dpos.x = dpos.x + 1
|
||||
elseif dparam2 == 1 then dpos.z = dpos.z - 1
|
||||
elseif dparam2 == 2 then dpos.x = dpos.x - 1
|
||||
elseif dparam2 == 3 then dpos.z = dpos.z + 1
|
||||
end
|
||||
end
|
||||
if destination_list then
|
||||
-- Move item
|
||||
local ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||
|
||||
-- Try transfer to neighbor node if transfer failed and double container
|
||||
if not ok and dctype == 5 then
|
||||
dpos = mcl_util.get_double_container_neighbor_pos(dpos, dnode.param2, "left")
|
||||
dmeta = minetest.get_meta(dpos)
|
||||
dinv = dmeta:get_inventory()
|
||||
|
||||
ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||
end
|
||||
|
||||
-- Update furnace
|
||||
if ok and dctype == 4 then
|
||||
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
|
||||
minetest.get_node_timer(dpos):start(1.0)
|
||||
end
|
||||
|
||||
return ok
|
||||
dnode = minetest_get_node(dpos)
|
||||
dctype = minetest_get_item_group(dnode.name, "container")
|
||||
if dctype ~= DOUBLE_CHEST_RIGHT then return end
|
||||
dmeta = minetest_get_meta(dpos)
|
||||
dinv = dmeta:get_inventory()
|
||||
ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||
end
|
||||
end
|
||||
return false
|
||||
-- Update furnace
|
||||
if ok and dctype == FURNACE then
|
||||
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
|
||||
minetest_get_node_timer(dpos):start(1.0)
|
||||
end
|
||||
return ok
|
||||
end
|
||||
|
||||
-- Returns the ID of the first non-empty slot in the given inventory list
|
||||
|
@ -292,7 +248,7 @@ function mcl_util.generate_on_place_plant_function(condition)
|
|||
end
|
||||
|
||||
-- Call on_rightclick if the pointed node defines it
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
local node = minetest_get_node(pointed_thing.under)
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||
|
@ -300,8 +256,8 @@ function mcl_util.generate_on_place_plant_function(condition)
|
|||
end
|
||||
|
||||
local place_pos
|
||||
local def_under = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
|
||||
local def_above = minetest.registered_nodes[minetest.get_node(pointed_thing.above).name]
|
||||
local def_under = minetest.registered_nodes[minetest_get_node(pointed_thing.under).name]
|
||||
local def_above = minetest.registered_nodes[minetest_get_node(pointed_thing.above).name]
|
||||
if not def_under or not def_above then
|
||||
return itemstack
|
||||
end
|
||||
|
@ -359,7 +315,7 @@ function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
|
|||
-- Call on_rightclick if the pointed node defines it
|
||||
if pointed_thing and pointed_thing.type == "node" then
|
||||
local pos = pointed_thing.under
|
||||
local node = minetest.get_node(pos)
|
||||
local node = minetest_get_node(pos)
|
||||
if player and not player:get_player_control().sneak then
|
||||
local nodedef = minetest.registered_nodes[node.name]
|
||||
local on_rightclick = nodedef and nodedef.on_rightclick
|
||||
|
@ -372,7 +328,7 @@ end
|
|||
|
||||
function mcl_util.calculate_durability(itemstack)
|
||||
local unbreaking_level = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
|
||||
local armor_uses = minetest.get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
||||
local armor_uses = minetest_get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
||||
|
||||
local uses
|
||||
|
||||
|
@ -417,6 +373,7 @@ function mcl_util.deal_damage(target, damage, 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
|
||||
luaentity.pause_timer = 0.4
|
||||
end
|
||||
return
|
||||
end
|
||||
|
|
|
@ -2,11 +2,18 @@ mcl_worlds = {}
|
|||
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
|
||||
local min1, min2, min3
|
||||
local max1, max2, max3
|
||||
local get_local_settings = function()
|
||||
min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
||||
max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
||||
end
|
||||
get_local_settings()
|
||||
mcl_mapgen.register_on_settings_changed(get_local_settings)
|
||||
|
||||
-- For a given position, returns a 2-tuple:
|
||||
-- 1st return value: true if pos is in void
|
||||
-- 2nd return value: true if it is in the deadly part of the void
|
||||
local min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
||||
local max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
||||
function mcl_worlds.is_in_void(pos)
|
||||
local y = pos.y
|
||||
local void = not ((y < max1 and y > min1) or (y < max2 and y > min2) or (y < max3 and y > min3))
|
||||
|
@ -152,3 +159,23 @@ minetest.register_globalstep(function(dtime)
|
|||
dimtimer = 0
|
||||
end
|
||||
end)
|
||||
|
||||
function mcl_worlds.get_cloud_parameters()
|
||||
if mcl_mapgen.name == "valleys" then
|
||||
return {
|
||||
height = 384,
|
||||
speed = {x=-2, z=0},
|
||||
thickness=5,
|
||||
color="#FFF0FEF",
|
||||
ambient = "#201060",
|
||||
}
|
||||
else
|
||||
-- MC-style clouds: Layer 127, thickness 4, fly to the “West”
|
||||
return {
|
||||
height = mcl_worlds.layer_to_y(127),
|
||||
speed = {x=-2, z=0},
|
||||
thickness = 4,
|
||||
color = "#FFF0FEF",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
# tga_encoder
|
||||
A TGA Encoder written in Lua without the use of external Libraries.
|
||||
|
||||
Created by fleckenstein for MineClone2, then improved by erlehmann.
|
||||
|
||||
May be used as a Minetest mod.
|
||||
|
||||
See `examples.lua` for example code and usage hints.
|
||||
|
||||
## Use Cases for `tga_encoder`
|
||||
|
||||
### Encoding Textures for Editing
|
||||
|
||||
TGA images of types 1/2/3 consist of header data followed by a pixel array.
|
||||
|
||||
This makes it trivial to parse TGA files – and even edit pixels in-place.
|
||||
|
||||
No checksums need to be updated on any kind of in-place texture editing.
|
||||
|
||||
**Tip**: When storing an editable image in item meta, use zlib compression.
|
||||
|
||||
### Legacy Minetest Texture Encoding
|
||||
|
||||
Minetest 5.4 did not include `minetest.encode_png()` (or any equvivalent).
|
||||
|
||||
Since `tga_encoder` is written in pure Lua, it does not need engine support.
|
||||
|
||||
**Tip:** Look at `examples.lua` and the Minetest mod `mcl_maps` for guidance.
|
||||
|
||||
### Advanced Texture Format Control
|
||||
|
||||
The function `minetest.encode_png()` always encodes images as 32bpp RGBA.
|
||||
|
||||
`tga_encoder` allows saving images as grayscale, 16bpp RGBA and 24bpp RGB.
|
||||
|
||||
For generating maps from terrain, color-mapped formats can be more useful.
|
||||
|
||||
### Encoding Very Small Textures
|
||||
|
||||
Images of size 8×8 or below are often smaller than an equivalent PNG file.
|
||||
|
||||
Note that on many filesystems files use at least 4096 bytes (i.e. 64×64).
|
||||
|
||||
Therefore, saving bytes on files up to a few 100 bytes is often useless.
|
||||
|
||||
### Encoding Reference Textures
|
||||
|
||||
TGA is a simple format, which makes it easy to create reference textures.
|
||||
|
||||
Using a hex editor, one can trivially see how all the pixels are stored.
|
||||
|
||||
## Supported Image Types
|
||||
|
||||
For all types, images are encoded in a fast single pass (i.e. append-only).
|
||||
|
||||
### Color-Mapped Images (Type 1)
|
||||
|
||||
These images contain a palette, followed by pixel data.
|
||||
|
||||
* `A1R5G5B5` (8bpp RGB)
|
||||
* `B8G8R8` (8bpp RGB)
|
||||
* `B8G8R8A8` (8bpp RGBA)
|
||||
|
||||
### True-Color Images (Type 2)
|
||||
|
||||
These images contain uncompressed RGB(A) pixel data.
|
||||
|
||||
* `A1R5G5B5` (16bpp RGBA)
|
||||
* `B8G8R8` (24bpp RGB)
|
||||
* `B8G8R8A8` (32bpp RGBA)
|
||||
|
||||
### Grayscale Images (Type 3)
|
||||
|
||||
* `Y8` (8bpp grayscale)
|
||||
|
||||
### Run-Length Encoded (RLE), True-Color Images (Type 10)
|
||||
|
||||
These images contain compressed RGB(A) pixel data.
|
||||
|
||||
* `A1R5G5B5` (16bpp RGBA)
|
||||
* `B8G8R8` (24bpp RGB)
|
||||
* `B8G8R8A8` (32bpp RGBA)
|
||||
|
||||
## TODO
|
||||
|
||||
* Actually support `R8G8B8A8` input for `A1R5G5B5` output
|
||||
* Add both zoomable and explorable maps to `mcl_maps`.
|
|
@ -0,0 +1,150 @@
|
|||
dofile("init.lua")
|
||||
|
||||
-- encode a bitmap
|
||||
local _ = { 0, 0, 0 }
|
||||
local R = { 255, 127, 127 }
|
||||
local pixels = {
|
||||
{ _, _, _, _, _, _, _ },
|
||||
{ _, _, _, R, _, _, _ },
|
||||
{ _, _, R, R, R, _, _ },
|
||||
{ _, R, R, R, R, R, _ },
|
||||
{ _, R, R, R, R, R, _ },
|
||||
{ _, _, R, _, R, _, _ },
|
||||
{ _, _, _, _, _, _, _ },
|
||||
}
|
||||
tga_encoder.image(pixels):save("bitmap_small.tga")
|
||||
|
||||
-- change a single pixel, then rescale the bitmap
|
||||
local pixels_orig = pixels
|
||||
pixels_orig[4][4] = { 255, 255, 255 }
|
||||
local pixels = {}
|
||||
for x = 1,56,1 do
|
||||
local x_orig = math.ceil(x/8)
|
||||
for z = 1,56,1 do
|
||||
local z_orig = math.ceil(z/8)
|
||||
local color = pixels_orig[z_orig][x_orig]
|
||||
pixels[z] = pixels[z] or {}
|
||||
pixels[z][x] = color
|
||||
end
|
||||
end
|
||||
tga_encoder.image(pixels):save("bitmap_large.tga")
|
||||
|
||||
-- note that the uncompressed grayscale TGA file written in this
|
||||
-- example is 80 bytes – but an optimized PNG file is 81 bytes …
|
||||
local pixels = {}
|
||||
for x = 1,6,1 do -- left to right
|
||||
for z = 1,6,1 do -- bottom to top
|
||||
local color = { math.min(x * z * 4 - 1, 255) }
|
||||
pixels[z] = pixels[z] or {}
|
||||
pixels[z][x] = color
|
||||
end
|
||||
end
|
||||
tga_encoder.image(pixels):save("gradient_8bpp_raw.tga", {color_format="Y8", compression="RAW"})
|
||||
|
||||
local pixels = {}
|
||||
for x = 1,16,1 do -- left to right
|
||||
for z = 1,16,1 do -- bottom to top
|
||||
local r = math.min(x * 32 - 1, 255)
|
||||
local g = math.min(z * 32 - 1, 255)
|
||||
local b = 0
|
||||
-- blue rectangle in top right corner
|
||||
if x > 8 and z > 8 then
|
||||
r = 0
|
||||
g = 0
|
||||
b = math.min(z * 16 - 1, 255)
|
||||
end
|
||||
local color = { r, g, b }
|
||||
pixels[z] = pixels[z] or {}
|
||||
pixels[z][x] = color
|
||||
end
|
||||
end
|
||||
local gradients = tga_encoder.image(pixels)
|
||||
gradients:save("gradients_8bpp_raw.tga", {color_format="Y8", compression="RAW"})
|
||||
gradients:save("gradients_16bpp_raw.tga", {color_format="A1R5G5B5", compression="RAW"})
|
||||
gradients:save("gradients_16bpp_rle.tga", {color_format="A1R5G5B5", compression="RLE"})
|
||||
gradients:save("gradients_24bpp_raw.tga", {color_format="B8G8R8", compression="RAW"})
|
||||
gradients:save("gradients_24bpp_rle.tga", {color_format="B8G8R8", compression="RLE"})
|
||||
|
||||
for x = 1,16,1 do -- left to right
|
||||
for z = 1,16,1 do -- bottom to top
|
||||
local color = pixels[z][x]
|
||||
color[#color+1] = ((x * x) + (z * z)) % 256
|
||||
pixels[z][x] = color
|
||||
end
|
||||
end
|
||||
gradients:save("gradients_32bpp_raw.tga", {color_format="B8G8R8A8", compression="RAW"})
|
||||
-- the RLE-compressed file is larger than just dumping pixels because
|
||||
-- the gradients in this picture can not be compressed well using RLE
|
||||
gradients:save("gradients_32bpp_rle.tga", {color_format="B8G8R8A8", compression="RLE"})
|
||||
|
||||
local pixels = {}
|
||||
for x = 1,512,1 do -- left to right
|
||||
for z = 1,512,1 do -- bottom to top
|
||||
local oz = (z - 256) / 256 + 0.75
|
||||
local ox = (x - 256) / 256
|
||||
local px, pz, i = 0, 0, 0
|
||||
while (px * px) + (pz * pz) <= 4 and i < 128 do
|
||||
px = (px * px) - (pz * pz) + oz
|
||||
pz = (2 * px * pz) + ox
|
||||
i = i + 1
|
||||
end
|
||||
local color = {
|
||||
math.max(0, math.min(255, math.floor(px * 64))),
|
||||
math.max(0, math.min(255, math.floor(pz * 64))),
|
||||
math.max(0, math.min(255, math.floor(i))),
|
||||
}
|
||||
pixels[z] = pixels[z] or {}
|
||||
pixels[z][x] = color
|
||||
end
|
||||
end
|
||||
tga_encoder.image(pixels):save("fractal_8bpp.tga", {color_format="Y8"})
|
||||
tga_encoder.image(pixels):save("fractal_16bpp.tga", {color_format="A1R5G5B5"})
|
||||
tga_encoder.image(pixels):save("fractal_24bpp.tga", {color_format="B8G8R8"})
|
||||
|
||||
-- encode a colormapped bitmap
|
||||
local K = { 0 }
|
||||
local B = { 1 }
|
||||
local R = { 2 }
|
||||
local G = { 3 }
|
||||
local W = { 4 }
|
||||
local colormap = {
|
||||
{ 1, 2, 3 }, -- K
|
||||
{ 0, 0, 255 }, -- B
|
||||
{ 255, 0, 0 }, -- R
|
||||
{ 0, 255, 0 }, -- G
|
||||
{ 253, 254, 255 }, -- W
|
||||
}
|
||||
local pixels = {
|
||||
{ W, K, W, K, W, K, W },
|
||||
{ R, G, B, R, G, B, K },
|
||||
{ K, W, K, W, K, W, K },
|
||||
{ G, B, R, G, B, R, W },
|
||||
{ W, W, W, K, K, K, W },
|
||||
{ B, R, G, B, R, G, K },
|
||||
{ B, R, G, B, R, G, W },
|
||||
}
|
||||
-- note that the uncompressed colormapped TGA file written in this
|
||||
-- example is 108 bytes – but an optimized PNG file is 121 bytes …
|
||||
tga_encoder.image(pixels):save("colormapped_B8G8R8.tga", {colormap=colormap})
|
||||
-- encoding as A1R5G5B5 saves 1 byte per palette entry → 103 bytes
|
||||
tga_encoder.image(pixels):save("colormapped_A1R5G5B5.tga", {colormap=colormap, color_format="A1R5G5B5"})
|
||||
|
||||
-- encode a colormapped bitmap with transparency
|
||||
local _ = { 0 }
|
||||
local K = { 1 }
|
||||
local W = { 2 }
|
||||
local colormap = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 255 },
|
||||
{ 255, 255, 255, 255 },
|
||||
}
|
||||
local pixels = {
|
||||
{ _, K, K, K, K, K, _ },
|
||||
{ _, K, W, W, W, K, _ },
|
||||
{ K, K, W, W, W, K, K },
|
||||
{ K, W, W, W, W, W, K },
|
||||
{ _, K, W, W, W, K, _ },
|
||||
{ _, _, K, W, K, _, _ },
|
||||
{ _, _, _, K, _, _, _ },
|
||||
}
|
||||
tga_encoder.image(pixels):save("colormapped_B8G8R8A8.tga", {colormap=colormap})
|
|
@ -0,0 +1,594 @@
|
|||
tga_encoder = {}
|
||||
|
||||
local image = setmetatable({}, {
|
||||
__call = function(self, ...)
|
||||
local t = setmetatable({}, {__index = self})
|
||||
t:constructor(...)
|
||||
return t
|
||||
end,
|
||||
})
|
||||
|
||||
function image:constructor(pixels)
|
||||
self.pixels = pixels
|
||||
self.width = #pixels[1]
|
||||
self.height = #pixels
|
||||
end
|
||||
|
||||
local pixel_depth_by_color_format = {
|
||||
["Y8"] = 8,
|
||||
["A1R5G5B5"] = 16,
|
||||
["B8G8R8"] = 24,
|
||||
["B8G8R8A8"] = 32,
|
||||
}
|
||||
|
||||
function image:encode_colormap_spec(properties)
|
||||
local colormap = properties.colormap
|
||||
local colormap_pixel_depth = 0
|
||||
if 0 ~= #colormap then
|
||||
colormap_pixel_depth = pixel_depth_by_color_format[
|
||||
properties.color_format
|
||||
]
|
||||
end
|
||||
local colormap_spec =
|
||||
string.char(0, 0) .. -- first entry index
|
||||
string.char(#colormap % 256, math.floor(#colormap / 256)) .. -- number of entries
|
||||
string.char(colormap_pixel_depth) -- bits per pixel
|
||||
self.data = self.data .. colormap_spec
|
||||
end
|
||||
|
||||
function image:encode_image_spec(properties)
|
||||
local color_format = properties.color_format
|
||||
assert(
|
||||
"Y8" == color_format or -- (8 bit grayscale = 1 byte = 8 bits)
|
||||
"A1R5G5B5" == color_format or -- (A1R5G5B5 = 2 bytes = 16 bits)
|
||||
"B8G8R8" == color_format or -- (B8G8R8 = 3 bytes = 24 bits)
|
||||
"B8G8R8A8" == color_format -- (B8G8R8A8 = 4 bytes = 32 bits)
|
||||
)
|
||||
local pixel_depth
|
||||
if 0 ~= #properties.colormap then
|
||||
pixel_depth = self.pixel_depth
|
||||
else
|
||||
pixel_depth = pixel_depth_by_color_format[color_format]
|
||||
end
|
||||
assert( nil ~= pixel_depth)
|
||||
self.data = self.data
|
||||
.. string.char(0, 0) -- X-origin
|
||||
.. string.char(0, 0) -- Y-origin
|
||||
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width
|
||||
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height
|
||||
.. string.char(pixel_depth)
|
||||
.. string.char(0) -- image descriptor
|
||||
end
|
||||
|
||||
function image:encode_colormap(properties)
|
||||
local colormap = properties.colormap
|
||||
if 0 == #colormap then
|
||||
return
|
||||
end
|
||||
local color_format = properties.color_format
|
||||
assert (
|
||||
"A1R5G5B5" == color_format or
|
||||
"B8G8R8" == color_format or
|
||||
"B8G8R8A8" == color_format
|
||||
)
|
||||
local colors = {}
|
||||
if "A1R5G5B5" == color_format then
|
||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
||||
local max_sample_in = math.pow(2, 8) - 1
|
||||
local max_sample_out = math.pow(2, 5) - 1
|
||||
for i = 1,#colormap,1 do
|
||||
local color = colormap[i]
|
||||
local colorword = 32768 +
|
||||
((math.floor((color[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
||||
((math.floor((color[2] * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
||||
((math.floor((color[3] * max_sample_out / max_sample_in) + 0.5)) * 1)
|
||||
local color_bytes = string.char(
|
||||
colorword % 256,
|
||||
math.floor(colorword / 256)
|
||||
)
|
||||
colors[#colors + 1] = color_bytes
|
||||
end
|
||||
elseif "B8G8R8" == color_format then
|
||||
for i = 1,#colormap,1 do
|
||||
local color = colormap[i]
|
||||
local color_bytes = string.char(
|
||||
color[3], -- B
|
||||
color[2], -- G
|
||||
color[1] -- R
|
||||
)
|
||||
colors[#colors + 1] = color_bytes
|
||||
end
|
||||
elseif "B8G8R8A8" == color_format then
|
||||
for i = 1,#colormap,1 do
|
||||
local color = colormap[i]
|
||||
local color_bytes = string.char(
|
||||
color[3], -- B
|
||||
color[2], -- G
|
||||
color[1], -- R
|
||||
color[4] -- A
|
||||
)
|
||||
colors[#colors + 1] = color_bytes
|
||||
end
|
||||
end
|
||||
assert( 0 ~= #colors )
|
||||
self.data = self.data .. table.concat(colors)
|
||||
end
|
||||
|
||||
function image:encode_header(properties)
|
||||
local color_format = properties.color_format
|
||||
local colormap = properties.colormap
|
||||
local compression = properties.compression
|
||||
local colormap_type
|
||||
local image_type
|
||||
if "Y8" == color_format and "RAW" == compression then
|
||||
colormap_type = 0
|
||||
image_type = 3 -- grayscale
|
||||
elseif (
|
||||
"A1R5G5B5" == color_format or
|
||||
"B8G8R8" == color_format or
|
||||
"B8G8R8A8" == color_format
|
||||
) then
|
||||
if "RAW" == compression then
|
||||
if 0 ~= #colormap then
|
||||
colormap_type = 1
|
||||
image_type = 1 -- colormapped RGB(A)
|
||||
else
|
||||
colormap_type = 0
|
||||
image_type = 2 -- RAW RGB(A)
|
||||
end
|
||||
elseif "RLE" == compression then
|
||||
colormap_type = 0
|
||||
image_type = 10 -- RLE RGB
|
||||
end
|
||||
end
|
||||
assert( nil ~= colormap_type )
|
||||
assert( nil ~= image_type )
|
||||
self.data = self.data
|
||||
.. string.char(0) -- image id
|
||||
.. string.char(colormap_type)
|
||||
.. string.char(image_type)
|
||||
self:encode_colormap_spec(properties) -- color map specification
|
||||
self:encode_image_spec(properties) -- image specification
|
||||
self:encode_colormap(properties)
|
||||
end
|
||||
|
||||
function image:encode_data(properties)
|
||||
local color_format = properties.color_format
|
||||
local colormap = properties.colormap
|
||||
local compression = properties.compression
|
||||
|
||||
local data_length_before = #self.data
|
||||
if "Y8" == color_format and "RAW" == compression then
|
||||
if 8 == self.pixel_depth then
|
||||
self:encode_data_Y8_as_Y8_raw()
|
||||
elseif 24 == self.pixel_depth then
|
||||
self:encode_data_R8G8B8_as_Y8_raw()
|
||||
end
|
||||
elseif "A1R5G5B5" == color_format then
|
||||
if 0 ~= #colormap then
|
||||
if "RAW" == compression then
|
||||
if 8 == self.pixel_depth then
|
||||
self:encode_data_Y8_as_Y8_raw()
|
||||
end
|
||||
end
|
||||
else
|
||||
if "RAW" == compression then
|
||||
self:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
||||
elseif "RLE" == compression then
|
||||
self:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
||||
end
|
||||
end
|
||||
elseif "B8G8R8" == color_format then
|
||||
if 0 ~= #colormap then
|
||||
if "RAW" == compression then
|
||||
if 8 == self.pixel_depth then
|
||||
self:encode_data_Y8_as_Y8_raw()
|
||||
end
|
||||
end
|
||||
else
|
||||
if "RAW" == compression then
|
||||
self:encode_data_R8G8B8_as_B8G8R8_raw()
|
||||
elseif "RLE" == compression then
|
||||
self:encode_data_R8G8B8_as_B8G8R8_rle()
|
||||
end
|
||||
end
|
||||
elseif "B8G8R8A8" == color_format then
|
||||
if 0 ~= #colormap then
|
||||
if "RAW" == compression then
|
||||
if 8 == self.pixel_depth then
|
||||
self:encode_data_Y8_as_Y8_raw()
|
||||
end
|
||||
end
|
||||
else
|
||||
if "RAW" == compression then
|
||||
self:encode_data_R8G8B8A8_as_B8G8R8A8_raw()
|
||||
elseif "RLE" == compression then
|
||||
self:encode_data_R8G8B8A8_as_B8G8R8A8_rle()
|
||||
end
|
||||
end
|
||||
end
|
||||
local data_length_after = #self.data
|
||||
assert(
|
||||
data_length_after ~= data_length_before,
|
||||
"No data encoded for color format: " .. color_format
|
||||
)
|
||||
end
|
||||
|
||||
function image:encode_data_Y8_as_Y8_raw()
|
||||
assert(8 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
local raw_pixel = string.char(pixel[1])
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
end
|
||||
end
|
||||
self.data = self.data .. table.concat(raw_pixels)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8_as_Y8_raw()
|
||||
assert(24 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
-- the HSP RGB to brightness formula is
|
||||
-- sqrt( 0.299 r² + .587 g² + .114 b² )
|
||||
-- see <https://alienryderflex.com/hsp.html>
|
||||
local gray = math.floor(
|
||||
math.sqrt(
|
||||
0.299 * pixel[1]^2 +
|
||||
0.587 * pixel[2]^2 +
|
||||
0.114 * pixel[3]^2
|
||||
) + 0.5
|
||||
)
|
||||
local raw_pixel = string.char(gray)
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
end
|
||||
end
|
||||
self.data = self.data .. table.concat(raw_pixels)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
||||
assert(24 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
||||
local max_sample_in = math.pow(2, 8) - 1
|
||||
local max_sample_out = math.pow(2, 5) - 1
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
local colorword = 32768 +
|
||||
((math.floor((pixel[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
||||
((math.floor((pixel[2] * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
||||
((math.floor((pixel[3] * max_sample_out / max_sample_in) + 0.5)) * 1)
|
||||
local raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
end
|
||||
end
|
||||
self.data = self.data .. table.concat(raw_pixels)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
||||
assert(24 == self.pixel_depth)
|
||||
local colorword = nil
|
||||
local previous_r = nil
|
||||
local previous_g = nil
|
||||
local previous_b = nil
|
||||
local raw_pixel = ''
|
||||
local raw_pixels = {}
|
||||
local count = 1
|
||||
local packets = {}
|
||||
local raw_packet = ''
|
||||
local rle_packet = ''
|
||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
||||
local max_sample_in = math.pow(2, 8) - 1
|
||||
local max_sample_out = math.pow(2, 5) - 1
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or count == 128 then
|
||||
if nil ~= previous_r then
|
||||
colorword = 32768 +
|
||||
((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
||||
((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
||||
((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1)
|
||||
if 1 == count then
|
||||
-- remember pixel verbatim for raw encoding
|
||||
raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
if 128 == #raw_pixels then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256))
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
end
|
||||
count = 1
|
||||
previous_r = pixel[1]
|
||||
previous_g = pixel[2]
|
||||
previous_b = pixel[3]
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
colorword = 32768 +
|
||||
((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
||||
((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
||||
((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1)
|
||||
if 1 == count then
|
||||
raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256))
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
self.data = self.data .. table.concat(packets)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8_as_B8G8R8_raw()
|
||||
assert(24 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
local raw_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
end
|
||||
end
|
||||
self.data = self.data .. table.concat(raw_pixels)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8_as_B8G8R8_rle()
|
||||
assert(24 == self.pixel_depth)
|
||||
local previous_r = nil
|
||||
local previous_g = nil
|
||||
local previous_b = nil
|
||||
local raw_pixel = ''
|
||||
local raw_pixels = {}
|
||||
local count = 1
|
||||
local packets = {}
|
||||
local raw_packet = ''
|
||||
local rle_packet = ''
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or count == 128 then
|
||||
if nil ~= previous_r then
|
||||
if 1 == count then
|
||||
-- remember pixel verbatim for raw encoding
|
||||
raw_pixel = string.char(previous_b, previous_g, previous_r)
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
if 128 == #raw_pixels then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r)
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
end
|
||||
count = 1
|
||||
previous_r = pixel[1]
|
||||
previous_g = pixel[2]
|
||||
previous_b = pixel[3]
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if 1 == count then
|
||||
raw_pixel = string.char(previous_b, previous_g, previous_r)
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r)
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
self.data = self.data .. table.concat(packets)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8A8_as_B8G8R8A8_raw()
|
||||
assert(32 == self.pixel_depth)
|
||||
local raw_pixels = {}
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
local raw_pixel = string.char(pixel[3], pixel[2], pixel[1], pixel[4])
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
end
|
||||
end
|
||||
self.data = self.data .. table.concat(raw_pixels)
|
||||
end
|
||||
|
||||
function image:encode_data_R8G8B8A8_as_B8G8R8A8_rle()
|
||||
assert(32 == self.pixel_depth)
|
||||
local previous_r = nil
|
||||
local previous_g = nil
|
||||
local previous_b = nil
|
||||
local previous_a = nil
|
||||
local raw_pixel = ''
|
||||
local raw_pixels = {}
|
||||
local count = 1
|
||||
local packets = {}
|
||||
local raw_packet = ''
|
||||
local rle_packet = ''
|
||||
for _, row in ipairs(self.pixels) do
|
||||
for _, pixel in ipairs(row) do
|
||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or pixel[4] ~= previous_a or count == 128 then
|
||||
if nil ~= previous_r then
|
||||
if 1 == count then
|
||||
-- remember pixel verbatim for raw encoding
|
||||
raw_pixel = string.char(previous_b, previous_g, previous_r, previous_a)
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
if 128 == #raw_pixels then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r, previous_a)
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
end
|
||||
count = 1
|
||||
previous_r = pixel[1]
|
||||
previous_g = pixel[2]
|
||||
previous_b = pixel[3]
|
||||
previous_a = pixel[4]
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if 1 == count then
|
||||
raw_pixel = string.char(previous_b, previous_g, previous_r, previous_a)
|
||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
else
|
||||
-- encode raw pixels, if any
|
||||
if #raw_pixels > 0 then
|
||||
raw_packet = string.char(#raw_pixels - 1)
|
||||
packets[#packets + 1] = raw_packet
|
||||
for i=1, #raw_pixels do
|
||||
packets[#packets +1] = raw_pixels[i]
|
||||
end
|
||||
raw_pixels = {}
|
||||
end
|
||||
-- RLE encoding
|
||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r, previous_a)
|
||||
packets[#packets +1] = rle_packet
|
||||
end
|
||||
self.data = self.data .. table.concat(packets)
|
||||
end
|
||||
|
||||
function image:encode_footer()
|
||||
self.data = self.data
|
||||
.. string.char(0, 0, 0, 0) -- extension area offset
|
||||
.. string.char(0, 0, 0, 0) -- developer area offset
|
||||
.. "TRUEVISION-XFILE"
|
||||
.. "."
|
||||
.. string.char(0)
|
||||
end
|
||||
|
||||
function image:encode(properties)
|
||||
self.data = ""
|
||||
self:encode_header(properties) -- header
|
||||
-- no color map and image id data
|
||||
self:encode_data(properties) -- encode data
|
||||
-- no extension or developer area
|
||||
self:encode_footer() -- footer
|
||||
end
|
||||
|
||||
function image:save(filename, properties)
|
||||
local properties = properties or {}
|
||||
properties.colormap = properties.colormap or {}
|
||||
properties.compression = properties.compression or "RAW"
|
||||
|
||||
self.pixel_depth = #self.pixels[1][1] * 8
|
||||
|
||||
local color_format_defaults_by_pixel_depth = {
|
||||
[8] = "Y8",
|
||||
[24] = "B8G8R8",
|
||||
[32] = "B8G8R8A8",
|
||||
}
|
||||
if nil == properties.color_format then
|
||||
if 0 ~= #properties.colormap then
|
||||
properties.color_format =
|
||||
color_format_defaults_by_pixel_depth[
|
||||
#properties.colormap[1] * 8
|
||||
]
|
||||
else
|
||||
properties.color_format =
|
||||
color_format_defaults_by_pixel_depth[
|
||||
self.pixel_depth
|
||||
]
|
||||
end
|
||||
end
|
||||
assert( nil ~= properties.color_format )
|
||||
|
||||
self:encode(properties)
|
||||
|
||||
local f = assert(io.open(filename, "wb"))
|
||||
f:write(self.data)
|
||||
f:close()
|
||||
end
|
||||
|
||||
tga_encoder.image = image
|
|
@ -0,0 +1,2 @@
|
|||
name = tga_encoder
|
||||
description = A TGA Encoder written in Lua without the use of external Libraries.
|
|
@ -30,26 +30,26 @@ local S = minetest.get_translator("extra_mobs")
|
|||
--###################
|
||||
|
||||
local cod = {
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
rotate = 270,
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_cod.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_cod.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.3, 0.0, -0.3, 0.3, 0.79, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_cod.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_cod.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
stand_start = 1,
|
||||
stand_end = 20,
|
||||
walk_start = 1,
|
||||
|
@ -57,45 +57,52 @@ local cod = {
|
|||
run_start = 1,
|
||||
run_end = 20,
|
||||
},
|
||||
drops = {
|
||||
drops = {
|
||||
{name = "mcl_fishing:fish_raw",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
{name = "mcl_dye:white",
|
||||
{name = "mcl_dye:white",
|
||||
chance = 20,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
swim = true,
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
runaway = true,
|
||||
fear_height = 4,
|
||||
do_custom = function(self)
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.object:get_velocity().y < 2.5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random(-.002, .002) , z = 0 })
|
||||
end
|
||||
end
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 10)) do
|
||||
local lp = object:get_pos()
|
||||
local s = self.object:get_pos()
|
||||
local vec = {
|
||||
x = lp.x - s.x,
|
||||
y = lp.y - s.y,
|
||||
z = lp.z - s.z
|
||||
}
|
||||
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "extra_mobs:cod" then
|
||||
self.state = "runaway"
|
||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||
end
|
||||
end
|
||||
end
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
swim = true,
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
runaway = true,
|
||||
fear_height = 4,
|
||||
do_custom = function(self)
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.object:get_velocity().y < 2.5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random(-.002, .002) , z = 0 })
|
||||
end
|
||||
end
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 10)) do
|
||||
local lp = object:get_pos()
|
||||
local s = self.object:get_pos()
|
||||
local vec = {
|
||||
x = lp.x - s.x,
|
||||
y = lp.y - s.y,
|
||||
z = lp.z - s.z
|
||||
}
|
||||
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "extra_mobs:cod" then
|
||||
self.state = "runaway"
|
||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_rightclick = function(self, clicker)
|
||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
||||
self.object:remove()
|
||||
clicker:set_wielded_item("mcl_fishing:bucket_cod")
|
||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
mobs:register_mob("extra_mobs:cod", cod)
|
||||
|
|
|
@ -246,4 +246,4 @@ water-16,
|
|||
water)
|
||||
|
||||
--spawn egg
|
||||
mobs:register_egg("extra_mobs:dolphin", S("dolphin"), "extra_mobs_spawn_icon_dolphin.png", 0)
|
||||
mobs:register_egg("extra_mobs:dolphin", S("Dolphin"), "extra_mobs_spawn_icon_dolphin.png", 0)
|
||||
|
|
|
@ -18,17 +18,14 @@ local S = minetest.get_translator("extra_mobs")
|
|||
--################### fox
|
||||
--###################
|
||||
|
||||
local followitem = ""
|
||||
if minetest.get_modpath("mc_sweet_berry") then
|
||||
followitem = "mc_sweet_berry:sweet_berry"
|
||||
else
|
||||
followitem = nil
|
||||
end
|
||||
local followitem = "mcl_farming:sweet_berry"
|
||||
|
||||
local fox = {
|
||||
type = "monster",
|
||||
type = "animal",
|
||||
passive = false,
|
||||
spawn_class = "hostile",
|
||||
skittish = true,
|
||||
runaway = true,
|
||||
hp_min = 10,
|
||||
hp_max = 10,
|
||||
xp_min = 1,
|
||||
|
@ -37,9 +34,20 @@ local fox = {
|
|||
attack_type = "dogfight",
|
||||
damage = 2,
|
||||
reach = 1.5,
|
||||
jump = true,
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 3,
|
||||
run_velocity = 6,
|
||||
follow_velocity = 2,
|
||||
follow = followitem,
|
||||
pathfinding = 1,
|
||||
fear_height = 4,
|
||||
view_range = 16,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.84, 0.3},
|
||||
specific_attack = { "mobs_mc:chicken", "extra_mobs:cod", "extra_mobs:salmon" },
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_fox.b3d",
|
||||
rotate = 270,
|
||||
textures = { {
|
||||
"extra_mobs_fox.png",
|
||||
"extra_mobs_trans.png",
|
||||
|
@ -47,10 +55,6 @@ local fox = {
|
|||
visual_size = {x=3, y=3},
|
||||
sounds = {
|
||||
},
|
||||
jump = true,
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 3,
|
||||
run_velocity = 6,
|
||||
drops = {
|
||||
},
|
||||
animation = {
|
||||
|
@ -68,9 +72,9 @@ local fox = {
|
|||
lay_start = 34,
|
||||
lay_end = 34,
|
||||
},
|
||||
runaway = true,
|
||||
on_spawn = function(self)
|
||||
if minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:snow") ~= nil or minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:dirt_with_grass_snow") ~= nil then
|
||||
if minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:snow") ~= nil
|
||||
or minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:dirt_with_grass_snow") ~= nil then
|
||||
self.object:set_properties({textures={"extra_mobs_artic_fox.png", "extra_mobs_trans.png"}})
|
||||
end
|
||||
end,
|
||||
|
@ -88,7 +92,11 @@ local fox = {
|
|||
end)
|
||||
end
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 8)) do
|
||||
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "extra_mobs:fox" and self.state ~= "attack" and math.random(1, 500) == 1 then
|
||||
if object
|
||||
and not object:is_player()
|
||||
and object:get_luaentity()
|
||||
and object:get_luaentity().name == "extra_mobs:fox"
|
||||
and self.state ~= "attack" and math.random(1, 500) == 1 then
|
||||
self.horny = true
|
||||
end
|
||||
local lp = object:get_pos()
|
||||
|
@ -98,15 +106,30 @@ local fox = {
|
|||
y = lp.y - s.y,
|
||||
z = lp.z - s.z
|
||||
}
|
||||
if object and object:is_player() and not object:get_player_control().sneak or not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mobs_mc:wolf" then
|
||||
self.state = "runaway"
|
||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||
if self.reach > vector.distance(self.object:get_pos(), object:get_pos()) and self.timer > .9 then
|
||||
self.timer = 0
|
||||
object:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = self.damage}
|
||||
}, nil)
|
||||
-- scare logic
|
||||
if (object
|
||||
and object:is_player()
|
||||
and not object:get_player_control().sneak)
|
||||
or (not object:is_player()
|
||||
and object:get_luaentity()
|
||||
and object:get_luaentity().name == "mobs_mc:wolf") then
|
||||
-- don't keep setting it once it's set
|
||||
if not self.state == "runaway" then
|
||||
self.state = "runaway"
|
||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||
end
|
||||
-- if it is within a distance of the player or wolf
|
||||
if 6 > vector.distance(self.object:get_pos(), object:get_pos()) then
|
||||
self.timer = self.timer + 1
|
||||
-- have some time before getting scared
|
||||
if self.timer > 6 then
|
||||
self.timer = 0
|
||||
-- punch the fox for the player, but don't do any damage
|
||||
self.object:punch(object, 0, {
|
||||
full_punch_interval = 0,
|
||||
damage_groups = {fleshy = 0}
|
||||
}, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -114,44 +137,35 @@ local fox = {
|
|||
do_punch = function(self)
|
||||
self.state = "runaway"
|
||||
end,
|
||||
follow = followitem,
|
||||
fear_height = 4,
|
||||
view_range = 16,
|
||||
specific_attack = { "mobs_mc:chicken", "extra_mobs:cod", "extra_mobs:salmon" },
|
||||
}
|
||||
|
||||
mobs:register_mob("extra_mobs:fox", fox)
|
||||
|
||||
-- spawning
|
||||
mobs:spawn_specific(
|
||||
"extra_mobs:fox",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
6000,
|
||||
3,
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_setup({
|
||||
name = "extra_mobs:fox",
|
||||
biomes = {
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
interval = 30,
|
||||
chance = 6000,
|
||||
min_height = mobs_mc.spawn_height.water,
|
||||
})
|
||||
|
||||
--mobs:spawn_specific("extra_mobs:fox", "overworld", "ground", 0, minetest.LIGHT_MAX+1, 30, 6000, 3, 0, 500)
|
||||
--[[
|
||||
|
|
|
@ -231,3 +231,13 @@ water)
|
|||
|
||||
-- spawn egg
|
||||
mobs:register_egg("extra_mobs:glow_squid", S("Glow Squid"), "extra_mobs_spawn_icon_glow_squid.png", 0)
|
||||
|
||||
-- dropped item (used to craft glowing itemframe)
|
||||
|
||||
minetest.register_craftitem("extra_mobs:glow_ink_sac", {
|
||||
description = S("Glow Ink Sac"),
|
||||
_doc_items_longdesc = S("Use it to craft the Glow Item Frame."),
|
||||
_doc_items_usagehelp = S("Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame."),
|
||||
inventory_image = "extra_mobs_glow_ink_sac.png",
|
||||
groups = { craftitem = 1 },
|
||||
})
|
||||
|
|
|
@ -1,329 +0,0 @@
|
|||
local S = minetest.get_translator("extra_mobs")
|
||||
|
||||
minetest.register_craftitem("extra_mobs:glow_ink_sac", {
|
||||
description = S("Glow Ink Sac"),
|
||||
_doc_items_longdesc = S("Use it to craft the Glow Item Frame."),
|
||||
_doc_items_usagehelp = S("Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame."),
|
||||
inventory_image = "extra_mobs_glow_ink_sac.png",
|
||||
groups = { craftitem = 1 },
|
||||
})
|
||||
|
||||
|
||||
--------------------
|
||||
|
||||
--[[This mod is originally by Zeg9, but heavily modified for MineClone 2.
|
||||
|
||||
Model created by 22i, licensed under the
|
||||
GNU GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||
|
||||
Source: <https://github.com/22i/amc>
|
||||
]]
|
||||
|
||||
|
||||
local VISUAL_SIZE = 0.3
|
||||
|
||||
minetest.register_entity("extra_mobs:glow_item_frame_item",{
|
||||
hp_max = 1,
|
||||
visual = "wielditem",
|
||||
visual_size = {x=VISUAL_SIZE, y=VISUAL_SIZE},
|
||||
physical = false,
|
||||
pointable = false,
|
||||
textures = { "blank.png" },
|
||||
_texture = "blank.png",
|
||||
_scale = 1,
|
||||
glow = minetest.LIGHT_MAX,
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata ~= nil and staticdata ~= "" then
|
||||
local data = staticdata:split(';')
|
||||
if data and data[1] and data[2] then
|
||||
self._nodename = data[1]
|
||||
self._texture = data[2]
|
||||
if data[3] then
|
||||
self._scale = data[3]
|
||||
else
|
||||
self._scale = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if self._texture ~= nil then
|
||||
self.object:set_properties({
|
||||
textures={self._texture},
|
||||
visual_size={x=VISUAL_SIZE/self._scale, y=VISUAL_SIZE/self._scale},
|
||||
})
|
||||
end
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
if self._nodename ~= nil and self._texture ~= nil then
|
||||
local ret = self._nodename .. ';' .. self._texture
|
||||
if self._scale ~= nil then
|
||||
ret = ret .. ';' .. self._scale
|
||||
end
|
||||
return ret
|
||||
end
|
||||
return ""
|
||||
end,
|
||||
|
||||
_update_texture = function(self)
|
||||
if self._texture ~= nil then
|
||||
self.object:set_properties({
|
||||
textures={self._texture},
|
||||
visual_size={x=VISUAL_SIZE/self._scale, y=VISUAL_SIZE/self._scale},
|
||||
})
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local facedir = {}
|
||||
facedir[0] = {x=0,y=0,z=1}
|
||||
facedir[1] = {x=1,y=0,z=0}
|
||||
facedir[2] = {x=0,y=0,z=-1}
|
||||
facedir[3] = {x=-1,y=0,z=0}
|
||||
|
||||
local remove_item_entity = function(pos, node)
|
||||
local objs = nil
|
||||
if node.name == "extra_mobs:glow_item_frame" then
|
||||
objs = minetest.get_objects_inside_radius(pos, .5)
|
||||
end
|
||||
if objs then
|
||||
for _, obj in ipairs(objs) do
|
||||
if obj and obj:get_luaentity() and obj:get_luaentity().name == "extra_mobs:glow_item_frame_item" then
|
||||
obj:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local update_item_entity = function(pos, node, param2)
|
||||
remove_item_entity(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local item = inv:get_stack("main", 1)
|
||||
if not item:is_empty() then
|
||||
if not param2 then
|
||||
param2 = node.param2
|
||||
end
|
||||
if node.name == "extra_mobs:glow_item_frame" then
|
||||
local posad = facedir[param2]
|
||||
pos.x = pos.x + posad.x*6.5/16
|
||||
pos.y = pos.y + posad.y*6.5/16
|
||||
pos.z = pos.z + posad.z*6.5/16
|
||||
end
|
||||
local e = minetest.add_entity(pos, "extra_mobs:glow_item_frame_item")
|
||||
local lua = e:get_luaentity()
|
||||
lua._nodename = node.name
|
||||
local itemname = item:get_name()
|
||||
if itemname == "" or itemname == nil then
|
||||
lua._texture = "blank.png"
|
||||
lua._scale = 1
|
||||
else
|
||||
lua._texture = itemname
|
||||
local def = minetest.registered_items[itemname]
|
||||
if def and def.wield_scale then
|
||||
lua._scale = def.wield_scale.x
|
||||
else
|
||||
lua._scale = 1
|
||||
end
|
||||
end
|
||||
lua:_update_texture()
|
||||
if node.name == "extra_mobs:glow_item_frame" then
|
||||
local yaw = math.pi*2 - param2 * math.pi/2
|
||||
e:set_yaw(yaw)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local drop_item = function(pos, node, meta, clicker)
|
||||
local cname = ""
|
||||
if clicker and clicker:is_player() then
|
||||
cname = clicker:get_player_name()
|
||||
end
|
||||
if node.name == "extra_mobs:glow_item_frame" and not minetest.is_creative_enabled(cname) then
|
||||
local inv = meta:get_inventory()
|
||||
local item = inv:get_stack("main", 1)
|
||||
if not item:is_empty() then
|
||||
minetest.add_item(pos, item)
|
||||
end
|
||||
end
|
||||
meta:set_string("infotext", "")
|
||||
remove_item_entity(pos, node)
|
||||
end
|
||||
|
||||
minetest.register_node("extra_mobs:glow_item_frame",{
|
||||
description = S("Glow Item Frame"),
|
||||
_tt_help = S("Can hold an item and glows"),
|
||||
_doc_items_longdesc = S("Glow Item frames are decorative blocks in which items can be placed."),
|
||||
_doc_items_usagehelp = S("Just place any item on the item frame. Use the item frame again to retrieve the item."),
|
||||
drawtype = "mesh",
|
||||
is_ground_content = false,
|
||||
mesh = "extra_mobs_glow_item_frame.obj",
|
||||
selection_box = { type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5} },
|
||||
collision_box = { type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5} },
|
||||
tiles = {"extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png"},
|
||||
inventory_image = "extra_mobs_glow_item_frame_item.png",
|
||||
wield_image = "extra_mobs_glow_item_frame.png",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
|
||||
--FIXME: should only be glowing, no light source. How is that possible with a node?
|
||||
light_source = 1,
|
||||
|
||||
sunlight_propagates = true,
|
||||
groups = { dig_immediate=3,deco_block=1,dig_by_piston=1,container=7,attached_node_facedir=1 },
|
||||
sounds = mcl_sounds.node_sound_defaults(),
|
||||
node_placement_prediction = "",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
|
||||
return minetest.item_place(itemstack, placer, pointed_thing, minetest.dir_to_facedir(vector.direction(pointed_thing.above, pointed_thing.under)))
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 1)
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker, itemstack)
|
||||
if not itemstack then
|
||||
return
|
||||
end
|
||||
local pname = clicker:get_player_name()
|
||||
if minetest.is_protected(pos, pname) then
|
||||
minetest.record_protection_violation(pos, pname)
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
drop_item(pos, node, meta, clicker)
|
||||
local inv = meta:get_inventory()
|
||||
if itemstack:is_empty() then
|
||||
remove_item_entity(pos, node)
|
||||
meta:set_string("infotext", "")
|
||||
inv:set_stack("main", 1, "")
|
||||
return itemstack
|
||||
end
|
||||
local put_itemstack = ItemStack(itemstack)
|
||||
put_itemstack:set_count(1)
|
||||
inv:set_stack("main", 1, put_itemstack)
|
||||
update_item_entity(pos, node)
|
||||
-- Add node infotext when item has been named
|
||||
local imeta = itemstack:get_meta()
|
||||
local iname = imeta:get_string("name")
|
||||
if iname then
|
||||
meta:set_string("infotext", iname)
|
||||
end
|
||||
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local name = player:get_player_name()
|
||||
if minetest.is_protected(pos, name) then
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return 0
|
||||
else
|
||||
return count
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local name = player:get_player_name()
|
||||
if minetest.is_protected(pos, name) then
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return 0
|
||||
else
|
||||
return stack:get_count()
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local name = player:get_player_name()
|
||||
if minetest.is_protected(pos, name) then
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return 0
|
||||
else
|
||||
return stack:get_count()
|
||||
end
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
drop_item(pos, node, meta)
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode, param2)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
-- Rotate face
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
|
||||
local objs = nil
|
||||
if node.name == "extra_mobs:glow_item_frame" then
|
||||
objs = minetest.get_objects_inside_radius(pos, .5)
|
||||
end
|
||||
if objs then
|
||||
for _, obj in ipairs(objs) do
|
||||
if obj and obj:get_luaentity() and obj:get_luaentity().name == "extra_mobs:glow_item_frame_item" then
|
||||
update_item_entity(pos, node, (node.param2+1) % 4)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
elseif mode == screwdriver.ROTATE_AXIS then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = 'extra_mobs:glow_item_frame',
|
||||
recipe = {'mcl_itemframes:item_frame', 'extra_mobs:glow_ink_sac'},
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Update legacy item frames",
|
||||
name = "extra_mobs:update_legacy_glow_item_frames",
|
||||
nodenames = {"extra_mobs:glow_frame"},
|
||||
action = function(pos, node)
|
||||
-- Swap legacy node, then respawn entity
|
||||
node.name = "extra_mobs:glow_item_frame"
|
||||
local meta = minetest.get_meta(pos)
|
||||
local item = meta:get_string("item")
|
||||
minetest.swap_node(pos, node)
|
||||
if item ~= "" then
|
||||
local itemstack = ItemStack(minetest.deserialize(meta:get_string("itemdata")))
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 1)
|
||||
if not itemstack:is_empty() then
|
||||
inv:set_stack("main", 1, itemstack)
|
||||
end
|
||||
end
|
||||
update_item_entity(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
-- FIXME: Item entities can get destroyed by /clearobjects
|
||||
minetest.register_lbm({
|
||||
label = "Respawn item frame item entities",
|
||||
name = "extra_mobs:respawn_entities",
|
||||
nodenames = {"extra_mobs:glow_item_frame"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
update_item_entity(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_alias("extra_mobs:glow_frame", "extra_mobs:glow_item_frame")
|
||||
|
||||
--------------------
|
|
@ -21,8 +21,3 @@ dofile(path .. "/cod.lua")
|
|||
dofile(path .. "/salmon.lua")
|
||||
dofile(path .. "/dolphin.lua")
|
||||
dofile(path .. "/glow_squid.lua")
|
||||
|
||||
--Items
|
||||
dofile(path .. "/glow_squid_items.lua")
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# textdomain:extra_mobs
|
||||
Hoglin=Hoglin
|
||||
Piglin=Piglin
|
||||
Piglin Brute=Piglin Barbare
|
||||
Strider=Arpenteur
|
||||
Fox=Renard
|
||||
Cod=Poisson
|
||||
Salmon=Saumon
|
||||
Dolphin=Dauphin
|
||||
Glow Squid=Pieuvre Lumineuse
|
||||
Glow Ink Sac=Sac d'Encre Lumineuse
|
|
@ -9,9 +9,3 @@ Salmon=Лосось
|
|||
dolphin=Дельфин
|
||||
Glow Squid=Светящийся спрут
|
||||
Glow Ink Sac=Светящийся чернильный мешок
|
||||
Use it to craft the Glow Item Frame.=Используется для крафта светящейся рамки.
|
||||
Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame.=Используйте светящийся чернильный мешок и обычную рамку для крафта светящейся рамки.
|
||||
Glow Item Frame=Светящаяся рамка
|
||||
Can hold an item and glows=Светится и может хранить предмет
|
||||
Glow Item frames are decorative blocks in which items can be placed.=Светящаяся рамка это декоративный блок в который можно положить предметы.
|
||||
Just place any item on the item frame. Use the item frame again to retrieve the item.=Просто используйте любой предмет на рамке. Используйте рамку снова, чтобы забрать предмет.
|
|
@ -1,17 +1,11 @@
|
|||
# textdomain:extra_mobs
|
||||
Hoglin=
|
||||
piglin=
|
||||
piglin Brute=
|
||||
Piglin=
|
||||
Piglin Brute=
|
||||
Strider=
|
||||
Fox=
|
||||
Cod=
|
||||
Salmon=
|
||||
dolphin=
|
||||
Dolphin=
|
||||
Glow Squid=
|
||||
Glow Ink Sac=
|
||||
Use it to craft the Glow Item Frame.=
|
||||
Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame.=
|
||||
Glow Item Frame=
|
||||
Can hold an item and glows=
|
||||
Glow Item frames are decorative blocks in which items can be placed.=
|
||||
Just place any item on the item frame. Use the item frame again to retrieve the item.=
|
|
@ -296,5 +296,5 @@ minetest.LIGHT_MAX+1,
|
|||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
-- spawn eggs
|
||||
mobs:register_egg("extra_mobs:piglin", S("piglin"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||
mobs:register_egg("extra_mobs:piglin_brute", S("piglin Brute"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||
mobs:register_egg("extra_mobs:piglin", S("Piglin"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||
mobs:register_egg("extra_mobs:piglin_brute", S("Piglin Brute"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||
|
|
|
@ -10,26 +10,26 @@ local S = minetest.get_translator("extra_mobs")
|
|||
--###################
|
||||
|
||||
local salmon = {
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
rotate = 270,
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.4, 0.0, -0.4, 0.4, 0.79, 0.4},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_salmon.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_salmon.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
type = "animal",
|
||||
spawn_class = "water",
|
||||
can_despawn = true,
|
||||
passive = true,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
armor = 100,
|
||||
rotate = 270,
|
||||
tilt_swim = true,
|
||||
collisionbox = {-0.4, 0.0, -0.4, 0.4, 0.79, 0.4},
|
||||
visual = "mesh",
|
||||
mesh = "extra_mobs_salmon.b3d",
|
||||
textures = {
|
||||
{"extra_mobs_salmon.png"}
|
||||
},
|
||||
sounds = {
|
||||
},
|
||||
animation = {
|
||||
stand_start = 1,
|
||||
stand_end = 20,
|
||||
walk_start = 1,
|
||||
|
@ -37,24 +37,31 @@ local salmon = {
|
|||
run_start = 1,
|
||||
run_end = 20,
|
||||
},
|
||||
drops = {
|
||||
drops = {
|
||||
{name = "mcl_fishing:salmon_raw",
|
||||
chance = 1,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
{name = "mcl_dye:white",
|
||||
{name = "mcl_dye:white",
|
||||
chance = 20,
|
||||
min = 1,
|
||||
max = 1,},
|
||||
},
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
swim = true,
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
runaway = true,
|
||||
fear_height = 4,
|
||||
visual_size = {x=3, y=3},
|
||||
makes_footstep_sound = false,
|
||||
swim = true,
|
||||
breathes_in_water = true,
|
||||
jump = false,
|
||||
view_range = 16,
|
||||
runaway = true,
|
||||
fear_height = 4,
|
||||
on_rightclick = function(self, clicker)
|
||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
||||
self.object:remove()
|
||||
clicker:set_wielded_item("mcl_fishing:bucket_salmon")
|
||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
mobs:register_mob("extra_mobs:salmon", salmon)
|
||||
|
|
|
@ -213,20 +213,36 @@ baby_strider.child = 1
|
|||
mobs:register_mob("extra_mobs:baby_strider", baby_strider)
|
||||
|
||||
-- Regular spawning in the Nether
|
||||
mobs:spawn_specific(
|
||||
"extra_mobs:strider",
|
||||
"nether",
|
||||
"lava",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
6000,
|
||||
3,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
|
||||
mobs:spawn_setup({
|
||||
name = "extra_mobs:strider",
|
||||
type_of_spawning = "lava",
|
||||
dimension = "nether",
|
||||
biomes = {
|
||||
"Nether"
|
||||
},
|
||||
min_height = mcl_mapgen.nether.min,
|
||||
max_height = mcl_mapgen.nether.max,
|
||||
chance = 2000,
|
||||
check_position = function(pos)
|
||||
return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava")
|
||||
end
|
||||
})
|
||||
|
||||
mobs:spawn_setup({
|
||||
name = "extra_mobs:baby_strider",
|
||||
type_of_spawning = "lava",
|
||||
dimension = "nether",
|
||||
biomes = {
|
||||
"Nether"
|
||||
},
|
||||
min_height = mcl_mapgen.nether.min,
|
||||
max_height = mcl_mapgen.nether.max,
|
||||
chance = 100,
|
||||
check_position = function(pos)
|
||||
return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava")
|
||||
end
|
||||
})
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("extra_mobs:strider", S("Strider"), "extra_mobs_spawn_icon_strider.png", 0)
|
||||
|
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
|
@ -166,19 +166,24 @@ function boat.on_activate(self, staticdata, dtime_s)
|
|||
self._last_v = self._v
|
||||
self._itemstring = data.itemstring
|
||||
|
||||
while #data.textures < 5 do
|
||||
table.insert(data.textures, data.textures[1])
|
||||
end
|
||||
if data.textures then
|
||||
while #data.textures < 5 do
|
||||
table.insert(data.textures, data.textures[1])
|
||||
end
|
||||
|
||||
self.object:set_properties({textures = data.textures})
|
||||
self.object:set_properties({textures = data.textures})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function boat.get_staticdata(self)
|
||||
if not self then return end
|
||||
local object = self.object
|
||||
local object_properties = object and object.get_properties and object:get_properties()
|
||||
return minetest.serialize({
|
||||
v = self._v,
|
||||
itemstring = self._itemstring,
|
||||
textures = self.object:get_properties().textures
|
||||
textures = object_properties and object_properties.textures
|
||||
})
|
||||
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
|
||||
Jungle Boat=Bateau en Acajou
|
||||
Oak Boat=Bateau en Chêne
|
||||
Obsidian Boat=Bateau en Obsidienne
|
||||
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
|
||||
Water vehicle=Véhicule aquatique
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# textdomain: mcl_boats
|
||||
Acacia Boat=Barca de Cacièr
|
||||
Birch Boat=Barca de Bèç
|
||||
Boat=Barca
|
||||
Boats are used to travel on the surface of water.= Las Barcas permetàn de vogar per l'aiga.
|
||||
Dark Oak Boat=Barca de Jàrric
|
||||
Jungle Boat=Barca de Jungla
|
||||
Oak Boat=Barca de Ròure
|
||||
Obsidian Boat=Barca d'Obsidiana
|
||||
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.=Fasetz un clic dreit sobre una sorça d'aiga per botar la barca. Fasetz un clic dreit sobre la barca per ne'n dintrar. Utilisatz [Esquèrra] e [Dreita] per menar, [Davant] per accelerar, e [Darrèir] per alentir o recular. Utilizatz [S'acatar] per o quitar, picatz la barca per o faire tombar en objèct.
|
||||
Spruce Boat=Barca de Sap
|
||||
Water vehicle=Veïcule d'Aiga
|
||||
Sneak to dismount=S'acatar per descendre
|
|
@ -89,6 +89,7 @@ minetest.register_entity(":__builtin:falling_node", {
|
|||
})
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
if not self then return end
|
||||
local meta = self.meta
|
||||
-- Workaround: Save inventory seperately from metadata.
|
||||
-- Because Minetest crashes when a node with inventory gets deactivated
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# textdomain: mcl_falling_nodes
|
||||
@1 was smashed by a falling anvil.=@1 fuguèt espotit per una enclutge.
|
||||
@1 was smashed by a falling block.=@1 fuguèt espotit per un blòc.
|
|
@ -65,6 +65,8 @@ mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
|
|||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_core:crying_obsidian", "mcl:whosCuttingOnions")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hiddenInTheDepths")
|
||||
|
||||
local function check_pickup_achievements(object, player)
|
||||
if has_awards then
|
||||
|
@ -154,6 +156,10 @@ minetest.register_globalstep(function(dtime)
|
|||
object:set_velocity({x=0,y=0,z=0})
|
||||
object:set_acceleration({x=0,y=0,z=0})
|
||||
|
||||
if object._flowing then
|
||||
object._flowing = false
|
||||
end
|
||||
|
||||
object:move_to(checkpos)
|
||||
|
||||
pool[name] = pool[name] + 1
|
||||
|
@ -486,6 +492,7 @@ minetest.register_entity(":__builtin:item", {
|
|||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
if not self then return end
|
||||
local data = minetest.serialize({
|
||||
itemstring = self.itemstring,
|
||||
always_collect = self.always_collect,
|
||||
|
@ -792,6 +799,9 @@ minetest.register_entity(":__builtin:item", {
|
|||
|
||||
local oldvel = self.object:get_velocity() -- v is vector, vel is velocity
|
||||
|
||||
-- apply gravity *before* drag computations
|
||||
oldvel.y = oldvel.y - get_gravity() * dtime
|
||||
|
||||
-- drag
|
||||
local fluid_drag = item_drop_settings.fluid_drag
|
||||
|
||||
|
@ -806,12 +816,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
newv.y = newv.y - (oldvel.y - newv.y) * fluid_drag * dtime
|
||||
newv.z = newv.z - (oldvel.z - newv.z) * fluid_drag * dtime
|
||||
|
||||
newv.y = newv.y + -0.22 -- (keep slight downward thrust from previous version of code)
|
||||
-- NOTE: is there any particular reason we have this, anyway?
|
||||
-- since fluid drag is now on, we could as well just
|
||||
-- apply gravity here; drag will slow down the fall
|
||||
-- realistically
|
||||
|
||||
self.object:set_velocity({x = oldvel.x + newv.x * dtime, y = oldvel.y + newv.y * dtime, z = oldvel.z + newv.z * dtime})
|
||||
|
||||
self.physical_state = true
|
||||
|
|
|
@ -503,6 +503,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
|||
end
|
||||
|
||||
function cart:get_staticdata()
|
||||
if not self then return end
|
||||
return minetest.serialize({_railtype = self._railtype})
|
||||
end
|
||||
|
||||
|
|
|
@ -227,6 +227,11 @@ functions needed for the mob to work properly which contains the following:
|
|||
older mobs.
|
||||
'pushable' Allows players, & other mobs to push the mob.
|
||||
|
||||
'spawn_with_armor' If set to true, the mob has a small chance of spawning with
|
||||
random matched armor. If set to a string, the string represents
|
||||
the material type of the armor. Any materials used by
|
||||
mcl_armor will work. Example: "gold"
|
||||
It is assumed that the first texture is for armor.
|
||||
|
||||
|
||||
MineClone 2 extensions:
|
||||
|
|
|
@ -375,6 +375,7 @@ function mobs:register_mob(name, def)
|
|||
--moves the wrong way
|
||||
swap_y_with_x = def.swap_y_with_x or false,
|
||||
reverse_head_yaw = def.reverse_head_yaw or false,
|
||||
_spawn_with_armor = def.spawn_with_armor,
|
||||
|
||||
--END HEAD CODE VARIABLES
|
||||
|
||||
|
@ -401,6 +402,7 @@ function mobs:register_mob(name, def)
|
|||
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
||||
eye_height = def.eye_height or 1.5,
|
||||
defuse_reach = def.defuse_reach or 4,
|
||||
spawn = def.spawn,
|
||||
-- End of MCL2 extensions
|
||||
|
||||
on_spawn = def.on_spawn,
|
||||
|
@ -415,7 +417,7 @@ function mobs:register_mob(name, def)
|
|||
|
||||
--on_breed = def.on_breed,
|
||||
|
||||
--on_grown = def.on_grown,
|
||||
on_grown = def.on_grown,
|
||||
|
||||
--on_detach_child = mob_detach_child,
|
||||
|
||||
|
@ -425,7 +427,9 @@ function mobs:register_mob(name, def)
|
|||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
return mobs.mob_staticdata(self)
|
||||
if self and mobs then
|
||||
return mobs.mob_staticdata(self)
|
||||
end
|
||||
end,
|
||||
|
||||
--harmed_by_heal = def.harmed_by_heal,
|
||||
|
|
|
@ -88,7 +88,7 @@ local function land_state_switch(self, dtime)
|
|||
end
|
||||
|
||||
--ignore everything else if following
|
||||
if mobs.check_following(self) and
|
||||
if mobs.check_following(self, dtime) and
|
||||
(not self.breed_lookout_timer or (self.breed_lookout_timer and self.breed_lookout_timer == 0)) and
|
||||
(not self.breed_timer or (self.breed_timer and self.breed_timer == 0)) then
|
||||
self.state = "follow"
|
||||
|
@ -801,20 +801,6 @@ function mobs.mob_step(self, dtime)
|
|||
return false
|
||||
end
|
||||
|
||||
|
||||
--DEBUG TIME!
|
||||
--REMEMBER TO MOVE THIS AFTER DEATH CHECK
|
||||
|
||||
--if self.has_head then
|
||||
-- mobs.do_head_logic(self,dtime)
|
||||
--end
|
||||
|
||||
|
||||
|
||||
--if true then--DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
|
||||
-- return
|
||||
--end
|
||||
|
||||
--despawn mechanism
|
||||
--don't despawned tamed or bred mobs
|
||||
if not self.tamed and not self.bred then
|
||||
|
@ -833,7 +819,7 @@ function mobs.mob_step(self, dtime)
|
|||
self.object:set_texture_mod("^[colorize:red:120")
|
||||
--fix double death sound
|
||||
if self.health > 0 then
|
||||
mobs.play_sound(self,"damage")
|
||||
mobs.play_sound(self, "damage")
|
||||
end
|
||||
end
|
||||
self.old_health = self.health
|
||||
|
@ -863,7 +849,7 @@ function mobs.mob_step(self, dtime)
|
|||
return
|
||||
end
|
||||
|
||||
mobs.random_sound_handling(self,dtime)
|
||||
mobs.random_sound_handling(self, dtime)
|
||||
|
||||
--mobs drowning mechanic
|
||||
if not self.breathes_in_water then
|
||||
|
@ -893,14 +879,40 @@ function mobs.mob_step(self, dtime)
|
|||
end
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local node = minetest_get_node(pos).name
|
||||
|
||||
--water damage
|
||||
if self.water_damage and self.water_damage ~= 0 then
|
||||
local pos = self.object:get_pos()
|
||||
local node = minetest_get_node(pos).name
|
||||
if minetest_get_item_group(node, "water") ~= 0 then
|
||||
if self.water_damage and self.water_damage ~= 0 and minetest_get_item_group(node, "water") ~= 0 then
|
||||
self.water_counter = (self.water_counter or 0) + dtime
|
||||
if self.water_counter >= 1 then
|
||||
mobs.smoke_effect(self)
|
||||
self.health = self.health - self.water_damage
|
||||
self:teleport()
|
||||
self.water_counter = 0
|
||||
end
|
||||
end
|
||||
|
||||
--lava damage
|
||||
local lava_damage = self.lava_damage
|
||||
if lava_damage and lava_damage ~= 0 and minetest_get_item_group(node, "lava") ~= 0 then
|
||||
self.lava_counter = (self.lava_counter or 0) + dtime
|
||||
if self.lava_counter >= 1 then
|
||||
minetest.sound_play("default_punch", {
|
||||
object = self.object,
|
||||
max_hear_distance = 5
|
||||
}, true)
|
||||
--[[ if not mcl_burning.is_burning(self.object) then
|
||||
mcl_burning.set_on_fire(self.object, 1.1)
|
||||
else
|
||||
]] self.object:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = self.lava_damage}
|
||||
}, nil)
|
||||
-- end
|
||||
self.lava_counter = 0
|
||||
self.health = self.health - lava_damage
|
||||
self:teleport()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -972,7 +984,7 @@ function mobs.mob_step(self, dtime)
|
|||
|
||||
--go get the closest player
|
||||
if attacking then
|
||||
|
||||
mobs.do_head_logic(self, dtime, attacking)
|
||||
self.memory = 6 --6 seconds of memory
|
||||
|
||||
--set initial punch timer
|
||||
|
@ -1028,6 +1040,7 @@ function mobs.mob_step(self, dtime)
|
|||
--don't break eye contact
|
||||
if self.hostile and self.attacking then
|
||||
mobs.set_yaw_while_attacking(self)
|
||||
mobs.do_head_logic(self, dtime, self.attacking)
|
||||
end
|
||||
|
||||
--perfectly reset pause_timer
|
||||
|
|
|
@ -26,6 +26,8 @@ local math_random = math.random
|
|||
|_|
|
||||
]]--
|
||||
|
||||
local minetest_line_of_sight = minetest.line_of_sight
|
||||
|
||||
mobs.explode_attack_walk = function(self,dtime)
|
||||
|
||||
--this needs an exception
|
||||
|
@ -36,17 +38,27 @@ mobs.explode_attack_walk = function(self,dtime)
|
|||
|
||||
mobs.set_yaw_while_attacking(self)
|
||||
|
||||
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||
local pos = self.object:get_pos()
|
||||
local attack_pos = self.attacking:get_pos()
|
||||
local distance_from_attacking = vector_distance(pos, attack_pos)
|
||||
|
||||
--make mob walk up to player within 2 nodes distance then start exploding
|
||||
if distance_from_attacking >= self.reach and
|
||||
--don't allow explosion to cancel unless out of the reach boundary
|
||||
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) then
|
||||
if (
|
||||
distance_from_attacking >= self.reach and
|
||||
|
||||
--don't allow explosion to cancel unless out of the reach boundary
|
||||
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) or
|
||||
--don't allow creeper to finish exploding animation if can't see target
|
||||
not minetest_line_of_sight(
|
||||
{x = pos.x, y = pos.y + self.eye_height, z = pos.z},
|
||||
{x = attack_pos.x, y = attack_pos.y + (self.attacking.eye_height or 0), z = attack_pos.z}
|
||||
)
|
||||
) 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)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|||
local vector = vector
|
||||
|
||||
--check to see if someone nearby has some tasty food
|
||||
mobs.check_following = function(self) -- returns true or false
|
||||
mobs.check_following = function(self, dtime) -- returns true or false
|
||||
--ignore
|
||||
if not self.follow then
|
||||
self.following_person = nil
|
||||
|
@ -15,6 +15,7 @@ mobs.check_following = function(self) -- returns true or false
|
|||
|
||||
--check if the follower is a player incase they log out
|
||||
if follower and follower:is_player() then
|
||||
mobs.do_head_logic(self, dtime, follower)
|
||||
local stack = follower:get_wielded_item()
|
||||
--safety check
|
||||
if not stack then
|
||||
|
@ -145,11 +146,12 @@ end
|
|||
|
||||
--make the baby grow up
|
||||
mobs.baby_grow_up = function(self)
|
||||
self.baby = nil
|
||||
self.visual_size = self.backup_visual_size
|
||||
self.collisionbox = self.backup_collisionbox
|
||||
self.selectionbox = self.backup_selectionbox
|
||||
self.object:set_properties(self)
|
||||
self.baby = nil
|
||||
self.visual_size = self.backup_visual_size
|
||||
self.collisionbox = self.backup_collisionbox
|
||||
self.selectionbox = self.backup_selectionbox
|
||||
self.object:set_properties(self)
|
||||
if self.on_grown then self.on_grown(self) end
|
||||
end
|
||||
|
||||
--makes the baby grow up faster with diminishing returns
|
||||
|
|
|
@ -211,26 +211,6 @@ mobs.teleport = function(self, target)
|
|||
end
|
||||
end
|
||||
|
||||
--a function used for despawning mobs
|
||||
mobs.check_for_player_within_area = function(self, radius)
|
||||
local pos1 = self.object:get_pos()
|
||||
if not pos1 then return end
|
||||
--get players in radius
|
||||
for _,player in pairs(minetest_get_connected_players()) do
|
||||
if player and player:get_hp() > 0 then
|
||||
local pos2 = player:get_pos()
|
||||
local distance = vector_distance(pos1,pos2)
|
||||
if distance < radius then
|
||||
--found a player
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
--did not find a player
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--a simple helper function for mobs following
|
||||
mobs.get_2d_distance = function(pos1,pos2)
|
||||
pos1.y = 0
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
local math = math
|
||||
local vector = vector
|
||||
local debug_head = minetest.settings:get_bool("mcl_debug_head_code", false)
|
||||
|
||||
--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")
|
||||
mobs.do_head_logic = function(self, dtime, player)
|
||||
if not self.has_head == true then return end
|
||||
local player = player or minetest.get_player_by_name("singleplayer")
|
||||
|
||||
local look_at = player:get_pos()
|
||||
look_at.y = look_at.y + player:get_properties().eye_height
|
||||
|
@ -25,15 +26,16 @@ mobs.do_head_logic = function(self,dtime)
|
|||
|
||||
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",
|
||||
})
|
||||
|
||||
if debug_head then
|
||||
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",
|
||||
})
|
||||
end
|
||||
local bone_pos = vector.new(0,0,0)
|
||||
|
||||
--(horizontal)
|
||||
|
@ -89,10 +91,21 @@ mobs.do_head_logic = function(self,dtime)
|
|||
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))
|
||||
local head_bone = self.head_bone
|
||||
if (type(head_bone) == "table") then
|
||||
for _, v in pairs(head_bone) do
|
||||
if self.swap_y_with_x then
|
||||
self.object:set_bone_position(v, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
||||
else
|
||||
self.object:set_bone_position(v, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
||||
end
|
||||
end
|
||||
else
|
||||
self.object:set_bone_position(self.head_bone, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
||||
if self.swap_y_with_x then
|
||||
self.object:set_bone_position(head_bone, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
||||
else
|
||||
self.object:set_bone_position(head_bone, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
||||
end
|
||||
end
|
||||
--set_bone_position([bone, position, rotation])
|
||||
end
|
|
@ -5,9 +5,32 @@ local minetest_settings = minetest.settings
|
|||
-- CMI support check
|
||||
local use_cmi = minetest.global_exists("cmi")
|
||||
|
||||
local vector_distance = vector.distance
|
||||
local minetest_get_connected_players = minetest.get_connected_players
|
||||
local math_random = math.random
|
||||
|
||||
mobs.can_despawn = function(self)
|
||||
return (not self.tamed and not self.bred and not self.nametag and
|
||||
not mobs.check_for_player_within_area(self, 64));
|
||||
if self.tamed or self.bred or self.nametag then return false end
|
||||
local mob_pos = self.object:get_pos()
|
||||
if not mob_pos then return true end
|
||||
local players = minetest_get_connected_players()
|
||||
if #players == 0 then return false end
|
||||
-- If no players, probably this is being called from get_staticdata() at server shutdown time
|
||||
-- Minetest is to buggy (as of 5.5) to delete entities at server shutdown time anyway
|
||||
|
||||
local distance = 999
|
||||
for _, player in pairs(players) do
|
||||
if player and player:get_hp() > 0 then
|
||||
local player_pos = player:get_pos()
|
||||
local new_distance = vector_distance(player_pos, mob_pos)
|
||||
if new_distance < distance then
|
||||
distance = new_distance
|
||||
if distance < 33 then return false end
|
||||
if distance < 128 and math_random(1, 42) ~= 11 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- get entity staticdata
|
||||
|
@ -24,7 +47,7 @@ mobs.mob_staticdata = function(self)
|
|||
self.following = nil
|
||||
|
||||
if use_cmi then
|
||||
self.serialized_cmi_components = cmi.serialize_components(self._cmi_components)
|
||||
self.serialized_cmi_components = cmi and cmi.serialize_components(self._cmi_components)
|
||||
end
|
||||
|
||||
local tmp = {}
|
||||
|
@ -44,6 +67,102 @@ mobs.mob_staticdata = function(self)
|
|||
return minetest.serialize(tmp)
|
||||
end
|
||||
|
||||
mobs.armor_setup = function(self)
|
||||
if not self._armor_items then
|
||||
local armor = {}
|
||||
-- Source: https://minecraft.fandom.com/wiki/Zombie
|
||||
local materials = {
|
||||
{name = "leather", chance = 0.3706},
|
||||
{name = "gold", chance = 0.4873},
|
||||
{name = "chain", chance = 0.129},
|
||||
{name = "iron", chance = 0.0127},
|
||||
{name = "diamond", chance = 0.0004}
|
||||
}
|
||||
local types = {
|
||||
{name = "helmet", chance = 0.15},
|
||||
{name = "chestplate", chance = 0.75},
|
||||
{name = "leggings", chance = 0.75},
|
||||
{name = "boots", chance = 0.75}
|
||||
}
|
||||
|
||||
local material
|
||||
if type(self._spawn_with_armor) == "string" then
|
||||
material = self._spawn_with_armor
|
||||
else
|
||||
local chance = 0
|
||||
for i, m in pairs(materials) do
|
||||
chance = chance + m.chance
|
||||
if math.random() <= chance then
|
||||
material = m.name
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, t in pairs(types) do
|
||||
if math.random() <= t.chance then
|
||||
armor[t.name] = material
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Save armor items in lua entity
|
||||
self._armor_items = {}
|
||||
for atype, material in pairs(armor) do
|
||||
local item = "mcl_armor:" .. atype .. "_" .. material
|
||||
self._armor_items[atype] = item
|
||||
end
|
||||
|
||||
-- Setup armor drops
|
||||
for atype, material in pairs(armor) do
|
||||
local wear = math.random(1, 65535)
|
||||
local item = "mcl_armor:" .. atype .. "_" .. material .. " 1 " .. wear
|
||||
self.drops = table.copy(self.drops)
|
||||
table.insert(self.drops, {
|
||||
name = item,
|
||||
chance = 1/0.085, -- 8.5%
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
looting_factor = 0.01 / 3,
|
||||
})
|
||||
end
|
||||
|
||||
-- Configure textures
|
||||
local t = ""
|
||||
local first_image = true
|
||||
for atype, material in pairs(armor) do
|
||||
if not first_image then
|
||||
t = t .. "^"
|
||||
end
|
||||
t = t .. "mcl_armor_" .. atype .. "_" .. material .. ".png"
|
||||
first_image = false
|
||||
end
|
||||
if t ~= "" then
|
||||
self.base_texture = table.copy(self.base_texture)
|
||||
self.base_texture[1] = t
|
||||
end
|
||||
|
||||
-- Configure damage groups based on armor
|
||||
-- Source: https://minecraft.fandom.com/wiki/Armor#Armor_points
|
||||
local points = 2
|
||||
for atype, material in pairs(armor) do
|
||||
local item_name = "mcl_armor:" .. atype .. "_" .. material
|
||||
points = points + minetest.get_item_group(item_name, "mcl_armor_points")
|
||||
end
|
||||
local armor_strength = 100 - 4 * points
|
||||
local armor_groups = self.object:get_armor_groups()
|
||||
armor_groups.fleshy = armor_strength
|
||||
self.armor = armor_groups
|
||||
|
||||
-- Helmet protects mob from sun damage
|
||||
if armor.helmet then
|
||||
self.ignited_by_sunlight = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- activate mob and reload settings
|
||||
mobs.mob_activate = function(self, staticdata, def, dtime)
|
||||
|
@ -87,6 +206,11 @@ mobs.mob_activate = function(self, staticdata, def, dtime)
|
|||
self.base_selbox = self.selectionbox
|
||||
end
|
||||
|
||||
-- Setup armor on mobs
|
||||
if self._spawn_with_armor then
|
||||
mobs.armor_setup(self)
|
||||
end
|
||||
|
||||
-- for current mobs that dont have this set
|
||||
if not self.base_selbox then
|
||||
self.base_selbox = self.selectionbox or self.base_colbox
|
||||
|
|
|
@ -6,11 +6,14 @@ local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
|||
local get_biome_name = minetest.get_biome_name
|
||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
|
||||
local minetest_get_perlin = minetest.get_perlin
|
||||
|
||||
local math_random = math.random
|
||||
local math_floor = math.floor
|
||||
--local max = math.max
|
||||
local math_ceil = math.ceil
|
||||
local math_cos = math.cos
|
||||
local math_sin = math.sin
|
||||
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
|
||||
|
||||
--local vector_distance = vector.distance
|
||||
local vector_new = vector.new
|
||||
|
@ -22,151 +25,171 @@ local table_remove = table.remove
|
|||
local pairs = pairs
|
||||
|
||||
-- range for mob count
|
||||
local aoc_range = 48
|
||||
local aoc_range = 32
|
||||
|
||||
--do mobs spawn?
|
||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||
|
||||
--[[
|
||||
|
||||
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
||||
local noise_params = {
|
||||
offset = 0,
|
||||
scale = 3,
|
||||
spread = {
|
||||
x = 301,
|
||||
y = 50,
|
||||
z = 304,
|
||||
},
|
||||
seed = 100,
|
||||
octaves = 3,
|
||||
persistence = 0.5,
|
||||
}
|
||||
|
||||
underground:
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
||||
-- Also used for missing parameter
|
||||
-- Please update the list when adding new biomes!
|
||||
|
||||
ocean:
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
local list_of_all_biomes = {
|
||||
|
||||
water or beach?
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
-- underground:
|
||||
|
||||
beach:
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
|
||||
dimension biome:
|
||||
"Nether",
|
||||
"End",
|
||||
-- ocean:
|
||||
|
||||
Overworld regular:
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
]]--
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
|
||||
-- water or beach?
|
||||
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
|
||||
-- beach:
|
||||
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
|
||||
-- dimension biome:
|
||||
|
||||
"Nether",
|
||||
"End",
|
||||
|
||||
-- Overworld regular:
|
||||
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
}
|
||||
|
||||
-- count how many mobs are in an area
|
||||
local function count_mobs(pos)
|
||||
|
@ -216,11 +239,88 @@ WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
|
|||
|
||||
--this is where all of the spawning information is kept
|
||||
local spawn_dictionary = {}
|
||||
local summary_chance = 0
|
||||
|
||||
function mobs:spawn_setup(def)
|
||||
if not mobs_spawn then return end
|
||||
|
||||
if not def then
|
||||
minetest.log("warning", "Empty mob spawn setup definition")
|
||||
return
|
||||
end
|
||||
|
||||
local name = def.name
|
||||
if not name then
|
||||
minetest.log("warning", "Missing mob name")
|
||||
return
|
||||
end
|
||||
|
||||
local dimension = def.dimension or "overworld"
|
||||
local type_of_spawning = def.type_of_spawning or "ground"
|
||||
local biomes = def.biomes or list_of_all_biomes
|
||||
local min_light = def.min_light or 0
|
||||
local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
|
||||
local chance = def.chance or 1000
|
||||
local aoc = def.aoc or aoc_range
|
||||
local min_height = def.min_height or mcl_mapgen.overworld.min
|
||||
local max_height = def.max_height or mcl_mapgen.overworld.max
|
||||
local day_toggle = def.day_toggle
|
||||
local on_spawn = def.on_spawn
|
||||
local check_position = def.check_position
|
||||
local group_size_min = def.group_size_min or 1
|
||||
local group_size_max = def.group_size_max or 1
|
||||
|
||||
-- chance/spawn number override in minetest.conf for registered mob
|
||||
local numbers = minetest.settings:get(name)
|
||||
if numbers then
|
||||
numbers = numbers:split(",")
|
||||
chance = tonumber(numbers[1]) or chance
|
||||
aoc = tonumber(numbers[2]) or aoc
|
||||
if chance == 0 then
|
||||
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
||||
return
|
||||
end
|
||||
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||
end
|
||||
|
||||
if chance < 1 then
|
||||
chance = 1
|
||||
minetest.log("warning", "Chance shouldn't be less than 1 (mob name: " .. name ..")")
|
||||
end
|
||||
|
||||
spawn_dictionary[#spawn_dictionary + 1] = {
|
||||
name = name,
|
||||
dimension = dimension,
|
||||
type_of_spawning = type_of_spawning,
|
||||
biomes = biomes,
|
||||
min_light = min_light,
|
||||
max_light = max_light,
|
||||
chance = chance,
|
||||
aoc = aoc,
|
||||
min_height = min_height,
|
||||
max_height = max_height,
|
||||
day_toggle = day_toggle,
|
||||
check_position = check_position,
|
||||
on_spawn = on_spawn,
|
||||
group_size_min = group_size_min,
|
||||
group_size_max = group_size_max,
|
||||
}
|
||||
summary_chance = summary_chance + chance
|
||||
end
|
||||
|
||||
function mobs.spawn_mob(name, pos)
|
||||
local def = minetest.registered_entities[name]
|
||||
if not def then return end
|
||||
if def.spawn then
|
||||
return def.spawn(pos)
|
||||
end
|
||||
return minetest.add_entity(pos, name)
|
||||
end
|
||||
|
||||
local spawn_mob = mobs.spawn_mob
|
||||
|
||||
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||
|
||||
--print(dump(biomes))
|
||||
|
||||
-- Do mobs spawn at all?
|
||||
if not mobs_spawn then
|
||||
return
|
||||
|
@ -239,179 +339,7 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
|||
return
|
||||
end
|
||||
|
||||
minetest.log("action",
|
||||
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||
end
|
||||
|
||||
--[[
|
||||
local function spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||
|
||||
local orig_pos = table.copy(pos)
|
||||
-- is mob actually registered?
|
||||
if not mobs.spawning_mobs[name]
|
||||
or not minetest.registered_entities[name] then
|
||||
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
||||
return
|
||||
end
|
||||
|
||||
-- additional custom checks for spawning mob
|
||||
if mobs:spawn_abm_check(pos, node, name) == true then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- count nearby mobs in same spawn class
|
||||
local entdef = minetest.registered_entities[name]
|
||||
local spawn_class = entdef and entdef.spawn_class
|
||||
if not spawn_class then
|
||||
if entdef.type == "monster" then
|
||||
spawn_class = "hostile"
|
||||
else
|
||||
spawn_class = "passive"
|
||||
end
|
||||
end
|
||||
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
||||
-- do not spawn if too many of same mob in area
|
||||
if active_object_count_wider >= max_per_block -- large-range mob cap
|
||||
or (not in_class_cap) -- spawn class mob cap
|
||||
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
||||
-- too many entities
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
||||
return
|
||||
end
|
||||
|
||||
-- if toggle set to nil then ignore day/night check
|
||||
if day_toggle then
|
||||
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
if tod > 4500 and tod < 19500 then
|
||||
-- daylight, but mob wants night
|
||||
if day_toggle == false then
|
||||
-- mob needs night
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
||||
return
|
||||
end
|
||||
else
|
||||
-- night time but mob wants day
|
||||
if day_toggle == true then
|
||||
-- mob needs day
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn above node
|
||||
pos.y = pos.y + 1
|
||||
|
||||
-- only spawn away from player
|
||||
local objs = minetest.get_objects_inside_radius(pos, 24)
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
if objs[n]:is_player() then
|
||||
-- player too close
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- mobs cannot spawn in protected areas when enabled
|
||||
if not spawn_protected
|
||||
and minetest.is_protected(pos, "") then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are we spawning within height limits?
|
||||
if pos.y > max_height
|
||||
or pos.y < min_height then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are light levels ok?
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light
|
||||
or light > max_light
|
||||
or light < min_light then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
||||
return
|
||||
end
|
||||
|
||||
-- do we have enough space to spawn mob?
|
||||
local ent = minetest.registered_entities[name]
|
||||
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
||||
local min_x, max_x
|
||||
if width_x % 2 == 0 then
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -(max_x-1)
|
||||
else
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -max_x
|
||||
end
|
||||
|
||||
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
||||
local min_z, max_z
|
||||
if width_z % 2 == 0 then
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -(max_z-1)
|
||||
else
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -max_z
|
||||
end
|
||||
|
||||
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
||||
|
||||
for y = 0, max_y do
|
||||
for x = min_x, max_x do
|
||||
for z = min_z, max_z do
|
||||
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
||||
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
||||
-- inside block
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
||||
if ent.spawn_small_alternative and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
||||
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
||||
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- tweak X/Y/Z spawn pos
|
||||
if width_x % 2 == 0 then
|
||||
pos.x = pos.x + 0.5
|
||||
end
|
||||
if width_z % 2 == 0 then
|
||||
pos.z = pos.z + 0.5
|
||||
end
|
||||
pos.y = pos.y - 0.5
|
||||
|
||||
local mob = minetest.add_entity(pos, name)
|
||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
if on_spawn then
|
||||
|
||||
local ent = mob:get_luaentity()
|
||||
|
||||
on_spawn(ent, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
||||
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||
end
|
||||
]]--
|
||||
|
||||
local entdef = minetest.registered_entities[name]
|
||||
local spawn_class
|
||||
if entdef.type == "monster" then
|
||||
spawn_class = "hostile"
|
||||
else
|
||||
spawn_class = "passive"
|
||||
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||
end
|
||||
|
||||
--load information into the spawn dictionary
|
||||
|
@ -423,107 +351,36 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
|||
spawn_dictionary[key]["biomes"] = biomes
|
||||
spawn_dictionary[key]["min_light"] = min_light
|
||||
spawn_dictionary[key]["max_light"] = max_light
|
||||
spawn_dictionary[key]["interval"] = interval
|
||||
spawn_dictionary[key]["chance"] = chance
|
||||
spawn_dictionary[key]["aoc"] = aoc
|
||||
spawn_dictionary[key]["min_height"] = min_height
|
||||
spawn_dictionary[key]["max_height"] = max_height
|
||||
spawn_dictionary[key]["day_toggle"] = day_toggle
|
||||
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
|
||||
spawn_dictionary[key]["spawn_class"] = spawn_class
|
||||
spawn_dictionary[key]["group_size_min"] = 1
|
||||
spawn_dictionary[key]["group_size_max"] = 3
|
||||
|
||||
--[[
|
||||
minetest.register_abm({
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = floor(max(1, chance * mobs_spawn_chance)),
|
||||
catch_up = false,
|
||||
action = spawn_abm_action,
|
||||
})
|
||||
]]--
|
||||
summary_chance = summary_chance + chance
|
||||
end
|
||||
|
||||
-- compatibility with older mob registration
|
||||
-- we're going to forget about this for now -j4i
|
||||
--[[
|
||||
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
||||
|
||||
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
||||
chance, active_object_count, -31000, max_height, day_toggle)
|
||||
local two_pi = 2 * math.pi
|
||||
local function get_next_mob_spawn_pos(pos)
|
||||
local distance = math_random(25, 32)
|
||||
local angle = math_random() * two_pi
|
||||
return {
|
||||
x = math_round(pos.x + distance * math_cos(angle)),
|
||||
y = pos.y,
|
||||
z = math_round(pos.z + distance * math_sin(angle))
|
||||
}
|
||||
end
|
||||
]]--
|
||||
|
||||
|
||||
--Don't disable this yet-j4i
|
||||
-- MarkBu's spawn function
|
||||
|
||||
function mobs:spawn(def)
|
||||
--does nothing for now
|
||||
--[[
|
||||
local name = def.name
|
||||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||
local neighbors = def.neighbors or {"air"}
|
||||
local min_light = def.min_light or 0
|
||||
local max_light = def.max_light or 15
|
||||
local interval = def.interval or 30
|
||||
local chance = def.chance or 5000
|
||||
local active_object_count = def.active_object_count or 1
|
||||
local min_height = def.min_height or -31000
|
||||
local max_height = def.max_height or 31000
|
||||
local day_toggle = def.day_toggle
|
||||
local on_spawn = def.on_spawn
|
||||
|
||||
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
||||
]]--
|
||||
end
|
||||
|
||||
|
||||
|
||||
local axis
|
||||
--inner and outer part of square donut radius
|
||||
local inner = 15
|
||||
local outer = 64
|
||||
local int = {-1,1}
|
||||
|
||||
local function position_calculation(pos)
|
||||
|
||||
pos = vector_floor(pos)
|
||||
|
||||
--this is used to determine the axis buffer from the player
|
||||
axis = math_random(0,1)
|
||||
|
||||
--cast towards the direction
|
||||
if axis == 0 then --x
|
||||
pos.x = pos.x + math_random(inner,outer)*int[math_random(1,2)]
|
||||
pos.z = pos.z + math_random(-outer,outer)
|
||||
else --z
|
||||
pos.z = pos.z + math_random(inner,outer)*int[math_random(1,2)]
|
||||
pos.x = pos.x + math_random(-outer,outer)
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
--[[
|
||||
local decypher_limits_dictionary = {
|
||||
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
|
||||
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
|
||||
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
|
||||
}
|
||||
]]--
|
||||
|
||||
local function decypher_limits(posy)
|
||||
--local min_max_table = decypher_limits_dictionary[dimension]
|
||||
--return min_max_table[1],min_max_table[2]
|
||||
posy = math_floor(posy)
|
||||
return posy - 32, posy + 32
|
||||
end
|
||||
|
||||
--a simple helper function for mob_spawn
|
||||
local function biome_check(biome_list, biome_goal)
|
||||
for _,data in ipairs(biome_list) do
|
||||
for _, data in pairs(biome_list) do
|
||||
if data == biome_goal then
|
||||
return true
|
||||
end
|
||||
|
@ -533,176 +390,102 @@ local function biome_check(biome_list, biome_goal)
|
|||
end
|
||||
|
||||
|
||||
--todo mob limiting
|
||||
--MAIN LOOP
|
||||
|
||||
if mobs_spawn then
|
||||
|
||||
local perlin_noise
|
||||
|
||||
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
||||
local dimension = dimension or mcl_worlds.pos_to_dimension(pos)
|
||||
local goal_pos = get_next_mob_spawn_pos(pos)
|
||||
local spawning_position_list = find_nodes_in_area_under_air(
|
||||
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
||||
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
||||
{"group:solid", "group:water", "group:lava"}
|
||||
)
|
||||
if #spawning_position_list <= 0 then return end
|
||||
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
||||
|
||||
--hard code mob limit in area to 5 for now
|
||||
if count_mobs(spawning_position) >= 5 then return end
|
||||
|
||||
local gotten_node = get_node(spawning_position).name
|
||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||
if not gotten_node or not gotten_biome then return end
|
||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||
|
||||
--add this so mobs don't spawn inside nodes
|
||||
spawning_position.y = spawning_position.y + 1
|
||||
|
||||
--only need to poll for node light if everything else worked
|
||||
local gotten_light = get_node_light(spawning_position)
|
||||
|
||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||
local is_ground = not (is_water or is_lava)
|
||||
if not is_ground then
|
||||
spawning_position.y = spawning_position.y - 1
|
||||
end
|
||||
|
||||
local mob_def
|
||||
|
||||
--create a disconnected clone of the spawn dictionary
|
||||
--prevents memory leak
|
||||
local mob_library_worker_table = table_copy(spawn_dictionary)
|
||||
|
||||
--grab mob that fits into the spawning location
|
||||
--randomly grab a mob, don't exclude any possibilities
|
||||
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
||||
local noise = perlin_noise:get_3d(spawning_position)
|
||||
local current_summary_chance = summary_chance
|
||||
while #mob_library_worker_table > 0 do
|
||||
local mob_chance_offset = (math_round(noise * current_summary_chance + 12345) % current_summary_chance) + 1
|
||||
local mob_index = 1
|
||||
local mob_chance = mob_library_worker_table[mob_index].chance
|
||||
local step_chance = mob_chance
|
||||
while step_chance < mob_chance_offset do
|
||||
mob_index = mob_index + 1
|
||||
mob_chance = mob_library_worker_table[mob_index].chance
|
||||
step_chance = step_chance + mob_chance
|
||||
end
|
||||
local mob_def = mob_library_worker_table[mob_index]
|
||||
if mob_def
|
||||
and spawning_position.y >= mob_def.min_height
|
||||
and spawning_position.y <= mob_def.max_height
|
||||
and mob_def.dimension == dimension
|
||||
and biome_check(mob_def.biomes, gotten_biome)
|
||||
and gotten_light >= mob_def.min_light
|
||||
and gotten_light <= mob_def.max_light
|
||||
and (is_ground or mob_def.type_of_spawning ~= "ground")
|
||||
and (mob_def.check_position and mob_def.check_position(spawning_position) or true)
|
||||
then
|
||||
--everything is correct, spawn mob
|
||||
local object = spawn_mob(mob_def.name, spawning_position)
|
||||
if object then
|
||||
return mob_def.on_spawn and mob_def.on_spawn(object, spawning_position)
|
||||
end
|
||||
end
|
||||
current_summary_chance = current_summary_chance - mob_chance
|
||||
table_remove(mob_library_worker_table, mob_index)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--MAIN LOOP
|
||||
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer >= 10 then
|
||||
timer = 0
|
||||
for _,player in pairs(get_connected_players()) do
|
||||
-- after this line each "break" means "continue"
|
||||
local do_mob_spawning = true
|
||||
repeat
|
||||
--don't need to get these variables more than once
|
||||
--they happen in a single server step
|
||||
|
||||
local player_pos = player:get_pos()
|
||||
local dimension = mcl_worlds.pos_to_dimension(player_pos)
|
||||
|
||||
if dimension == "void" or dimension == "default" then
|
||||
break -- ignore void and unloaded area
|
||||
end
|
||||
|
||||
local min, max = decypher_limits(player_pos.y)
|
||||
|
||||
for i = 1, math_random(1,4) do
|
||||
-- after this line each "break" means "continue"
|
||||
local do_mob_algorithm = true
|
||||
repeat
|
||||
|
||||
local goal_pos = position_calculation(player_pos)
|
||||
|
||||
local spawning_position_list = find_nodes_in_area_under_air(vector_new(goal_pos.x,min,goal_pos.z), vector_new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
|
||||
|
||||
--couldn't find node
|
||||
if #spawning_position_list <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
|
||||
|
||||
--Prevent strange behavior --- this is commented out: /too close to player --fixed with inner circle
|
||||
if not spawning_position then -- or vector_distance(player_pos, spawning_position) < 15
|
||||
break
|
||||
end
|
||||
|
||||
--hard code mob limit in area to 5 for now
|
||||
if count_mobs(spawning_position) >= 5 then
|
||||
break
|
||||
end
|
||||
|
||||
local gotten_node = get_node(spawning_position).name
|
||||
|
||||
if not gotten_node or gotten_node == "air" then --skip air nodes
|
||||
break
|
||||
end
|
||||
|
||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||
|
||||
if not gotten_biome then
|
||||
break --skip if in unloaded area
|
||||
end
|
||||
|
||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||
|
||||
--add this so mobs don't spawn inside nodes
|
||||
spawning_position.y = spawning_position.y + 1
|
||||
|
||||
--only need to poll for node light if everything else worked
|
||||
local gotten_light = get_node_light(spawning_position)
|
||||
|
||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||
|
||||
local mob_def = nil
|
||||
|
||||
--create a disconnected clone of the spawn dictionary
|
||||
--prevents memory leak
|
||||
local mob_library_worker_table = table_copy(spawn_dictionary)
|
||||
|
||||
--grab mob that fits into the spawning location
|
||||
--randomly grab a mob, don't exclude any possibilities
|
||||
local repeat_mob_search = true
|
||||
repeat
|
||||
|
||||
--do not infinite loop
|
||||
if #mob_library_worker_table <= 0 then
|
||||
--print("breaking infinite loop")
|
||||
break
|
||||
end
|
||||
|
||||
local skip = false
|
||||
|
||||
--use this for removing table elements of mobs that do not match
|
||||
local temp_index = math_random(1,#mob_library_worker_table)
|
||||
|
||||
local temp_def = mob_library_worker_table[temp_index]
|
||||
|
||||
--skip if something ridiculous happens (nil mob def)
|
||||
--something truly horrible has happened if skip gets
|
||||
--activated at this point
|
||||
if not temp_def then
|
||||
skip = true
|
||||
end
|
||||
|
||||
if not skip and (spawning_position.y < temp_def.min_height or spawning_position.y > temp_def.max_height) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
--skip if not correct dimension
|
||||
if not skip and (temp_def.dimension ~= dimension) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
--skip if not in correct biome
|
||||
if not skip and (not biome_check(temp_def.biomes, gotten_biome)) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
--don't spawn if not in light limits
|
||||
if not skip and (gotten_light < temp_def.min_light or gotten_light > temp_def.max_light) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
--skip if not in correct spawning type
|
||||
if not skip and (temp_def.type_of_spawning == "ground" and is_water) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
if not skip and (temp_def.type_of_spawning == "ground" and is_lava) then
|
||||
skip = true
|
||||
end
|
||||
|
||||
--found a mob, exit out of loop
|
||||
if not skip then
|
||||
--minetest.log("warning", "found mob:"..temp_def.name)
|
||||
--print("found mob:"..temp_def.name)
|
||||
mob_def = table_copy(temp_def)
|
||||
break
|
||||
else
|
||||
--minetest.log("warning", "deleting temp index "..temp_index)
|
||||
--print("deleting temp index")
|
||||
table_remove(mob_library_worker_table, temp_index)
|
||||
end
|
||||
|
||||
until repeat_mob_search == false --this is needed to sort through mobs randomly
|
||||
|
||||
|
||||
--catch if went through all mobs and something went horribly wrong
|
||||
--could not find a valid mob to spawn that fits the environment
|
||||
if not mob_def then
|
||||
break
|
||||
end
|
||||
|
||||
--adjust the position for water and lava mobs
|
||||
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
|
||||
spawning_position.y = spawning_position.y - 1
|
||||
end
|
||||
|
||||
--print("spawning: " .. mob_def.name)
|
||||
|
||||
--everything is correct, spawn mob
|
||||
minetest.add_entity(spawning_position, mob_def.name)
|
||||
|
||||
break
|
||||
until do_mob_algorithm == false --this is a safety catch
|
||||
end
|
||||
|
||||
break
|
||||
until do_mob_spawning == false --this is a performance catch
|
||||
if timer < 10 then return end
|
||||
timer = 0
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
local pos = player:get_pos()
|
||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
||||
-- ignore void and unloaded area
|
||||
if dimension ~= "void" and dimension ~= "default" then
|
||||
local y_min, y_max = decypher_limits(pos.y)
|
||||
for i = 1, math_random(1, 4) do
|
||||
spawn_a_mob(pos, dimension, y_min, y_max)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# textdomain: mcl_mobs
|
||||
Peaceful mode active! No monsters will spawn.=Mode paisible actif! Aucun monstre n'apparaîtra.
|
||||
Peaceful mode active! No monsters will spawn.=Mode paisible actif ! Aucun monstre n'apparaîtra.
|
||||
This allows you to place a single mob.=Cela vous permet de placer un seul mob.
|
||||
Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns.=Placez-le là où vous voulez que le mob apparaisse. Les animaux apparaîtront apprivoisés, sauf si vous maintenez la touche furtive enfoncée pendant le placement. Si vous le placez sur un générateur de mob, vous changez le mob qu'il génère.
|
||||
You need the “maphack” privilege to change the mob spawner.=Vous avez besoin du privilège "maphack" pour changer le générateur de mob.
|
||||
Name Tag=Étiquette de nom
|
||||
A name tag is an item to name a mob.=Une étiquette de nom est un élément pour nommer un mob.
|
||||
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=Avant d'utiliser l'étiquette de nom, vous devez définir un nom sur une enclume. Ensuite, vous pouvez utiliser l'étiquette de nom pour nommer un mob. Cela utilise l'étiquette de nom.
|
||||
Only peaceful mobs allowed!=Seuls les mobs pacifiques sont autorisés!
|
||||
Only peaceful mobs allowed!=Seuls les mobs pacifiques sont autorisés !
|
||||
Give names to mobs=Donne des noms aux mobs
|
||||
Set name at anvil=Définir le nom sur l'enclume
|
||||
|
|
|
@ -160,6 +160,7 @@ minetest.register_entity("mcl_paintings:painting", {
|
|||
set_entity(self.object)
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
if not self then return end
|
||||
local data = {
|
||||
_facing = self._facing,
|
||||
_pos = self._pos,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# textdomain:mcl_paintings
|
||||
Painting=Pintura
|
|
@ -61,6 +61,7 @@ dofile(path .. "/villager.lua") -- KrupnoPavel Mesh and animation by toby109tt
|
|||
--dofile(path .. "/agent.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
|
||||
-- Illagers and witch
|
||||
dofile(path .. "/pillager.lua") -- Mesh by KrupnoPavel and MrRar, animation by MrRar
|
||||
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||
|
|
|
@ -74,3 +74,4 @@ Tool Smith=Fabriquant d'outil
|
|||
Cleric=Clerc
|
||||
Nitwit=Crétin
|
||||
Protects you from death while wielding it=Vous protège de la mort en le maniant
|
||||
Pillager=Pillard
|
|
@ -0,0 +1,77 @@
|
|||
# textdomain: mobs_mc
|
||||
Totem of Undying=Totèm d'Imortalitat
|
||||
A totem of undying is a rare artifact which may safe you from certain death.=Un totèm d'imortalitat es un artefacte rara que pòt vos sauvar d'una mòrt surada.
|
||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=
|
||||
Agent=Agent
|
||||
Bat=Ratapenada
|
||||
Blaze=Blaze
|
||||
Chicken=Pola
|
||||
Cow=Vacha
|
||||
Mooshroom=Champavacha
|
||||
Creeper=Creeper
|
||||
Ender Dragon=Drac de l'End
|
||||
Enderman=Òme de l'End
|
||||
Endermite=Endarna
|
||||
Ghast=Trèva
|
||||
Elder Guardian=Gardian Ainat
|
||||
Guardian=Gardian
|
||||
Horse=Ega
|
||||
Skeleton Horse=Ega Esquelèta
|
||||
Zombie Horse=Ega Mòrtaviva
|
||||
Donkey=Asne
|
||||
Mule=Miula
|
||||
Iron Golem=Golèm de Fèrre
|
||||
Llama=Lama
|
||||
Ocelot=Ocelòt
|
||||
Parrot=Papagai
|
||||
Pig=Pòrc
|
||||
Polar Bear=Ors Polar
|
||||
Rabbit=Lapin
|
||||
Killer Bunny=Lapin Tuaire
|
||||
The Killer Bunny=Lo Lapin Tuaire
|
||||
Sheep=Moton
|
||||
Shulker=
|
||||
Silverfish=
|
||||
Skeleton=
|
||||
Stray=
|
||||
Wither Skeleton=
|
||||
Magma Cube=
|
||||
Slime=
|
||||
Snow Golem=
|
||||
Spider=
|
||||
Cave Spider=
|
||||
Squid=
|
||||
Vex=
|
||||
Evoker=
|
||||
Illusioner=
|
||||
Villager=
|
||||
Vindicator=
|
||||
Zombie Villager=
|
||||
Witch=
|
||||
Wither=
|
||||
Wolf=
|
||||
Husk=
|
||||
Zombie=
|
||||
Zombie Pigman=
|
||||
Iron Horse Armor=
|
||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=
|
||||
Golden Horse Armor=
|
||||
Golden horse armor can be worn by horses to increase their protection from harm.=
|
||||
Diamond Horse Armor=
|
||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=
|
||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=
|
||||
Farmer=
|
||||
Fisherman=
|
||||
Fletcher=
|
||||
Shepherd=
|
||||
Librarian=
|
||||
Cartographer=
|
||||
Armorer=
|
||||
Leatherworker=
|
||||
Butcher=
|
||||
Weapon Smith=
|
||||
Tool Smith=
|
||||
Cleric=
|
||||
Nitwit=
|
||||
Protects you from death while wielding it=
|
||||
Pillager=
|
|
@ -74,3 +74,4 @@ Tool Smith=
|
|||
Cleric=
|
||||
Nitwit=
|
||||
Protects you from death while wielding it=
|
||||
Pillager=
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
local function reload(self)
|
||||
if not self or not self.object then return end
|
||||
minetest.sound_play("mcl_bows_crossbow_drawback_1", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
if not props then return end
|
||||
props.textures[2] = "mcl_bows_crossbow_3.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
end
|
||||
|
||||
local function reset_animation(self, animation)
|
||||
if not self or not self.object or self.current_animation ~= animation then return end
|
||||
self.current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
||||
mobs.set_mob_animation(self, animation)
|
||||
end
|
||||
|
||||
pillager = {
|
||||
description = S("Pillager"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
hostile = true,
|
||||
rotate = 270,
|
||||
hp_min = 24,
|
||||
hp_max = 24,
|
||||
xp_min = 6,
|
||||
xp_max = 6,
|
||||
breath_max = -1,
|
||||
eye_height = 1.5,
|
||||
projectile_cooldown = 3, -- Useless
|
||||
shoot_interval = 3, -- Useless
|
||||
shoot_offset = 1.5,
|
||||
dogshoot_switch = 1,
|
||||
dogshoot_count_max = 1.8,
|
||||
projectile_cooldown_min = 3,
|
||||
projectile_cooldown_max = 2.5,
|
||||
armor = {fleshy = 100},
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.98, 0.3},
|
||||
pathfinding = 1,
|
||||
group_attack = true,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_pillager.b3d",
|
||||
|
||||
--head code
|
||||
has_head = false,
|
||||
head_bone = "head",
|
||||
|
||||
swap_y_with_x = true,
|
||||
reverse_head_yaw = true,
|
||||
|
||||
head_bone_pos_y = 2.4,
|
||||
head_bone_pos_z = 0,
|
||||
|
||||
head_height_offset = 1.1,
|
||||
head_direction_offset = 0,
|
||||
head_pitch_modifier = 0,
|
||||
--end head code
|
||||
|
||||
visual_size = {x=2.75, y=2.75},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1.2,
|
||||
run_velocity = 4,
|
||||
damage = 2,
|
||||
reach = 8,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
attack_type = "projectile",
|
||||
arrow = "mcl_bows:arrow_entity",
|
||||
sounds = {
|
||||
random = "mobs_mc_pillager_grunt2",
|
||||
war_cry = "mobs_mc_pillager_grunt1",
|
||||
death = "mobs_mc_pillager_ow2",
|
||||
damage = "mobs_mc_pillager_ow1",
|
||||
distance = 16,
|
||||
},
|
||||
textures = {
|
||||
{
|
||||
"mobs_mc_pillager.png", -- Skin
|
||||
"mcl_bows_crossbow_3.png^[resize:16x16", -- Wielded item
|
||||
}
|
||||
},
|
||||
drops = {
|
||||
{
|
||||
name = "mcl_bows:arrow",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",
|
||||
},
|
||||
{
|
||||
name = "mcl_bows:crossbow",
|
||||
chance = 100 / 8.5,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
},
|
||||
},
|
||||
animation = {
|
||||
unloaded_walk_start = 1,
|
||||
unloaded_walk_end = 40,
|
||||
unloaded_stand_start = 41,
|
||||
unloaded_stand_end = 60,
|
||||
|
||||
reload_stand_speed = 20,
|
||||
reload_stand_start = 61,
|
||||
reload_stand_end = 100,
|
||||
|
||||
stand_speed = 6,
|
||||
stand_start = 101,
|
||||
stand_end = 109,
|
||||
|
||||
walk_speed = 25,
|
||||
walk_start = 111,
|
||||
walk_end = 150,
|
||||
run_speed = 40,
|
||||
run_start = 111,
|
||||
run_end = 150,
|
||||
|
||||
reload_run_speed = 20,
|
||||
reload_run_start = 151,
|
||||
reload_run_end = 190,
|
||||
|
||||
die_speed = 15,
|
||||
die_start = 191,
|
||||
die_end = 192,
|
||||
die_loop = false,
|
||||
|
||||
stand_unloaded_start = 40,
|
||||
stand_unloaded_end = 59,
|
||||
},
|
||||
shoot_arrow = function(self, pos, dir)
|
||||
minetest.sound_play("mcl_bows_crossbow_shoot", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
props.textures[2] = "mcl_bows_crossbow_0.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
local old_anim = self.current_animation
|
||||
if old_anim == "run" then
|
||||
mobs.set_mob_animation(self, "reload_run")
|
||||
end
|
||||
if old_anim == "stand" then
|
||||
mobs.set_mob_animation(self, "reload_stand")
|
||||
end
|
||||
self.current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
||||
minetest.after(1, reload, self)
|
||||
minetest.after(2, reset_animation, self, old_anim)
|
||||
mobs.shoot_projectile_handling(
|
||||
"mcl_bows:arrow", pos, dir, self.object:get_yaw(),
|
||||
self.object, 30, math.random(3,4))
|
||||
|
||||
-- While we are at it, change the sounds since there is no way to do this in Mobs Redo
|
||||
if self.sounds and self.sounds.random then
|
||||
self.sounds = table.copy(self.sounds)
|
||||
self.sounds.random = "mobs_mc_pillager_grunt" .. math.random(2)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
mobs:register_mob("mobs_mc:pillager", pillager)
|
||||
mobs:register_egg("mobs_mc:pillager", S("Pillager"), "mobs_mc_spawn_icon_pillager.png", 0)
|
|
@ -2,118 +2,27 @@
|
|||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local rabbit = {
|
||||
description = S("Rabbit"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
passive = true,
|
||||
reach = 1,
|
||||
rotate = 270,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
||||
local mob_name = "mobs_mc:rabbit"
|
||||
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_rabbit.b3d",
|
||||
textures = {
|
||||
local textures = {
|
||||
{"mobs_mc_rabbit_brown.png"},
|
||||
{"mobs_mc_rabbit_gold.png"},
|
||||
{"mobs_mc_rabbit_white.png"},
|
||||
{"mobs_mc_rabbit_white_splotched.png"},
|
||||
{"mobs_mc_rabbit_salt.png"},
|
||||
{"mobs_mc_rabbit_black.png"},
|
||||
},
|
||||
visual_size = {x=1.5, y=1.5},
|
||||
sounds = {
|
||||
random = "mobs_mc_rabbit_random",
|
||||
damage = "mobs_mc_rabbit_hurt",
|
||||
death = "mobs_mc_rabbit_death",
|
||||
attack = "mobs_mc_rabbit_attack",
|
||||
eat = "mobs_mc_animal_eat_generic",
|
||||
distance = 16,
|
||||
},
|
||||
makes_footstep_sound = false,
|
||||
walk_velocity = 1,
|
||||
run_velocity = 3.7,
|
||||
follow_velocity = 1.1,
|
||||
floats = 1,
|
||||
runaway = true,
|
||||
jump = true,
|
||||
drops = {
|
||||
{name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1, looting = "common",},
|
||||
{name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1, looting = "common",},
|
||||
{name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,},
|
||||
},
|
||||
fear_height = 4,
|
||||
animation = {
|
||||
speed_normal = 25, speed_run = 50,
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 20,
|
||||
run_start = 0, run_end = 20,
|
||||
},
|
||||
-- Follow (yellow) dangelions, carrots and golden carrots
|
||||
follow = mobs_mc.follow.rabbit,
|
||||
view_range = 8,
|
||||
-- Eat carrots and reduce their growth stage by 1
|
||||
replace_rate = 10,
|
||||
replace_what = mobs_mc.replace.rabbit,
|
||||
on_rightclick = function(self, clicker)
|
||||
-- Feed, tame protect or capture
|
||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||
end,
|
||||
do_custom = function(self)
|
||||
-- Easter egg: Change texture if rabbit is named “Toast”
|
||||
if self.nametag == "Toast" and not self._has_toast_texture then
|
||||
self._original_rabbit_texture = self.base_texture
|
||||
self.base_texture = { "mobs_mc_rabbit_toast.png" }
|
||||
self.object:set_properties({ textures = self.base_texture })
|
||||
self._has_toast_texture = true
|
||||
elseif self.nametag ~= "Toast" and self._has_toast_texture then
|
||||
self.base_texture = self._original_rabbit_texture
|
||||
self.object:set_properties({ textures = self.base_texture })
|
||||
self._has_toast_texture = false
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
mobs:register_mob("mobs_mc:rabbit", rabbit)
|
||||
local sounds = {
|
||||
random = "mobs_mc_rabbit_random",
|
||||
damage = "mobs_mc_rabbit_hurt",
|
||||
death = "mobs_mc_rabbit_death",
|
||||
attack = "mobs_mc_rabbit_attack",
|
||||
eat = "mobs_mc_animal_eat_generic",
|
||||
distance = 16,
|
||||
}
|
||||
|
||||
-- The killer bunny (Only with spawn egg)
|
||||
local killer_bunny = table.copy(rabbit)
|
||||
killer_bunny.description = S("Killer Bunny")
|
||||
killer_bunny.type = "monster"
|
||||
killer_bunny.spawn_class = "hostile"
|
||||
killer_bunny.attack_type = "dogfight"
|
||||
killer_bunny.specific_attack = { "player", "mobs_mc:wolf", "mobs_mc:dog" }
|
||||
killer_bunny.damage = 8
|
||||
killer_bunny.passive = false
|
||||
-- 8 armor points
|
||||
killer_bunny.armor = 50
|
||||
killer_bunny.textures = { "mobs_mc_rabbit_caerbannog.png" }
|
||||
killer_bunny.view_range = 16
|
||||
killer_bunny.replace_rate = nil
|
||||
killer_bunny.replace_what = nil
|
||||
killer_bunny.on_rightclick = nil
|
||||
killer_bunny.run_velocity = 6
|
||||
killer_bunny.do_custom = function(self)
|
||||
if not self._killer_bunny_nametag_set then
|
||||
self.nametag = S("The Killer Bunny")
|
||||
self._killer_bunny_nametag_set = true
|
||||
end
|
||||
end
|
||||
|
||||
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||
|
||||
-- Mob spawning rules.
|
||||
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:rabbit",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
local biome_list = {
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
|
@ -161,73 +70,149 @@ mobs:spawn_specific(
|
|||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
8,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
--[[
|
||||
local spawn = {
|
||||
name = "mobs_mc:rabbit",
|
||||
neighbors = {"air"},
|
||||
chance = 15000,
|
||||
active_object_count = 10,
|
||||
min_light = 0,
|
||||
max_light = minetest.LIGHT_MAX+1,
|
||||
min_height = mobs_mc.spawn_height.overworld_min,
|
||||
max_height = mobs_mc.spawn_height.overworld_max,
|
||||
}
|
||||
|
||||
local spawn_desert = table.copy(spawn)
|
||||
spawn_desert.nodes = mobs_mc.spawn.desert
|
||||
spawn_desert.on_spawn = function(self, pos)
|
||||
local texture = "mobs_mc_rabbit_gold.png"
|
||||
self.base_texture = { "mobs_mc_rabbit_gold.png" }
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
end
|
||||
mobs:spawn(spawn_desert)
|
||||
|
||||
local spawn_snow = table.copy(spawn)
|
||||
spawn_snow.nodes = mobs_mc.spawn.snow
|
||||
spawn_snow.on_spawn = function(self, pos)
|
||||
local function spawn_rabbit(pos)
|
||||
local biome_data = minetest.get_biome_data(pos)
|
||||
local biome_name = biome_data and minetest.get_biome_name(biome_data.biome) or ""
|
||||
local mob = minetest.add_entity(pos, mob_name)
|
||||
if not mob then return end
|
||||
local self = mob:get_luaentity()
|
||||
local texture
|
||||
local r = math.random(1, 100)
|
||||
-- 80% white fur
|
||||
if r <= 80 then
|
||||
texture = "mobs_mc_rabbit_white.png"
|
||||
-- 20% black and white fur
|
||||
if biome_name:find("Desert") then
|
||||
texture = "mobs_mc_rabbit_gold.png"
|
||||
else
|
||||
texture = "mobs_mc_rabbit_white_splotched.png"
|
||||
local r = math.random(1, 100)
|
||||
if biome_name:find("Ice") or biome_name:find("snow") or biome_name:find("Cold") then
|
||||
-- 80% white fur
|
||||
if r <= 80 then
|
||||
texture = "mobs_mc_rabbit_white.png"
|
||||
-- 20% black and white fur
|
||||
else
|
||||
texture = "mobs_mc_rabbit_white_splotched.png"
|
||||
end
|
||||
else
|
||||
-- 50% brown fur
|
||||
if r <= 50 then
|
||||
texture = "mobs_mc_rabbit_brown.png"
|
||||
-- 40% salt fur
|
||||
elseif r <= 90 then
|
||||
texture = "mobs_mc_rabbit_salt.png"
|
||||
-- 10% black fur
|
||||
else
|
||||
texture = "mobs_mc_rabbit_black.png"
|
||||
end
|
||||
end
|
||||
end
|
||||
self.base_texture = { texture }
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
self.base_texture = {texture}
|
||||
self.object:set_properties({textures = {texture}})
|
||||
end
|
||||
mobs:spawn(spawn_snow)
|
||||
|
||||
local spawn_grass = table.copy(spawn)
|
||||
spawn_grass.nodes = mobs_mc.spawn.grassland
|
||||
spawn_grass.on_spawn = function(self, pos)
|
||||
local texture
|
||||
local r = math.random(1, 100)
|
||||
-- 50% brown fur
|
||||
if r <= 50 then
|
||||
texture = "mobs_mc_rabbit_brown.png"
|
||||
-- 40% salt fur
|
||||
elseif r <= 90 then
|
||||
texture = "mobs_mc_rabbit_salt.png"
|
||||
-- 10% black fur
|
||||
else
|
||||
texture = "mobs_mc_rabbit_black.png"
|
||||
local function do_custom_rabbit(self)
|
||||
-- Easter egg: Change texture if rabbit is named “Toast”
|
||||
if self.nametag == "Toast" and not self._has_toast_texture then
|
||||
self._original_rabbit_texture = self.base_texture
|
||||
self.base_texture = { "mobs_mc_rabbit_toast.png" }
|
||||
self.object:set_properties({ textures = self.base_texture })
|
||||
self._has_toast_texture = true
|
||||
elseif self.nametag ~= "Toast" and self._has_toast_texture then
|
||||
self.base_texture = self._original_rabbit_texture
|
||||
self.object:set_properties({ textures = self.base_texture })
|
||||
self._has_toast_texture = false
|
||||
end
|
||||
self.base_texture = { texture }
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
end
|
||||
mobs:spawn(spawn_grass)
|
||||
]]--
|
||||
|
||||
local rabbit = {
|
||||
description = S("Rabbit"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
passive = true,
|
||||
reach = 1,
|
||||
rotate = 270,
|
||||
hp_min = 3,
|
||||
hp_max = 3,
|
||||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_rabbit.b3d",
|
||||
textures = textures,
|
||||
visual_size = {x=1.5, y=1.5},
|
||||
sounds = sounds,
|
||||
makes_footstep_sound = false,
|
||||
walk_velocity = 1,
|
||||
run_velocity = 3.7,
|
||||
follow_velocity = 1.1,
|
||||
floats = 1,
|
||||
runaway = true,
|
||||
jump = true,
|
||||
drops = {
|
||||
{name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1, looting = "common",},
|
||||
{name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1, looting = "common",},
|
||||
{name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,},
|
||||
},
|
||||
fear_height = 4,
|
||||
animation = {
|
||||
speed_normal = 25, speed_run = 50,
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 20,
|
||||
run_start = 0, run_end = 20,
|
||||
},
|
||||
-- Follow (yellow) dangelions, carrots and golden carrots
|
||||
follow = mobs_mc.follow.rabbit,
|
||||
view_range = 8,
|
||||
-- Eat carrots and reduce their growth stage by 1
|
||||
replace_rate = 10,
|
||||
replace_what = mobs_mc.replace.rabbit,
|
||||
on_rightclick = function(self, clicker)
|
||||
-- Feed, tame protect or capture
|
||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||
end,
|
||||
do_custom = do_custom_rabbit,
|
||||
spawn = spawn_rabbit
|
||||
}
|
||||
|
||||
mobs:register_mob(mob_name, rabbit)
|
||||
|
||||
-- The killer bunny (Only with spawn egg)
|
||||
local killer_bunny = table.copy(rabbit)
|
||||
killer_bunny.description = S("Killer Bunny")
|
||||
killer_bunny.type = "monster"
|
||||
killer_bunny.spawn_class = "hostile"
|
||||
killer_bunny.attack_type = "dogfight"
|
||||
killer_bunny.specific_attack = { "player", "mobs_mc:wolf", "mobs_mc:dog" }
|
||||
killer_bunny.damage = 8
|
||||
killer_bunny.passive = false
|
||||
-- 8 armor points
|
||||
killer_bunny.armor = 50
|
||||
killer_bunny.textures = { "mobs_mc_rabbit_caerbannog.png" }
|
||||
killer_bunny.view_range = 16
|
||||
killer_bunny.replace_rate = nil
|
||||
killer_bunny.replace_what = nil
|
||||
killer_bunny.on_rightclick = nil
|
||||
killer_bunny.run_velocity = 6
|
||||
killer_bunny.do_custom = function(self)
|
||||
if not self._killer_bunny_nametag_set then
|
||||
self.nametag = S("The Killer Bunny")
|
||||
self._killer_bunny_nametag_set = true
|
||||
end
|
||||
end
|
||||
|
||||
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||
|
||||
-- Mob spawning rules.
|
||||
-- Different skins depending on spawn location <- we customized spawn function
|
||||
|
||||
mobs:spawn_setup({
|
||||
name = mob_name,
|
||||
min_light = 9,
|
||||
chance = 1000,
|
||||
aoc = 8,
|
||||
biomes = biome_list,
|
||||
group_size_max = 1,
|
||||
baby_min = 1,
|
||||
baby_max = 2,
|
||||
})
|
||||
|
||||
-- Spawn egg
|
||||
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
||||
|
|
|
@ -87,11 +87,11 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
swap_y_with_x = false,
|
||||
reverse_head_yaw = false,
|
||||
|
||||
head_bone_pos_y = 3.6,
|
||||
head_bone_pos_z = -0.6,
|
||||
head_bone_pos_y = 0,
|
||||
head_bone_pos_z = 0,
|
||||
|
||||
head_height_offset = 1.0525,
|
||||
head_direction_offset = 0.5,
|
||||
head_height_offset = 1.2,
|
||||
head_direction_offset = 0,
|
||||
head_pitch_modifier = 0,
|
||||
--end head code
|
||||
|
||||
|
@ -117,7 +117,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
},
|
||||
animation = {
|
||||
speed_normal = 25, run_speed = 65,
|
||||
stand_start = 40, stand_end = 80,
|
||||
stand_start = 0, stand_end = 0,
|
||||
walk_start = 0, walk_end = 40,
|
||||
run_start = 0, run_end = 40,
|
||||
},
|
||||
|
@ -330,6 +330,24 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
return false
|
||||
end
|
||||
end,
|
||||
on_spawn = function(self)
|
||||
if self.baby then
|
||||
self.animation = table.copy(self.animation)
|
||||
self.animation.stand_start = 81
|
||||
self.animation.stand_end = 81
|
||||
self.animation.walk_start = 81
|
||||
self.animation.walk_end = 121
|
||||
self.animation.run_start = 81
|
||||
self.animation.run_end = 121
|
||||
end
|
||||
return true
|
||||
end,
|
||||
on_grown = function(self)
|
||||
self.animation = nil
|
||||
local anim = self.current_animation
|
||||
self.current_animation = nil -- Mobs Redo does nothing otherwise
|
||||
mobs.set_mob_animation(self, anim)
|
||||
end
|
||||
})
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:sheep",
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
--################### SILVERFISH
|
||||
--###################
|
||||
|
||||
local PLAYER_SCAN_RADIUS = 5
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
mobs:register_mob("mobs_mc:silverfish", {
|
||||
|
@ -46,6 +48,20 @@ mobs:register_mob("mobs_mc:silverfish", {
|
|||
view_range = 16,
|
||||
attack_type = "punch",
|
||||
damage = 1,
|
||||
do_custom = function(self, dtime)
|
||||
self.do_custom_time = (self.do_custom_time or 0) + dtime
|
||||
if self.do_custom_time < 1.5 then return end
|
||||
self.do_custom_time = 0
|
||||
local selfpos = self.object:get_pos()
|
||||
local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
|
||||
for _, obj in pairs(objects) do
|
||||
if obj:is_player() and not minetest.is_creative_enabled(obj:get_player_name()) then
|
||||
self.attacking = obj
|
||||
mobs.group_attack_initialization(self)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
mobs:register_egg("mobs_mc:silverfish", S("Silverfish"), "mobs_mc_spawn_icon_silverfish.png", 0)
|
||||
|
|
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 477 B |
|
@ -712,6 +712,11 @@ local trade_inventory = {
|
|||
elseif listname == "output" then
|
||||
if not trader_exists(player:get_player_name()) then
|
||||
return 0
|
||||
-- Begin Award Code
|
||||
-- May need to be moved if award gets unlocked in the wrong cases.
|
||||
elseif trader_exists(player:get_player_name()) then
|
||||
awards.unlock(player:get_player_name(), "mcl:whatAdeal")
|
||||
-- End Award Code
|
||||
end
|
||||
-- Only allow taking full stack
|
||||
local count = stack:get_count()
|
||||
|
|
|
@ -9,6 +9,95 @@ local S = minetest.get_translator(minetest.get_current_modname())
|
|||
--################### ZOMBIE
|
||||
--###################
|
||||
|
||||
local husk_biomes = {
|
||||
"Desert",
|
||||
"SavannaM",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
}
|
||||
|
||||
local zombie_biomes = {
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
}
|
||||
|
||||
local drops_common = {
|
||||
{name = mobs_mc.items.rotten_flesh,
|
||||
chance = 1,
|
||||
|
@ -115,6 +204,7 @@ local zombie = {
|
|||
attack_type = "punch",
|
||||
punch_timer_cooloff = 0.5,
|
||||
harmed_by_heal = true,
|
||||
spawn_with_armor = true,
|
||||
}
|
||||
|
||||
mobs:register_mob("mobs_mc:zombie", zombie)
|
||||
|
@ -166,230 +256,36 @@ baby_husk.child = 1
|
|||
|
||||
mobs:register_mob("mobs_mc:baby_husk", baby_husk)
|
||||
|
||||
|
||||
-- Spawning
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:zombie",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
6000,
|
||||
4,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_setup({
|
||||
name = "mobs_mc:zombie",
|
||||
biomes = zombie_biomes,
|
||||
max_light = 7,
|
||||
chance = 2000,
|
||||
})
|
||||
|
||||
-- Baby zombie is 20 times less likely than regular zombies
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:baby_zombie",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
60000,
|
||||
4,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_setup({
|
||||
name = "mobs_mc:baby_zombie",
|
||||
biomes = zombie_biomes,
|
||||
max_lignt = 7,
|
||||
chance = 100,
|
||||
})
|
||||
|
||||
mobs:spawn_setup({
|
||||
name = "mobs_mc:husk",
|
||||
biomes = husk_biomes,
|
||||
max_light = 7,
|
||||
chance = 2000,
|
||||
})
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:husk",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Desert",
|
||||
"SavannaM",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
6500,
|
||||
4,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:baby_husk",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Desert",
|
||||
"SavannaM",
|
||||
"Savanna",
|
||||
"Savanna_beach",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
65000,
|
||||
4,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_setup({
|
||||
name = "mobs_mc:baby_husk",
|
||||
biomes = husk_biomes,
|
||||
max_light = 7,
|
||||
chance = 100,
|
||||
})
|
||||
|
||||
-- Spawn eggs
|
||||
mobs:register_egg("mobs_mc:husk", S("Husk"), "mobs_mc_spawn_icon_husk.png", 0)
|
||||
|
|
|
@ -229,19 +229,23 @@ mobs_mc.override.spawn = {
|
|||
}
|
||||
|
||||
-- This table contains important spawn height references for the mob spawn height.
|
||||
mobs_mc.override.spawn_height = {
|
||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
||||
local get_local_settings = function()
|
||||
mobs_mc.override.spawn_height = {
|
||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
||||
|
||||
-- Overworld boundaries (inclusive)
|
||||
overworld_min = mcl_mapgen.overworld.min,
|
||||
overworld_max = mcl_mapgen.overworld.max,
|
||||
-- Overworld boundaries (inclusive)
|
||||
overworld_min = mcl_mapgen.overworld.min,
|
||||
overworld_max = mcl_mapgen.overworld.max,
|
||||
|
||||
-- Nether boundaries (inclusive)
|
||||
nether_min = mcl_mapgen.nether.min,
|
||||
nether_max = mcl_mapgen.nether.max,
|
||||
-- Nether boundaries (inclusive)
|
||||
nether_min = mcl_mapgen.nether.min,
|
||||
nether_max = mcl_mapgen.nether.max,
|
||||
|
||||
-- End boundaries (inclusive)
|
||||
end_min = mcl_mapgen.end_.min,
|
||||
end_max = mcl_mapgen.end_.max,
|
||||
}
|
||||
-- End boundaries (inclusive)
|
||||
end_min = mcl_mapgen.end_.min,
|
||||
end_max = mcl_mapgen.end_.max,
|
||||
}
|
||||
end
|
||||
get_local_settings()
|
||||
mcl_mapgen.register_on_settings_changed(get_local_settings)
|
||||
|
||||
|
|
|
@ -238,8 +238,8 @@ after(5, function(dtime)
|
|||
end)
|
||||
|
||||
minetest.register_chatcommand("lightning", {
|
||||
params = "[<X> <Y> <Z>]",
|
||||
description = S("Let lightning strike at the specified position or yourself"),
|
||||
params = "[<X> <Y> <Z> | <player name>]",
|
||||
description = S("Let lightning strike at the specified position or player. No parameter will strike yourself."),
|
||||
privs = { maphack = true },
|
||||
func = function(name, param)
|
||||
local pos = {}
|
||||
|
@ -247,21 +247,21 @@ minetest.register_chatcommand("lightning", {
|
|||
pos.x = tonumber(pos.x)
|
||||
pos.y = tonumber(pos.y)
|
||||
pos.z = tonumber(pos.z)
|
||||
local player_to_strike
|
||||
if not (pos.x and pos.y and pos.z) then
|
||||
pos = nil
|
||||
player_to_strike = minetest.get_player_by_name(param)
|
||||
if not player_to_strike and param == "" then
|
||||
player_to_strike = minetest.get_player_by_name(name)
|
||||
end
|
||||
end
|
||||
if name == "" and pos == nil then
|
||||
if not player_to_strike and pos == nil then
|
||||
return false, "No position specified and unknown player"
|
||||
end
|
||||
if pos then
|
||||
lightning.strike(pos)
|
||||
else
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
lightning.strike(player:get_pos())
|
||||
else
|
||||
return false, S("No position specified and unknown player")
|
||||
end
|
||||
elseif player_to_strike then
|
||||
lightning.strike(player_to_strike:get_pos())
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: lightning
|
||||
@1 was struck by lightning.=@1 fuguèt pica·t·da per lo tròn
|
||||
Let lightning strike at the specified position or yourself=Pica lo tròn vès una posicion mencionada o sobre vosautr·e·a·s-mema
|
||||
No position specified and unknown player=Pas de posicion mencionada e jogair·e·a pas conegu·t·da
|
|
@ -1,4 +1,4 @@
|
|||
# textdomain: lightning
|
||||
@1 was struck by lightning.=@1 a été frappé(e) par la foudre.
|
||||
Let lightning strike at the specified position or yourself=Fait frapper la foudre à la position spécifiée ou sur vous-même
|
||||
Let lightning strike at the specified position or player. No parameter will strike yourself.=Fait frapper la foudre sur la position ou le joueur indiqué. Sans paramètre, la foudre frappera sur vous-même.
|
||||
No position specified and unknown player=Aucune position spécifiée et joueur inconnu
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# textdomain: lightning
|
||||
@1 was struck by lightning.=
|
||||
Let lightning strike at the specified position or yourself=
|
||||
Let lightning strike at the specified position or player. No parameter will strike yourself.=
|
||||
No position specified and unknown player=
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# textdomain: mcl_void_damage
|
||||
The void is off-limits to you!=Lo voeida es defendut per vosautr·e·a·s !
|
||||
@1 fell into the endless void.=@1 es tombar dins la voeida infinida.
|
|
@ -5,4 +5,4 @@ Error: No weather specified.=Erreur: Aucune météo spécifiée.
|
|||
Error: Invalid parameters.=Erreur: Paramètres non valides.
|
||||
Error: Duration can't be less than 1 second.=Erreur: La durée ne peut pas être inférieure à 1 seconde.
|
||||
Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.=Erreur: Météo non valide spécifiée. Utilisez "clear" (clair), "rain" (pluie), "snow" (neige) ou "thunder" (tonnerre).
|
||||
Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)=Bascule entre temps clair et temps avec chute (au hasard entre pluie, orage ou neige)
|
||||
Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)=Bascule entre temps clair et temps avec des précipitations (au hasard entre pluie, orage ou neige)
|
||||
|
|
|
@ -241,7 +241,7 @@ local function initsky(player)
|
|||
end
|
||||
|
||||
-- MC-style clouds: Layer 127, thickness 4, fly to the “West”
|
||||
player:set_clouds({height=mcl_worlds.layer_to_y(127), speed={x=-2, z=0}, thickness=4, color="#FFF0FEF"})
|
||||
player:set_clouds(mcl_worlds:get_cloud_parameters() or {height=mcl_worlds.layer_to_y(127), speed={x=-2, z=0}, thickness=4, color="#FFF0FEF"})
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(initsky)
|
||||
|
|
|
@ -611,11 +611,14 @@ do
|
|||
io.close(file)
|
||||
if string then
|
||||
local savetable = minetest.deserialize(string)
|
||||
for name, players_stored_data in pairs(savetable.players_stored_data) do
|
||||
doc.data.players[name] = {}
|
||||
doc.data.players[name].stored_data = players_stored_data
|
||||
local savetable_players_stored_data = savetable and savetable.players_stored_data
|
||||
if savetable_players_stored_data then
|
||||
for name, players_stored_data in pairs(savetable_players_stored_data) do
|
||||
doc.data.players[name] = {}
|
||||
doc.data.players[name].stored_data = players_stored_data
|
||||
end
|
||||
minetest.log("action", "[doc] doc.mt successfully read.")
|
||||
end
|
||||
minetest.log("action", "[doc] doc.mt successfully read.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,6 @@ Skeleton view range: -50%=
|
|||
Creeper view range: -50%=
|
||||
Damage: @1=
|
||||
Damage (@1): @2=
|
||||
Durability: @1
|
||||
Healing: @1=
|
||||
Healing (@1): @2=
|
||||
Full punch interval: @1s=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# textdomain:awards
|
||||
# textdomain: awards
|
||||
@1/@2 chat messages=@1/@2 chat messages
|
||||
@1/@2 crafted=@1/@2 fabrication
|
||||
@1/@2 deaths=@1/@2 Mort
|
||||
|
@ -6,12 +6,11 @@
|
|||
@1/@2 game joins=@1/@2 sessions
|
||||
@1/@2 placed=@1/@2 mis
|
||||
@1 (got)=@1 (obtenu)
|
||||
@1: @1=@1: @1
|
||||
@1: @2=@1: @2
|
||||
@1’s awards:=Récompenses de @1:
|
||||
(Secret Award)=(Récompense Secrètte)
|
||||
<achievement ID>=<Succès ID>
|
||||
<name>=<nom>
|
||||
A Cat in a Pop-Tart?!=Un chat beurré ?!
|
||||
Achievement gotten!=Succès obtenu !
|
||||
Achievement gotten:=Succès obtenu :
|
||||
Achievement gotten: @1=Succès obtenu : @1
|
||||
|
@ -28,9 +27,9 @@ Join the game.=Rejoignez le jeu.
|
|||
List awards in chat (deprecated)=Liste des récompenses dans le chat (obsolète)
|
||||
Place a block: @1=Placer un bloc: @1
|
||||
Place blocks: @1×@2=Placer des blocs: @1×@2
|
||||
Secret Achievement gotten!=Succès secret obtenu !
|
||||
Secret Achievement gotten:=Succès secret obtenu :
|
||||
Secret Achievement gotten: @1=Succès secret obtenu : @1
|
||||
Secret achievement gotten!=Succès secret obtenu !
|
||||
Secret achievement gotten:=Succès secret obtenu :
|
||||
Secret achievement gotten: @1=Succès secret obtenu : @1
|
||||
Show details of an achievement=Afficher les détails d'un succès
|
||||
Show, clear, disable or enable your achievements=Affichez, effacez, désactivez ou activez vos succès
|
||||
Get this achievement to find out what it is.=Obtenez ce succès pour découvrir de quoi il s'agit.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# textdomain:awards
|
||||
# textdomain: awards
|
||||
@1/@2 chat messages=
|
||||
@1/@2 crafted=
|
||||
@1/@2 deaths=
|
||||
|
|
|
@ -101,6 +101,18 @@ awards.register_achievement("mcl:bookcase", {
|
|||
}
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:buildIronPickaxe", {
|
||||
title = S("Isn't It Iron Pick"),
|
||||
-- TODO: This achievement should support all non-wood pickaxes
|
||||
description = S("Craft a iron pickaxe using sticks and iron."),
|
||||
icon = "default_tool_steelpick.png",
|
||||
trigger = {
|
||||
type = "craft",
|
||||
item = "mcl_tools:pick_iron",
|
||||
target = 1
|
||||
}
|
||||
})
|
||||
|
||||
-- Item pickup achievements: These are awarded when picking up a certain item.
|
||||
-- The achivements are manually given in the mod mcl_item_entity.
|
||||
awards.register_achievement("mcl:diamonds", {
|
||||
|
@ -125,6 +137,24 @@ awards.register_achievement("mcl:mineWood", {
|
|||
icon = "default_tree.png",
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:whosCuttingOnions", {
|
||||
title = S("Who is Cutting Onions?"),
|
||||
description = S("Pick up a crying obsidian from the floor."),
|
||||
icon = "default_obsidian.png^mcl_core_crying_obsidian.png",
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:hiddenInTheDepths", {
|
||||
title = S("Hidden in the Depths"),
|
||||
description = S("Pick up an Ancient Debris from the floor."),
|
||||
icon = "mcl_nether_ancient_debris_side.png",
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:notQuiteNineLives", {
|
||||
title = S('Not Quite "Nine" Lives'),
|
||||
description = S("Charge a Respawn Anchor to the maximum."),
|
||||
icon = "respawn_anchor_side4.png",
|
||||
})
|
||||
|
||||
-- Smelting achivements: These are awarded when picking up an item from a furnace
|
||||
-- output. They are given in mcl_furnaces.
|
||||
awards.register_achievement("mcl:acquireIron", {
|
||||
|
@ -163,6 +193,87 @@ awards.register_achievement("mcl:buildNetherPortal", {
|
|||
icon = "default_obsidian.png",
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:enterEndPortal", {
|
||||
title = S("The End?"),
|
||||
description = S("Or the beginning?\nHint: Enter an end portal."),
|
||||
icon = "mcl_end_end_stone.png",
|
||||
})
|
||||
|
||||
-- Triggered in mcl_totems
|
||||
awards.register_achievement("mcl:postMortal", {
|
||||
title = S("Postmortal"),
|
||||
description = S("Use a Totem of Undying to cheat death."),
|
||||
icon = "mcl_totems_totem.png",
|
||||
})
|
||||
|
||||
-- Triggered in mcl_beds
|
||||
awards.register_achievement("mcl:sweetDreams", {
|
||||
title = S("Sweet Dreams"),
|
||||
description = S("Sleep in a bed to change your respawn point."),
|
||||
icon = "mcl_beds_bed_red.png",
|
||||
})
|
||||
|
||||
-- Triggered in mcl_smithing_table
|
||||
awards.register_achievement("mcl:seriousDedication", {
|
||||
title = S("Serious Dedication"),
|
||||
description = S("Use a Netherite Ingot to upgrade a hoe, and then completely reevaluate your life choices."),
|
||||
icon = "farming_tool_netheritehoe.png",
|
||||
})
|
||||
|
||||
-- Triggered in mobs_mc
|
||||
awards.register_achievement("mcl:whatAdeal", {
|
||||
title = S("What A Deal!"),
|
||||
description = S("Successfully trade with a Villager."),
|
||||
icon = "mcl_core_emerald.png",
|
||||
})
|
||||
|
||||
-- Triggered in mcl_fishing
|
||||
awards.register_achievement("mcl:fishyBusiness", {
|
||||
title = S("Fishy Business"),
|
||||
description = S("Catch a fish. \nHint: Catch a fish, salmon, clownfish, or pufferfish."),
|
||||
icon = "mcl_fishing_fishing_rod.png",
|
||||
})
|
||||
|
||||
--Triggered in mcl_beacons
|
||||
awards.register_achievement("mcl:beacon", {
|
||||
title = S("Bring Home the Beacon"),
|
||||
description = S("Use a beacon."),
|
||||
icon = "beacon_achievement_icon.png",
|
||||
})
|
||||
|
||||
awards.register_achievement("mcl:maxed_beacon", {
|
||||
title = S("Beaconator"),
|
||||
description = S("Use a fully powered beacon."),
|
||||
icon = "beacon_achievement_icon.png",
|
||||
})
|
||||
|
||||
|
||||
-- Armor Advancements
|
||||
--[[awards.register_achievement("mcl:suitUp", {
|
||||
title = S("Suit Up"),
|
||||
description = S("Protect yourself with a piece of iron armor."),
|
||||
icon = "mcl_armor_inv_chestplate_iron.png",
|
||||
})]]--
|
||||
|
||||
--[[awards.register_achievement("mcl:coverMeDiamonds", {
|
||||
title = S("Cover Me with Diamonds"),
|
||||
description = S("Diamond armor saves lives."),
|
||||
icon = "mcl_armor_inv_chestplate_diamond.png",
|
||||
})]]--
|
||||
|
||||
--[[awards.register_achievement("mcl:coverMeDebris", {
|
||||
title = S("Cover Me in Debris"),
|
||||
description = S("Get a full suit of Netherite armor."),
|
||||
icon = "mcl_armor_inv_chestplate_netherite.png",
|
||||
})]]--
|
||||
|
||||
-- Triggered in extra_mobs
|
||||
awards.register_achievement("mcl:tacticalFishing", {
|
||||
title = S("Tactical Fishing"),
|
||||
description = S("Catch a fish... without a fishing rod!"),
|
||||
icon = "pufferfish_bucket.png",
|
||||
})
|
||||
|
||||
-- NON-PC ACHIEVEMENTS (XBox, Pocket Edition, etc.)
|
||||
|
||||
if non_pc_achievements then
|
||||
|
|
|
@ -47,3 +47,7 @@ Use a crafting table to craft a wooden hoe from wooden planks and sticks.=Benutz
|
|||
Use a crafting table to craft a wooden pickaxe from wooden planks and sticks.=Benutzen Sie eine Werkbank, um eine Holzspitzhacke aus Holzplanken und Stöcken zu fertigen.
|
||||
Use obsidian and a fire starter to construct a Nether portal.=Benutzen Sie Obsidian und ein Feuerzeug, um ein Netherportal zu errichten.
|
||||
Use wheat to craft a bread.=Benutzen Sie Weizen, um ein Brot zu machen.
|
||||
Bring Home the Beacon=Den Nachbarn heimleuchten
|
||||
Use a beacon.=Benutzen Sie ein Leuchtfeuer.
|
||||
Beaconator=Leuchtturmwärter
|
||||
Use a fully powered beacon.=Benutzen Sie ein vollständiges Leuchtfeuer.
|
|
@ -10,7 +10,7 @@ Craft a stone pickaxe using sticks and cobblestone.=Fabriquez une pioche en pier
|
|||
Craft a wooden sword using wooden planks and sticks on a crafting table.=Fabriquez une épée en bois à l'aide de planches et de bâtons en bois sur un établi.
|
||||
DIAMONDS!=DIAMANTS!
|
||||
Delicious Fish=Délicieux Poisson
|
||||
Dispense With This=Dispenser de ça
|
||||
Dispense With This=Dispensé de ça
|
||||
Eat a cooked porkchop.=Mangez du porc cuit.
|
||||
Eat a cooked rabbit.=Mangez du lapin cuit.
|
||||
Get really desperate and eat rotten flesh.=Soyez vraiment désespéré et mangez de la chair pourrie.
|
||||
|
@ -47,3 +47,31 @@ Use a crafting table to craft a wooden hoe from wooden planks and sticks.=Utilis
|
|||
Use a crafting table to craft a wooden pickaxe from wooden planks and sticks.=Utilisez un établi pour fabriquer une pioche en bois à partir de planches et de bâtons en bois.
|
||||
Use obsidian and a fire starter to construct a Nether portal.=Utilisez de l'obsidienne et un briquet pour construire un portail du Nether.
|
||||
Use wheat to craft a bread.=Utilisez du blé pour fabriquer un pain.
|
||||
Stone Age=L'Age de Pierre
|
||||
Mine a stone with new pickaxe.=Miner de la roche avec une nouvelle pioche
|
||||
Hot Stuff=Chaud Devant !
|
||||
Put lava in a bucket.=Remplir un Seau de lave
|
||||
Ice Bucket Challenge=Le défi du seau d'eau glacée
|
||||
Obtain an obsidian block.=Obtenir un bloc d'obsidienne
|
||||
Isn't It Iron Pick=Bonne Pioche !
|
||||
Craft a iron pickaxe using sticks and iron.=Fabriquer une pioche de fer avec des batons et du fer
|
||||
Who is Cutting Onions?=Qui épluche des oignons ?
|
||||
Pick up a crying obsidian from the floor.=Ramasser une obsidienne pleureuse sur le sol.
|
||||
Hidden in the Depths=Caché dans les profondeurs
|
||||
Pick up an Ancient Debris from the floor.=Ramasser un Ancien Débris
|
||||
Not Quite "Nine" Lives=Presque "neuf" vies
|
||||
Charge a Respawn Anchor to the maximum.=Charger une Ancre de Réapparition au maximum.
|
||||
The End?=L'End ?
|
||||
Or the beginning?\nHint: Enter an end portal.=Ou le commencement ?\nAstuce : Entrer dans un portail de l'End.
|
||||
Postmortal=Aux frontières de la mort
|
||||
Use a Totem of Undying to cheat death.=Utiliser un Totem d'imortalité pour tromper la mort.
|
||||
Sweet Dreams=Bonne nuit les petits
|
||||
Sleep in a bed to change your respawn point.=Dormez dans un lit pour changer votre point de réapparition.
|
||||
Serious Dedication=Sérieux dévouement
|
||||
Use a Netherite Ingot to upgrade a hoe, and then completely reevaluate your life choices.=Utilisez un lingot de netherite pour améliorez une houe, puis réévaluez complètement vos choix de vie.
|
||||
Fishy Business=Merci pour le poisson
|
||||
Catch a fish. \nHint: Catch a fish, salmon, clownfish, or pufferfish.=Attrapez un poisson. \nAstuce : attrapez un poisson, saumon, poisson-clown, ou poisson-globe.
|
||||
What A Deal!=Adjugé, Vendu !
|
||||
Successfully trade with a Villager.=Commercez avec succès avec un villageois.
|
||||
Tactical Fishing=Pêche tactique
|
||||
Catch a fish... without a fishing rod=Attrapez un poisson... sans canne à pêche
|
|
@ -53,3 +53,29 @@ Hot Stuff=
|
|||
Put lava in a bucket.=
|
||||
Ice Bucket Challenge=
|
||||
Obtain an obsidian block.=
|
||||
Isn't It Iron Pick=
|
||||
Craft a iron pickaxe using sticks and iron.=
|
||||
Who is Cutting Onions?=
|
||||
Pick up a crying obsidian from the floor.=
|
||||
Hidden in the Depths=
|
||||
Pick up an Ancient Debris from the floor.=
|
||||
Not Quite "Nine" Lives=
|
||||
Charge a Respawn Anchor to the maximum.=
|
||||
The End?=
|
||||
Or the beginning?\nHint: Enter an end portal.=
|
||||
Postmortal=
|
||||
Use a Totem of Undying to cheat death.=
|
||||
Sweet Dreams=
|
||||
Sleep in a bed to change your respawn point.=
|
||||
Serious Dedication=
|
||||
Use a Netherite Ingot to upgrade a hoe, and then completely reevaluate your life choices.=
|
||||
Fishy Business=
|
||||
Catch a fish. \nHint: Catch a fish, salmon, clownfish, or pufferfish.=
|
||||
What A Deal!=
|
||||
Successfully trade with a Villager.=
|
||||
Tactical Fishing=
|
||||
Catch a fish... without a fishing rod=
|
||||
Bring Home the Beacon=
|
||||
Use a beacon.=
|
||||
Beaconator=
|
||||
Use a fully powered beacon.=
|
|
@ -0,0 +1,94 @@
|
|||
1798643961
|
||||
3raven
|
||||
AFCMS
|
||||
aldum
|
||||
Alexander Minges
|
||||
aligator
|
||||
ArTee3
|
||||
Artem Arbatsky
|
||||
balazsszalab
|
||||
basxto
|
||||
Benjamin Schötz
|
||||
Blue Blancmange
|
||||
Booglejr
|
||||
Brandon
|
||||
Bu-Gee
|
||||
bzoss
|
||||
CableGuy67
|
||||
chmodsayshello
|
||||
Code-Sploit
|
||||
cora
|
||||
Daniel Cassidy
|
||||
davedevils
|
||||
Dave Devil's
|
||||
David McMackins II
|
||||
dBeans
|
||||
debiankaios
|
||||
Dieter44
|
||||
Doloment
|
||||
Elias Åström
|
||||
Elias Fleckenstein
|
||||
Emily
|
||||
Emojigit
|
||||
epCode
|
||||
erlehmann
|
||||
FinishedFragment
|
||||
FlamingRCCars
|
||||
Glaucos Ginez
|
||||
Gustavo Ramos Rehermann
|
||||
Guy Liner
|
||||
GuyLiner
|
||||
HimbeerserverDE
|
||||
iliekprogrammar
|
||||
j1233
|
||||
Jared Moody
|
||||
Johannes Fritz
|
||||
jordan4ibanez
|
||||
kabou
|
||||
kay27
|
||||
Laurent Rocher
|
||||
Li0n
|
||||
marcin-serwin
|
||||
Marcin Serwin
|
||||
Mark Roth
|
||||
Mental-Inferno
|
||||
Midgard
|
||||
MysticTempest
|
||||
Nicholas Niro
|
||||
nickolas360
|
||||
Nicu
|
||||
Niklp
|
||||
Nils Dagsson Moskopp
|
||||
NO11
|
||||
NO411
|
||||
Oil_boi
|
||||
pitchum
|
||||
PrairieAstronomer
|
||||
PrairieWind
|
||||
River River
|
||||
Rocher Laurent
|
||||
rootyjr
|
||||
Rootyjr
|
||||
rudzik8
|
||||
Sab Pyrope
|
||||
Saku Laesvuori
|
||||
sfan5
|
||||
SmallJoker
|
||||
Sumyjkl
|
||||
superfloh247
|
||||
Sven792
|
||||
Sydney Gems
|
||||
talamh
|
||||
TechDudie
|
||||
Thinking
|
||||
Tianyang Zhang
|
||||
unknown
|
||||
U.N.Owen
|
||||
Wouters Dorian
|
||||
wuniversales
|
||||
Wuzzy
|
||||
Yukitty
|
||||
yutyo
|
||||
ZedekThePD
|
||||
ZeDique la Ruleta
|
||||
ztianyang
|
|
@ -0,0 +1,5 @@
|
|||
Please run `./update_credits.sh` from [tools](../../../tools) folder to update contributor list.
|
||||
|
||||
Please check that there is no error on execution, and `CONTRUBUTOR_LIST.txt` is updated.
|
||||
|
||||
There should be contributor names, line by line.
|
|
@ -1,10 +1,40 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
|
||||
local contributors_file_name = minetest.get_modpath(modname) .. "/CONTRUBUTOR_LIST.txt"
|
||||
|
||||
local file = io.open(contributors_file_name)
|
||||
local contributors = {}
|
||||
if not file then
|
||||
minetest.log("error", "[" .. modname .. "] Can't read contributors from " .. contributors_file_name)
|
||||
else
|
||||
local contributor_list = file:read("*a")
|
||||
file:close()
|
||||
for contributor in contributor_list:gmatch("[^\r\n]+") do
|
||||
table.insert(contributors, contributor)
|
||||
end
|
||||
end
|
||||
|
||||
mcl_credits = {
|
||||
players = {},
|
||||
description = S("A faithful Open Source clone of Minecraft"),
|
||||
people = dofile(minetest.get_modpath(modname) .. "/people.lua"),
|
||||
people = {
|
||||
{S("Creator of MineClone"), 0x0A9400, {
|
||||
"davedevils",
|
||||
}},
|
||||
{S("Creator of MineClone 2"), 0xFBF837, {
|
||||
"Wuzzy",
|
||||
}},
|
||||
{S("Creators of MineClone 5"), 0x52FF00,
|
||||
contributors
|
||||
},
|
||||
{S("Special thanks"), 0x00E9FF, {
|
||||
"celeron55 for creating Minetest",
|
||||
"Jordach for the jukebox music compilation from Big Freaking Dig",
|
||||
"The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game",
|
||||
"Notch and Jeb for being the major forces behind Minecraft",
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
local function add_hud_element(def, huds, y)
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
|
||||
return {
|
||||
{S("Creator of MineClone"), 0x0A9400, {
|
||||
"davedevils",
|
||||
}},
|
||||
{S("Creator of MineClone2"), 0xFBF837, {
|
||||
"Wuzzy",
|
||||
}},
|
||||
{S("Creator of MineClone5"), 0xFF51D5, {
|
||||
S("The Community"),
|
||||
}},
|
||||
{S("Developers"), 0xF84355, {
|
||||
"Fleckenstein",
|
||||
"kay27",
|
||||
"oilboi",
|
||||
"bzoss",
|
||||
"AFCMS",
|
||||
"epCode",
|
||||
"ryvnf",
|
||||
"iliekprogrammar",
|
||||
"MysticTempest",
|
||||
"Rootyjr",
|
||||
"Nicu",
|
||||
"aligator",
|
||||
"Code-Sploit",
|
||||
"NO11",
|
||||
"cora",
|
||||
}},
|
||||
{S("Contributors"), 0x52FF00, {
|
||||
"Laurent Rocher",
|
||||
"HimbeerserverDE",
|
||||
"TechDudie",
|
||||
"Alexander Minges",
|
||||
"ArTee3",
|
||||
"ZeDique la Ruleta",
|
||||
"pitchum",
|
||||
"wuniversales",
|
||||
"Bu-Gee",
|
||||
"David McMackins II",
|
||||
"Nicholas Niro",
|
||||
"Wouters Dorian",
|
||||
"Blue Blancmange",
|
||||
"Jared Moody",
|
||||
"Li0n",
|
||||
"Midgard",
|
||||
"Saku Laesvuori",
|
||||
"Yukitty",
|
||||
"ZedekThePD",
|
||||
"aldum",
|
||||
"dBeans",
|
||||
"nickolas360",
|
||||
"yutyo",
|
||||
"ztianyang",
|
||||
"j45",
|
||||
"Marcin Serwin",
|
||||
"erlehmann",
|
||||
"E",
|
||||
"Benjamin Schötz",
|
||||
"Doloment",
|
||||
"Sydney Gems",
|
||||
"talamh",
|
||||
"Emily2255",
|
||||
"Emojigit",
|
||||
"FinishedFragment",
|
||||
"sfan5",
|
||||
"Blue Blancmange",
|
||||
"Jared Moody",
|
||||
"SmallJoker",
|
||||
"Sven792",
|
||||
"aldum",
|
||||
}},
|
||||
{S("MineClone5"), 0xA60014, {
|
||||
"kay27",
|
||||
"Debiankaios",
|
||||
"epCode",
|
||||
"NO11",
|
||||
"j45",
|
||||
}},
|
||||
{S("Original Mod Authors"), 0x343434, {
|
||||
"Wuzzy",
|
||||
"Fleckenstein",
|
||||
"BlockMen",
|
||||
"TenPlus1",
|
||||
"PilzAdam",
|
||||
"ryvnf",
|
||||
"stujones11",
|
||||
"Arcelmi",
|
||||
"celeron55",
|
||||
"maikerumine",
|
||||
"GunshipPenguin",
|
||||
"Qwertymine3",
|
||||
"Rochambeau",
|
||||
"rubenwardy",
|
||||
"stu",
|
||||
"4aiman",
|
||||
"Kahrl",
|
||||
"Krock",
|
||||
"UgnilJoZ",
|
||||
"lordfingle",
|
||||
"22i",
|
||||
"bzoss",
|
||||
"kilbith",
|
||||
"xeranas",
|
||||
"kddekadenz",
|
||||
"sofar",
|
||||
"4Evergreen4",
|
||||
"jordan4ibanez",
|
||||
"paramat",
|
||||
}},
|
||||
{S("3D Models"), 0x0019FF, {
|
||||
"22i",
|
||||
"tobyplowy",
|
||||
"epCode",
|
||||
}},
|
||||
{S("Textures"), 0xFF9705, {
|
||||
"XSSheep",
|
||||
"Wuzzy",
|
||||
"kingoscargames",
|
||||
"leorockway",
|
||||
"xMrVizzy",
|
||||
"yutyo",
|
||||
"NO11",
|
||||
"kay27",
|
||||
}},
|
||||
{S("Translations"), 0x00FF60, {
|
||||
"Wuzzy",
|
||||
"Rocher Laurent",
|
||||
"wuniversales",
|
||||
"kay27",
|
||||
"pitchum",
|
||||
"todoporlalibertad",
|
||||
"Marcin Serwin",
|
||||
}},
|
||||
{S("Funders"), 0xF7FF00, {
|
||||
"40W",
|
||||
}},
|
||||
{S("Special thanks"), 0x00E9FF, {
|
||||
"celeron55 for creating Minetest",
|
||||
"Jordach for the jukebox music compilation from Big Freaking Dig",
|
||||
"The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game",
|
||||
"Notch and Jeb for being the major forces behind Minecraft",
|
||||
}},
|
||||
}
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 277 KiB |
|
@ -1,59 +1,56 @@
|
|||
# textdomain: mcl_death_messages
|
||||
@1 was fatally hit by an arrow.=@1 a été mortellement touché par une flèche.
|
||||
@1 has been killed with an arrow.=@1 a été tué avec une flèche.
|
||||
@1 was shot by an arrow from @2.=@1 a été abattu par une flèche de @2.
|
||||
@1 was shot by an arrow from a skeleton.=@1 a été abattu par une flèche d'un squelette.
|
||||
@1 was shot by an arrow from a stray.=@1 a été abattu par une flèche d'un vagabond.
|
||||
@1 was shot by an arrow from an illusioner.=@1 a été abattu par une flèche d'un illusionniste.
|
||||
@1 was shot by an arrow.=@1 a été abattu par une flèche.
|
||||
@1 forgot to breathe.=@1 a oublié de respirer.
|
||||
@1 drowned.=@1 s'est noyé.
|
||||
@1 ran out of oxygen.=@1 a manqué d'oxygène.
|
||||
@1 was killed by @2.=@1 a été tué par @2.
|
||||
@1 was killed.=@1 a été tué.
|
||||
@1 was killed by a mob.=@1 a été tué par un mob.
|
||||
@1 was burned to death by a blaze's fireball.=@1 a été brûlé vif par la boule de feu d'un blaze.
|
||||
@1 was killed by a fireball from a blaze.=@1 a été tué par une boule de feu lors d'un blaze.
|
||||
@1 was burned by a fire charge.=@1 a été brûlé par un incendie.
|
||||
A ghast scared @1 to death.=Un ghast a éffrayé @1 à mort.
|
||||
@1 has been fireballed by a ghast.=@1 a été pétrifié par un ghast.
|
||||
@1 fell from a high cliff.=@1 est tombé d'une haute falaise.
|
||||
@1 took fatal fall damage.=@1 a succombé à un chute mortelle.
|
||||
@1 fell victim to gravity.=@1 a été victime de la gravité.
|
||||
@1 died.=@1 est mort.
|
||||
@1 was killed by a zombie.=@1 a été tué par un zombie.
|
||||
@1 was killed by a baby zombie.=@1 a été tué par un bébé zombie.
|
||||
@1 was killed by a blaze.=@1 a été tué par un blaze.
|
||||
@1 was killed by a slime.=@1 a été tué par un slime.
|
||||
@1 was killed by a witch.=@1 a été tué par un sorcier.
|
||||
@1 was killed by a magma cube.=@1 a été tué par un cube de magma.
|
||||
@1 was killed by a wolf.=@1 a été tué par un loup.
|
||||
@1 was killed by a cat.=@1 a été tué par un chat.
|
||||
@1 was killed by an ocelot.=@1 a été tué par un ocelot.
|
||||
@1 was killed by an ender dragon.=@1 a été tué par un ender dragon.
|
||||
@1 was killed by a wither.=@1 a été tué par un wither.
|
||||
@1 was killed by an enderman.=@1 a été tué par un enderman.
|
||||
@1 was killed by an endermite.=@1 a été tué par un endermite.
|
||||
@1 was killed by a ghast.=@1 a été tué par un ghast.
|
||||
@1 was killed by an elder guardian.=@1 a été tué par un grand gardien.
|
||||
@1 was killed by a guardian.=@1 a été tué par un gardien.
|
||||
@1 was killed by an iron golem.=@1 a été tué par un golem de fer.
|
||||
@1 was killed by a polar_bear.=@1 a été tué par un ours blanc.
|
||||
@1 was killed by a killer bunny.=@1 a été tué par un lapin tueur.
|
||||
@1 was killed by a shulker.=@1 a été tué par un shulker.
|
||||
@1 was killed by a silverfish.=@1 a été tué par un poisson d'argent.
|
||||
@1 was killed by a skeleton.=@1 a été tué par un squelette.
|
||||
@1 was killed by a stray.=@1 a été tué par un vagabond.
|
||||
@1 was killed by a slime.=@1 a été tué par un slime.
|
||||
@1 was killed by a spider.=@1 a été tué par une araignée.
|
||||
@1 was killed by a cave spider.=@1 a été tué par une araignée venimeuse.
|
||||
@1 was killed by a vex.=@1 a été tué par un vex.
|
||||
@1 was killed by an evoker.=@1 a été tué par un invocateur.
|
||||
@1 was killed by an illusioner.=@1 a été tué par un illusionniste.
|
||||
@1 was killed by a vindicator.=@1 a été tué par un vindicateur.
|
||||
@1 was killed by a zombie villager.=@1 a été tué par un villageois zombie.
|
||||
@1 was killed by a husk.=@1 a été tué par un zombie momie.
|
||||
@1 was killed by a baby husk.=@1 a été tué par un bébé zombie momie.
|
||||
@1 was killed by a zombie pigman.=@1 a été tué par un zombie-couchon.
|
||||
@1 was killed by a baby zombie pigman.=@1 a été tué par un bébé zombie-couchon
|
||||
@1 was slain by @2.=@1 a été tué par @2
|
||||
@1 went up in flames=@1 a marché dans les flammes
|
||||
@1 walked into fire whilst fighting @2=@1 a marché dans les flammes en combattant @2
|
||||
@1 was struck by lightning=@1 a été frappé par la foudre
|
||||
@1 was struck by lightning whilst fighting @2=@1 a été frappé par la foudre en combattant @2
|
||||
@1 burned to death=@1 a brûlé vif
|
||||
@1 was burnt to a crisp whilst fighting @2=@1 a brûlé comme une saucisse en combattant @2
|
||||
@1 tried to swim in lava=@1 a tenté de nager dans la lave
|
||||
@1 tried to swim in lava to escape @2=@1 a tenté de nager dans la lave pour échapper à @2
|
||||
@1 discovered the floor was lava=@1 a découvert que le sol était en lave
|
||||
@1 walked into danger zone due to @2=@1 a marché dans la zone de danger à cause de @2
|
||||
@1 suffocated in a wall=@1 est mort asphyxié dans un mur
|
||||
@1 suffocated in a wall whilst fighting @2=@1 est mort asphyxié dans un mur en combattant @2
|
||||
@1 drowned=@1 s'est noyé
|
||||
@1 drowned whilst trying to escape @2=@1 s'est noyé en essayant d'échapper à @2
|
||||
@1 starved to death=@1 est mort de faim
|
||||
@1 starved to death whilst fighting @2=@1 est mort de faim en combattant @2
|
||||
@1 was pricked to death=@1 a été piqué à mort
|
||||
@1 walked into a cactus whilst trying to escape @2=@1 a marché dans un cactus en essayant d'échapper à @2
|
||||
@1 hit the ground too hard=@1 a heurté le sol trop fort
|
||||
@1 hit the ground too hard whilst trying to escape @2=@1 a heurté le sol trop fort en essayant d'échapper à @2
|
||||
@1 experienced kinetic energy=@1 a expérimenté l'énergie cinétique
|
||||
@1 experienced kinetic energy whilst trying to escape @2=@1 a expérimenté l'énergie cinétique en essayant d'échapper à @2
|
||||
@1 fell out of the world=@1 est tombé hors du monde
|
||||
@1 didn't want to live in the same world as @2=@1 ne voulait vivre dans le même monde que @2
|
||||
@1 died=@1 est mort
|
||||
@1 died because of @2=@1 est mort à cause de @2
|
||||
@1 was killed by magic=@1 a été tué par magie
|
||||
@1 was killed by magic whilst trying to escape @2=@1 a été tué par magie en essayant d'échapper à @2
|
||||
@1 was killed by @2 using magic=@1 a été tué par @2 en utilisant la magie
|
||||
@1 was killed by @2 using @3=@1 a été tué par @2 en utilisant @3
|
||||
@1 was roasted in dragon breath=@1 a été rôti dans le souffle du dragon
|
||||
@1 was roasted in dragon breath by @2=@1 a été rôti dans le souffle du dragon par @2
|
||||
@1 withered away=@1 s'est flétri
|
||||
@1 withered away whilst fighting @2=@1 s'est flétri en combattant @2
|
||||
@1 was shot by a skull from @2=@1 a été abattu par un crane de @2
|
||||
@1 was squashed by a falling anvil=@1 a été écrasé par une enclume
|
||||
@1 was squashed by a falling anvil whilst fighting @2=@1 a été écrasé par une enclume en combattant @2
|
||||
@1 was squashed by a falling block=@1 a été écrasé par un bloc tombant
|
||||
@1 was squashed by a falling block whilst fighting @2=@1 a été écrasé par un bloc tombant en combattant @2
|
||||
@1 was slain by @2=@1 a été tué par @2
|
||||
@1 was slain by @2 using @3=@1 a été tué par @2 avec @3
|
||||
@1 was shot by @2=@1 a été abattu par @2
|
||||
@1 was shot by @2 using @3=@1 a été abattu par @2 avec @3
|
||||
@1 was fireballed by @2=@1 a reçu une boule de feu de @2
|
||||
@1 was fireballed by @2 using @3=@1 a reçu une boule de feu de @2 en utilisant @3
|
||||
@1 was killed trying to hurt @2=@1 a été tué en essayant de blesser @2
|
||||
@1 was killed by @3 trying to hurt @2=@1 a été tué par @3 en essayant de blesser @2
|
||||
@1 blew up=@1 a explosé
|
||||
@1 was blown up by @2=@1 a été explosé par @2
|
||||
@1 was blown up by @2 using @3=@1 a été explosé par @2 en utilisant @3
|
||||
@1 was squished too much=@1 a été trop écrabouillé
|
||||
@1 was squashed by @2=@1 a été écrasé par @2
|
||||
@1 went off with a bang=@1 est parti avec un bang
|
||||
@1 went off with a bang due to a firework fired from @3 by @2=@1 est parti avec un bang à cause d'un feu d'artifice tiré de @3 par @2
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
@1 was roasted in dragon breath by @2=
|
||||
@1 withered away=
|
||||
@1 withered away whilst fighting @2=
|
||||
@1 was killed by magic=
|
||||
@1 was shot by a skull from @2=
|
||||
@1 was squashed by a falling anvil=
|
||||
@1 was squashed by a falling anvil whilst fighting @2=
|
||||
|
@ -41,8 +40,6 @@
|
|||
@1 was squashed by a falling block whilst fighting @2=
|
||||
@1 was slain by @2=
|
||||
@1 was slain by @2 using @3=
|
||||
@1 was slain by @2=
|
||||
@1 was slain by @2 using @3=
|
||||
@1 was shot by @2=
|
||||
@1 was shot by @2 using @3=
|
||||
@1 was fireballed by @2=
|
||||
|
|
|
@ -5,3 +5,4 @@ Error: Too many parameters!=Erreur: Trop de paramètres!
|
|||
Error: Incorrect value of XP=Erreur: Valeur incorrecte de XP
|
||||
Error: Player not found=Erreur: Joueur introuvable
|
||||
Added @1 XP to @2, total: @3, experience level: @4=Ajout de @1 XP à @2, total: @3, niveau d'expérience: @4
|
||||
Bottle o' Enchanting=Fiole d'expérience
|
|
@ -0,0 +1,146 @@
|
|||
local refresh_interval = .63
|
||||
local huds = {}
|
||||
local default_debug = 5
|
||||
local after = minetest.after
|
||||
local get_connected_players = minetest.get_connected_players
|
||||
local get_biome_name = minetest.get_biome_name
|
||||
local get_biome_data = minetest.get_biome_data
|
||||
local get_node = minetest.get_node
|
||||
local format = string.format
|
||||
local table_concat = table.concat
|
||||
local floor = math.floor
|
||||
local minetest_get_gametime = minetest.get_gametime
|
||||
local get_voxel_manip = minetest.get_voxel_manip
|
||||
|
||||
local min1, min2, min3
|
||||
local max1, max2, max3
|
||||
local CS
|
||||
|
||||
local get_local_settings = function()
|
||||
min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
||||
max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
||||
CS = mcl_mapgen.CS_NODES
|
||||
end
|
||||
get_local_settings()
|
||||
mcl_mapgen.register_on_settings_changed(get_local_settings)
|
||||
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
local S = minetest.get_translator(modname)
|
||||
local storage = minetest.get_mod_storage()
|
||||
local player_dbg = minetest.deserialize(storage:get_string("player_dbg") or "return {}") or {}
|
||||
|
||||
local function get_text(pos, bits)
|
||||
local pos = pos
|
||||
local bits = bits
|
||||
if bits == 0 then return "" end
|
||||
local y = pos.y
|
||||
if y >= min1 then
|
||||
y = y - min1
|
||||
elseif y >= min3 and y <= max3 then
|
||||
y = y - min3
|
||||
elseif y >= min2 and y <= max2 then
|
||||
y = y - min2
|
||||
end
|
||||
|
||||
local will_show_mapgen_status = bits % 8 > 3
|
||||
local will_show_coordinates = bits % 4 > 1
|
||||
local will_show_biome_name = bits % 2 > 0
|
||||
local will_be_shown = {}
|
||||
|
||||
if will_show_biome_name then
|
||||
local biome_data = get_biome_data(pos)
|
||||
local biome_name = biome_data and get_biome_name(biome_data.biome) or "No biome"
|
||||
will_be_shown[#will_be_shown + 1] = biome_name
|
||||
end
|
||||
if will_show_coordinates then
|
||||
local coordinates = format("x:%.1f y:%.1f z:%.1f", pos.x, y, pos.z)
|
||||
will_be_shown[#will_be_shown + 1] = coordinates
|
||||
end
|
||||
if will_show_mapgen_status then
|
||||
local pos_x = floor(pos.x)
|
||||
local pos_y = floor(pos.y)
|
||||
local pos_z = floor(pos.z)
|
||||
local c = 0
|
||||
for x = pos_x - CS, pos_x + CS, CS do
|
||||
for y = pos_y - CS, pos_y + CS, CS do
|
||||
for z = pos_z - CS, pos_z + CS, CS do
|
||||
local pos = {x = x, y = y, z = z}
|
||||
get_voxel_manip():read_from_map(pos, pos)
|
||||
local node = get_node(pos)
|
||||
if node.name ~= "ignore" then c = c + 1 end
|
||||
end
|
||||
end
|
||||
end
|
||||
local p = floor(c / 27 * 100 + 0.5)
|
||||
local status = format("Generated %u%% (%u/27 chunks)", p, c)
|
||||
will_be_shown[#will_be_shown + 1] = status
|
||||
end
|
||||
|
||||
local text = table_concat(will_be_shown, ' ')
|
||||
return text
|
||||
end
|
||||
|
||||
local function info()
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local pos = player:get_pos()
|
||||
local text = get_text(pos, player_dbg[name] or default_debug)
|
||||
local hud = huds[name]
|
||||
if not hud then
|
||||
local def = {
|
||||
hud_elem_type = "text",
|
||||
alignment = {x = 1, y = -1},
|
||||
scale = {x = 100, y = 100},
|
||||
position = {x = 0.0073, y = 0.989},
|
||||
text = text,
|
||||
style = 5,
|
||||
["number"] = 0xcccac0,
|
||||
z_index = 0,
|
||||
}
|
||||
local def_bg = table.copy(def)
|
||||
def_bg.offset = {x = 2, y = 1}
|
||||
def_bg["number"] = 0
|
||||
def_bg.z_index = -1
|
||||
huds[name] = {
|
||||
player:hud_add(def),
|
||||
player:hud_add(def_bg),
|
||||
text,
|
||||
}
|
||||
elseif text ~= hud[3] then
|
||||
hud[3] = text
|
||||
player:hud_change(huds[name][1], "text", text)
|
||||
player:hud_change(huds[name][2], "text", text)
|
||||
end
|
||||
end
|
||||
after(refresh_interval, info)
|
||||
end
|
||||
|
||||
minetest.register_on_authplayer(function(name, ip, is_success)
|
||||
if is_success then
|
||||
huds[name] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("debug",{
|
||||
description = S("Set debug bit mask: 0 = disable, 1 = biome name, 2 = coordinates, 4 = mapgen status, 7 = all"),
|
||||
func = function(name, params)
|
||||
local dbg = math.floor(tonumber(params) or default_debug)
|
||||
if dbg < 0 or dbg > 7 then
|
||||
minetest.chat_send_player(name, S("Error! Possible values are integer numbers from @1 to @2", 0, 7))
|
||||
return
|
||||
end
|
||||
if dbg == default_debug then
|
||||
player_dbg[name] = nil
|
||||
else
|
||||
player_dbg[name] = dbg
|
||||
end
|
||||
minetest.chat_send_player(name, S("Debug bit mask set to @1", dbg))
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
storage:set_string("player_dbg", minetest.serialize(player_dbg))
|
||||
end)
|
||||
|
||||
info()
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mcl_info
|
||||
Set debug bit mask: 0 @= disable, 1 @= biome name, 2 @= coordinates, 4 @= mapgen status, 7 @= all=Réglage du masque de débugage : 0 @= désactiver, 1 @= nom de biome, 2 @= coordonnées, 4 @= mapgen status, 7 @= tout=
|
||||
Error! Possible values are integer numbers from @1 to @2=Erreur ! Les valeurs autorisées sont des nombres entiers de @1 à @2
|
||||
Debug bit mask set to @1=Masque de débugage réglé à @1
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mcl_info
|
||||
Set debug bit mask: 0 @= disable, 1 @= biome name, 2 @= coordinates, 4 @= mapgen status, 7 @= all=Установка отладочной битовой маски: 0 @= отключить, 1 @= биом, 2 @= координаты, 4 @= состояние мапгена, 7 @= всё
|
||||
Error! Possible values are integer numbers from @1 to @2=Ошибка! Допустимые значения - целые числа от @1 до @2
|
||||
Debug bit mask set to @1=Отладочной битовой маске присвоено значение @1
|
|
@ -0,0 +1,4 @@
|
|||
# textdomain: mcl_info
|
||||
Set debug bit mask: 0 @= disable, 1 @= biome name, 2 @= coordinates, 4 @= mapgen status, 7 @= all=
|
||||
Error! Possible values are integer numbers from @1 to @2=
|
||||
Debug bit mask set to @1=
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_info
|
||||
description = Prints biome name and player position
|
||||
optional_depends = mcl_mapgen
|
|
@ -34,7 +34,7 @@ minetest.register_on_mods_loaded(function()
|
|||
for name,def in pairs(minetest.registered_items) do
|
||||
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and def.description ~= "" then
|
||||
local function is_redstone(def)
|
||||
return def.mesecons or def.groups.mesecon or def.groups.mesecon_conductor_craftable or def.groups.mesecon_effecor_off
|
||||
return def.mesecons or def.groups.mesecon or def.groups.mesecon_conductor_craftable or def.groups.mesecon_effector_off
|
||||
end
|
||||
local function is_tool(def)
|
||||
return def.groups.tool or (def.tool_capabilities and def.tool_capabilities.damage_groups == nil)
|
||||
|
@ -339,14 +339,6 @@ function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size,
|
|||
if name == "inv" then
|
||||
inv_bg = "crafting_inventory_creative_survival.png"
|
||||
|
||||
-- Show armor and player image
|
||||
local player_preview
|
||||
if minetest.settings:get_bool("3d_player_preview", true) then
|
||||
player_preview = mcl_player.get_player_formspec_model(player, 3.9, 1.4, 1.2333, 2.4666, "")
|
||||
else
|
||||
player_preview = "image[3.9,1.4;1.2333,2.4666;"..mcl_player.player_get_preview(player).."]"
|
||||
end
|
||||
|
||||
-- Background images for armor slots (hide if occupied)
|
||||
local armor_slot_imgs = ""
|
||||
local inv = player:get_inventory()
|
||||
|
@ -386,8 +378,7 @@ function mcl_inventory.set_creative_formspec(player, start_i, pagenum, inv_size,
|
|||
|
||||
armor_slot_imgs..
|
||||
-- player preview
|
||||
player_preview..
|
||||
|
||||
mcl_player.get_player_formspec_model(player, 3.9, 1.4, 1.2333, 2.4666, "")..
|
||||
-- crafting guide button
|
||||
"image_button[9,1;1,1;craftguide_book.png;__mcl_craftguide;]"..
|
||||
"tooltip[__mcl_craftguide;"..F(S("Recipe book")).."]"..
|
||||
|
|
|
@ -61,14 +61,6 @@ local function set_inventory(player, armor_change_only)
|
|||
inv:set_width("craft", 2)
|
||||
inv:set_size("craft", 4)
|
||||
|
||||
-- Show armor and player image
|
||||
local player_preview
|
||||
if minetest.settings:get_bool("3d_player_preview", true) then
|
||||
player_preview = mcl_player.get_player_formspec_model(player, 1.0, 0.0, 2.25, 4.5, "")
|
||||
else
|
||||
player_preview = "image[1.1,0.2;2,4;"..mcl_player.player_get_preview(player).."]"
|
||||
end
|
||||
|
||||
local armor_slots = {"helmet", "chestplate", "leggings", "boots"}
|
||||
local armor_slot_imgs = ""
|
||||
for a=1,4 do
|
||||
|
@ -83,7 +75,7 @@ local function set_inventory(player, armor_change_only)
|
|||
|
||||
local form = "size[9,8.75]"..
|
||||
"background[-0.19,-0.25;9.41,9.49;crafting_formspec_bg.png]"..
|
||||
player_preview..
|
||||
mcl_player.get_player_formspec_model(player, 1.0, 0.0, 2.25, 4.5, "")..
|
||||
--armor
|
||||
"list[current_player;armor;0,0;1,1;1]"..
|
||||
"list[current_player;armor;0,1;1,1;2]"..
|
||||
|
|