Compare commits

...

81 Commits

Author SHA1 Message Date
SOS-Games 4f37c1600f Able to see all mobs in the lookup help menu (#4186)
added a Mobs category that has pages for all mobs

Co-authored-by: SOS-Games <101518564+SOS-Games@users.noreply.github.com>
Reviewed-on: MineClone2/MineClone2#4186
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: SOS-Games <gruberscomplete@gmail.com>
Co-committed-by: SOS-Games <gruberscomplete@gmail.com>
2024-04-21 08:16:15 +00:00
Doods bd4337a2dc Make Editorconfig compliant with the spec. (#4245)
Reviewed-on: MineClone2/MineClone2#4245
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Doods <yusufalishabaka@tutanota.com>
Co-committed-by: Doods <yusufalishabaka@tutanota.com>
2024-04-20 15:23:59 +00:00
the-real-herowl 0f20e18e53 Merge pull request 'fix_xp_reload_bug' (#4226) from teknomunk/MineClone2:fix_xp_reload_bug into master
Reviewed-on: MineClone2/MineClone2#4226
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-04-14 07:10:33 +00:00
the-real-herowl 69d3fa5f85 Merge pull request 'Added a check for the bone meal's applied position.' (#4201) from CyberMango/MineClone2:dev/mango/add_bone_meal_protection_check into master
Reviewed-on: MineClone2/MineClone2#4201
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-04-14 07:07:21 +00:00
the-real-herowl 5e673b8fee Merge pull request 'Add partial item stack pickup' (#4193) from teknomunk/MineClone2:grouped-item-pickup into master
Reviewed-on: MineClone2/MineClone2#4193
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-04-14 06:53:37 +00:00
Araca 596c56d31f Add possibility to edit a sign (#4188)
We can edit a sign by right-clicking in it. If in hand you have a color or a glow ink, the behaviour didn't change. Also some refactorization.

Reviewed-on: MineClone2/MineClone2#4188
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Araca <araca.prod@gmail.com>
Co-committed-by: Araca <araca.prod@gmail.com>
2024-04-14 06:50:19 +00:00
the-real-herowl b5b8d4f336 Merge pull request 'more piglin bartering items (and drop stack instead of single items)' (#4181) from nixnoxus/MineClone2:piglin-bartering into master
Reviewed-on: MineClone2/MineClone2#4181
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-04-14 00:01:13 +00:00
Doods c1971b662a Fireproof certain wood nodes & add planks crafting recipes (#4166)
* Make Crimson and Warped Planks, Double Slab Planks, Fences, Fence Gates, Doors, Trapdoors, Pressure Plates, Wood, Bark, Stripped Wood, Stripped Bark, and Stairs Immune to fire by removing them from the 'flammable', 'fire_encouragement', and 'fire_flammability' groups.
* Add crafting recipes which allow Cherry, Mangrove, Crimson, and Warped planks to be crafted from Wood, Bark, Stripped Wood, and Stripped Bark variants, to make them consistent with with all other planks.

Reviewed-on: MineClone2/MineClone2#4166
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Doods <yusufalishabaka@tutanota.com>
Co-committed-by: Doods <yusufalishabaka@tutanota.com>
2024-04-13 23:52:46 +00:00
nixnoxus a86e8e2c8e fix strider breeding & increase riding speed (#4096)
- Striders follow `mcl_crimson:warped_fungus`
- Striders can be bred with `mcl_crimson:warped_fungus`
- Riding speed increased to 2 m/s
- Warning fixed

Reviewed-on: MineClone2/MineClone2#4096
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: nixnoxus <nixnoxus@web.de>
Co-committed-by: nixnoxus <nixnoxus@web.de>
2024-04-13 23:38:46 +00:00
Doods a3db7bd504 Fix texture converter for sandstone (#4233)
Reviewed-on: MineClone2/MineClone2#4233
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: Doods <yusufalishabaka@tutanota.com>
Co-committed-by: Doods <yusufalishabaka@tutanota.com>
2024-04-08 19:02:43 +00:00
ancientmarinerdev ac4aff12ea Enable custom biome check in spawning. This allows mods to overide the check in singlenode and other mapgens that do not support biome API. (#4217)
Some older mapgens do not support the MT biome API. Singlenode and I think v6 also. This allows mods to overide the biomecheck in mob spawning to return valid biomenames and customise this.

Reviewed-on: MineClone2/MineClone2#4217
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: ancientmarinerdev <ancientmariner_dev@proton.me>
Co-committed-by: ancientmarinerdev <ancientmariner_dev@proton.me>
2024-04-01 15:40:14 +00:00
emptyshore 003288bc80 Update comments in the nether portals script (#4227)
Co-authored-by: Mateusz Uzdowski <mateusz@silverstripe.com>
Reviewed-on: MineClone2/MineClone2#4227
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: emptyshore <emptyshore@noreply.git.minetest.land>
Co-committed-by: emptyshore <emptyshore@noreply.git.minetest.land>
2024-04-01 00:30:06 +00:00
teknomunk 13ce4f9092 Additional cleanup, impelemnt partial item stack pickup 2024-03-31 02:32:24 +00:00
teknomunk 0a294c55a1 Move object pickup code to try_object_pickup(...) and refactor to remove most indentation for readability 2024-03-31 02:32:24 +00:00
teknomunk 44bb07507d Cleanup comment, whitespace for readability 2024-03-31 02:28:46 +00:00
cora 3509b85a3e Fix possible crash due to engine bug reloading XP orbs 2024-03-31 02:28:46 +00:00
emptyshore 55653fe37b Nether portals rewrite (#4128)
See MineClone2/MineClone2#4120

Co-authored-by: Mateusz Uzdowski <mateusz@silverstripe.com>
Reviewed-on: MineClone2/MineClone2#4128
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: emptyshore <emptyshore@noreply.git.minetest.land>
Co-committed-by: emptyshore <emptyshore@noreply.git.minetest.land>
2024-03-31 02:20:23 +00:00
the-real-herowl 0170619866 Merge pull request 'Imported the tga_encoder as subtree' (#4222) from tga_encoder_update into master
Reviewed-on: MineClone2/MineClone2#4222
2024-03-27 00:31:39 +00:00
ancientmarinerdev 0b521ef92b Fix - New game load crashes. Add defensive check. (#4216)
Reviewed-on: MineClone2/MineClone2#4216
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: ancientmarinerdev <ancientmariner_dev@proton.me>
Co-committed-by: ancientmarinerdev <ancientmariner_dev@proton.me>
2024-03-27 00:22:22 +00:00
nixnoxus 8931576c50 fix 'WARNING[Main]: Undeclared global variable ' in mcl_colorblocks (#4203)
fix WARNING: Undeclared global variable

Reviewed-on: MineClone2/MineClone2#4203
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: nixnoxus <nixnoxus@web.de>
Co-committed-by: nixnoxus <nixnoxus@web.de>
2024-03-27 00:20:12 +00:00
the-real-herowl c65176af81 Merge pull request 'Added clovers' (#4224) from clovers into master
Reviewed-on: MineClone2/MineClone2#4224
2024-03-27 00:15:32 +00:00
the-real-herowl a71a634168 Merge pull request 'Make end rods use a mesh model' (#4177) from end_rod_model into master
Reviewed-on: MineClone2/MineClone2#4177
2024-03-27 00:14:13 +00:00
the-real-herowl 89e8cf0213 Merge branch 'skeleton_sounds' 2024-03-24 06:38:35 +01:00
nixnoxus 08b41a3b39 accelerate non-moving carts on `mcl_minecarts:golden_rail_on` (#4097)
accelerate a non-moving minecart away from a solid opaque block the powered rail is facing

Reviewed-on: MineClone2/MineClone2#4097
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: nixnoxus <nixnoxus@web.de>
Co-committed-by: nixnoxus <nixnoxus@web.de>
2024-03-24 05:29:44 +00:00
the-real-herowl 26fc0cd4d8 Merge pull request 'update melon and pumpkin biomes' (#4037) from michaljmalinowski/MineClone2:master into master
Reviewed-on: MineClone2/MineClone2#4037
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-03-24 05:17:20 +00:00
𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 e50ad7d6de Merge pull request 'Fix a crash with rocket explosion' (#4178) from fix_rocket_crash into master
Reviewed-on: MineClone2/MineClone2#4178
Reviewed-by: 𝕵𝖔𝖍𝖆𝖓𝖓𝖊𝖘 𝕱𝖗𝖎𝖙𝖟 <mrrar@noreply.git.minetest.land>
2024-03-21 00:15:50 +00:00
the-real-herowl 4b6d63739c Fix a crash with rocket explosion
Defensive check
2024-03-20 19:13:44 -05:00
the-real-herowl c6a112d200 Clovers now generate
Also added a way to alter flower rarity
2024-03-20 22:29:03 +01:00
the-real-herowl ade2a1a6a2 Add clovers and four-leaf clovers 2024-03-20 22:18:47 +01:00
the-real-herowl b04493dcb6 Add 'mods/CORE/tga_encoder/' from commit 'aad231f5e406a7c1eaafe6595a64b9c55f230692'
git-subtree-dir: mods/CORE/tga_encoder
git-subtree-mainline: a5740f8edf
git-subtree-split: aad231f5e4
2024-03-19 19:28:16 +01:00
the-real-herowl a5740f8edf Remove old tga_encoder 2024-03-19 19:27:12 +01:00
CyberMango 4a22287eb6 Added a check for the bone meal's applied position.
protection the position above as well to prevent growing high grass on
areas in which you are not allowed to place blocks. Because, if you cant
place a block there, why should you be able to grow grass/flowers there?
2024-02-29 13:36:02 +02:00
nixnoxus 497f1dcd80 drop stack instead of items 2024-02-05 00:24:27 +01:00
nixnoxus 01cace413f update bartering items 2024-02-05 00:11:22 +01:00
the-real-herowl 1476c7ae59 Make end rods use a mesh model
* merged the 3 textures of the end rod into one
* made a proper image mask for the colored end rods
* (used to be hardcoded & didn't work for higher-res texture packs)
2024-02-02 02:31:57 +01:00
bakawun 49aa32d2ee mobs:skeletons:sounds: fix code review issue and add todo 2024-01-08 09:16:05 +01:00
bakawun a786b66d9e mobs:skeletons: add sounds to skeleton
and stray, fix random sound for wither skeleton
2023-12-18 11:17:25 +01:00
michaljmalinowski 9be5de980e Update mods/MAPGEN/mcl_biomes/init.lua
update melon and pumpkin biomes
2023-11-28 20:26:24 +00:00
Nils Dagsson Moskopp aad231f5e4
Generate test textures with scanline order “top-bottom” 2023-10-16 21:40:51 +02:00
Nils Dagsson Moskopp 10022c93ef
Explicitly state scanline order in test texture script 2023-10-16 21:38:19 +02:00
Nils Dagsson Moskopp 01bcbbf927
Allow encoding with top-bottom scanline order 2023-10-16 21:15:40 +02:00
Nils Dagsson Moskopp 12a33e458f
Add hashbang to script to generate TGA test textures 2023-10-16 20:34:47 +02:00
Nils Dagsson Moskopp 5dcf714cd1
Partially slice 3D rendered donut along its polodial direction 2023-10-16 19:20:30 +02:00
Nils Dagsson Moskopp 0c6d9a4d85
Partially slice 3D rendered donut along its torodial direction 2023-10-16 19:16:29 +02:00
Nils Dagsson Moskopp ef65de54c0
Make 3D rendered donut appear solid 2023-10-16 19:05:56 +02:00
Nils Dagsson Moskopp 4d57ba62bf
Add code to generate TGA type 1 test textures 2023-10-15 20:49:11 +02:00
Nils Dagsson Moskopp 4064333b06
Adjust TGA test texture file names 2023-10-15 20:11:28 +02:00
Nils Dagsson Moskopp e87a8fc332
Add script to generate TGA test textures 2023-10-15 17:02:19 +02:00
Nils Dagsson Moskopp bb68ceb38d
Do not encode images with illegal colormap indexes 2023-09-22 20:06:21 +02:00
Nils Dagsson Moskopp 4dd3833f3b
Move encoding format heuristics from image:save() to image:encode() 2023-09-18 16:45:31 +02:00
Nils Dagsson Moskopp 3029147ed7
Add example code for rendering a 3D shape using a Z-buffer 2023-09-18 15:21:17 +02:00
Nils Dagsson Moskopp 5bf0e79c6f
Add example code to generate TGA logo
The tiny logo is a 12×12 TGA image.
The huge logo is a 1200×1200 TGA image.
2023-08-27 15:12:47 +02:00
Nils Dagsson Moskopp e9862f5f24
Add example code for node colormap generation 2022-07-31 00:24:34 +02:00
Nils Dagsson Moskopp 685bdcb379
Allow encoding with B8G8R8A8 colormap 2022-05-19 18:26:41 +02:00
Nils Dagsson Moskopp 7446a275b5
Remove author from mod.conf
ContentDB sets this field automatically
2022-05-16 22:18:01 +02:00
Nils Dagsson Moskopp 83348bf3ac
Document use cases, image type support, plans 2022-05-16 20:34:33 +02:00
Nils Dagsson Moskopp 45e405982a
Allow encoding with A1R5G5B5 colormap 2022-05-16 18:48:18 +02:00
Nils Dagsson Moskopp e83894fcfa
Treat empty colormap as no colormap 2022-05-16 17:56:53 +02:00
Nils Dagsson Moskopp 9f9b78eed9
Allow color-mapped encoding for RGB images 2022-05-16 17:28:16 +02:00
Nils Dagsson Moskopp ed061e68ff
Allow RLE encoding for RGBA images 2022-05-16 13:16:23 +02:00
Nils Dagsson Moskopp 1f9c446a98
Assert that pixel data is encoded 2022-05-16 03:21:52 +02:00
Nils Dagsson Moskopp 7b94fc026d
Allow to specify color format 2022-05-16 02:48:51 +02:00
Nils Dagsson Moskopp fa23775bf9
Measure pixel_depth in bits everywhere 2022-05-16 01:54:21 +02:00
Nils Dagsson Moskopp 36ee45ebe3
Add RGBA support (only RAW encoding) 2022-05-16 01:39:33 +02:00
Nils Dagsson Moskopp 2112637faf
Use HSP for RGB-to-BW8 conversion 2022-05-15 19:58:19 +02:00
Nils Dagsson Moskopp 5b79bc6fb7
Set default encoding to R8G8B8 RAW
This the most trivial RGB encoding the encoder supports. Setting
it prevents a crash related to BW8 only supporting RAW encoding.
2022-05-15 19:31:24 +02:00
Nils Dagsson Moskopp 759b0a188f
Allow saving R8G8B8 images as BW8 2022-05-15 19:23:26 +02:00
Nils Dagsson Moskopp 376b6404b2
Allow RAW or RLE encoding for true-color images 2022-05-15 15:56:18 +02:00
Nils Dagsson Moskopp 9bd1702d60
Encode image only when saving it to a file 2022-05-15 14:58:12 +02:00
Nils Dagsson Moskopp 5640e19c94
Add fractal example 2022-05-15 03:10:53 +02:00
Nils Dagsson Moskopp 25b7b30945
Allow encoding grayscale images as BW8 2022-05-14 22:41:52 +02:00
Nils Dagsson Moskopp 9af0719df8
Allow encoding RGB images as B8G8R8 2022-05-14 21:56:42 +02:00
Nils Dagsson Moskopp 1b48c3f539
Use raw packets in RLE compression in tga_encoder 2022-05-14 21:52:08 +02:00
Nils Dagsson Moskopp 13552f16f2
Reduce TGA filesize by 25% by using 16-bit colors 2022-05-14 21:52:07 +02:00
Nils Dagsson Moskopp 4f6ea5d035
Add credits to README 2022-05-14 21:52:07 +02:00
Nils Dagsson Moskopp c00b0d50c6
Add usage examples 2022-05-14 21:52:00 +02:00
Nils Dagsson Moskopp adb8e45d67
Fix TGA file writing on Windows
Before this patch, the tga_encoder mod would write corrupted TGA files
on Windows: Bytes that looked like newlines were replaced by a carriage
return and a newline.
2022-05-14 20:21:59 +02:00
Nils Dagsson Moskopp 3f60b5baa6
Speed up TGA encoding by creating fewer strings 2022-05-14 20:21:58 +02:00
Nils Dagsson Moskopp 52e5c955f0
Use RLE compression in tga_encoder 2022-05-14 20:21:58 +02:00
Lizzy Fleckenstein b88579b52e
Add updated tga_encoder 2022-05-14 20:21:58 +02:00
Lizzy Fleckenstein eaa4dc99d7
Initial import 2022-05-14 20:21:52 +02:00
60 changed files with 3550 additions and 603 deletions

View File

@ -4,7 +4,7 @@ root = true
end_of_line = lf end_of_line = lf
[*.lua] [*.lua]
charset = utf8 charset = utf-8
indent_style = tab indent_style = tab
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true

View File

@ -36,6 +36,8 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000) mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000) mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
-- Central chunk is offset from 0,0,0 coordinates by 32 nodes (2 blocks)
-- See more in https://git.minetest.land/MineClone2/MineClone2/wiki/World-structure%3A-positions%2C-boundaries%2C-blocks%2C-chunks%2C-dimensions%2C-barriers-and-the-void
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2) local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE

View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -1,4 +1,87 @@
# tga_encoder # tga_encoder
A TGA Encoder written in Lua without the use of external Libraries. 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. 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`.

View File

@ -0,0 +1,32 @@
dofile("init.lua")
-- This generates images necessary to colorize 16 Minetest nodes in 4096 colors.
-- It serves as a demonstration of what you can achieve using colormapped nodes.
-- It is be useful for grass or beam or glass nodes that need to blend smoothly.
-- 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, 4) - 1
local max_sample_out = math.pow(2, 8) - 1
for r = 0,15 do
local pixels = {}
for g = 0,15 do
if nil == pixels[g + 1] then
pixels[g + 1] = {}
end
for b = 0,15 do
local color = {
math.floor((r * max_sample_out / max_sample_in) + 0.5),
math.floor((g * max_sample_out / max_sample_in) + 0.5),
math.floor((b * max_sample_out / max_sample_in) + 0.5),
}
pixels[g + 1][b + 1] = color
end
end
local filename = "colormap_" .. tostring(r) .. ".tga"
tga_encoder.image(pixels):save(
filename,
{ color_format="A1R5G5B5" } -- waste less bits
)
end

View File

@ -0,0 +1,105 @@
#!/usr/bin/env lua5.1
-- -*- coding: utf-8 -*-
-- 3D “donut” shape rendering using floating-point math
-- see <https://www.a1k0n.net/2011/07/20/donut-math.html>
-- cargo-culted by erle 2023-09-18
local theta_spacing = 0.01 -- 0.07
local phi_spacing = 0.002 -- 0.02
local R1 = 1
local R2 = 2
local K2 = 5
local screen_height = 256
local screen_width = 256
local K1 = screen_width * K2 * 3 / ( 8 * ( R1 + R2 ) )
local output = {}
local zbuffer = {}
local grey = { 120, 120, 120 }
local gray = { 136, 136, 136 }
for y = 1,screen_height,1 do
output[y] = {}
zbuffer[y] = {}
for x = 1,screen_width,1 do
local hori = math.floor( ( (y - 1) / 32 ) % 2 )
local vert = math.floor( ( (x - 1) / 32 ) % 2 )
output[y][x] = hori ~= vert and grey or gray
zbuffer[y][x] = 0
end
end
function render_frame(A, B)
-- precompute sines and cosines of A and B
local cosA = math.cos(A)
local sinA = math.sin(A)
local cosB = math.cos(B)
local sinB = math.sin(B)
-- theta goas around the cross-sectional circle of a torus
local theta = 0
while theta <= 2*math.pi do
if ( theta < 2*math.pi * 1/8 ) or ( theta > 2*math.pi * 7/8 ) then
theta = theta + (theta_spacing * 16)
else
theta = theta + theta_spacing
end
-- precompute sines and cosines of theta
local costheta = math.cos(theta)
local sintheta = math.sin(theta)
-- phi goes around the center of revolution of a torus
local phi = 0
while phi <= 2*math.pi do
if ( phi > 2*math.pi * 3/8 ) and ( phi < 2*math.pi * 5/8 ) then
phi = phi + (phi_spacing * 128)
else
phi = phi + phi_spacing
end
-- precompute sines and cosines of phi
local cosphi = math.cos(phi)
local sinphi = math.sin(phi)
-- 2D (x, y) coordinates of the circle, before revolving
local circlex = R2 + R1*costheta
local circley = R1*sintheta
-- 3D (x, y, z) coordinates after rotation
local x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB
local y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB
local z = K2 + cosA*circlex*sinphi + circley*sinA
local ooz = 1/z
-- x and y projection
local xp = math.floor(screen_width/2 + K1*ooz*x)
local yp = math.floor(screen_height/2 + K1*ooz*y)
-- calculate luminance
local L = cosphi*costheta*sinB - cosA*costheta*sinphi - sinA*sintheta + cosB*( cosA*sintheta - costheta*sinA*sinphi )
-- if (L > 0) then
if (true) then
if (ooz > zbuffer[yp][xp]) then
zbuffer[yp][xp] = ooz
local luminance = math.max( math.ceil( L * 180 ), 0 )
-- luminance is now in the range 0 to 255
r = math.ceil( (luminance + xp) / 2 )
g = math.ceil( (luminance + yp) / 2 )
b = math.ceil( (luminance + xp + yp) / 3 )
output[yp][xp] = { r, g, b }
end
end
end
end
end
dofile('init.lua')
render_frame(-0.7, 0.7)
tga_encoder.image(output):save("donut.tga")

View File

@ -0,0 +1,181 @@
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")
-- test that image can be encoded
local bitmap_small_0 = tga_encoder.image(pixels)
bitmap_small_0:encode()
assert(191 == #bitmap_small_0.data)
-- test that imbage can be encoded with parameters
local bitmap_small_1 = tga_encoder.image(pixels)
bitmap_small_1:encode(
{
colormap = {},
color_format = "B8G8R8",
compression = "RAW",
}
)
assert(191 == #bitmap_small_1.data)
-- 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})
-- encoding a colormapped image with illegal colormap indexes should error out
local colormap = {
{ 0, 0, 0, 0 },
{ 0, 0, 0, 255 },
}
local status, message = pcall(
function ()
tga_encoder.image(pixels):encode({colormap=colormap})
end
)
assert(
false == status and
"init.lua:36: colormap index 2 not in colormap of size 2" == message
)

View File

@ -9,60 +9,566 @@ local image = setmetatable({}, {
}) })
function image:constructor(pixels) function image:constructor(pixels)
self.data = ""
self.pixels = pixels self.pixels = pixels
self.width = #pixels[1] self.width = #pixels[1]
self.height = #pixels self.height = #pixels
self:encode()
end end
function image:encode_colormap_spec() local pixel_depth_by_color_format = {
self.data = self.data ["Y8"] = 8,
.. string.char(0, 0) -- first entry index ["A1R5G5B5"] = 16,
.. string.char(0, 0) -- number of entries ["B8G8R8"] = 24,
.. string.char(0) -- bits per pixel ["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
]
-- ensure that each pixel references a legal colormap entry
for _, row in ipairs(self.pixels) do
for _, pixel in ipairs(row) do
local colormap_index = pixel[1]
if colormap_index >= #colormap then
error(
"colormap index " .. colormap_index ..
" not in colormap of size " .. #colormap
)
end
end
end
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 end
function image:encode_image_spec() function image:encode_image_spec(properties)
self.data = self.data local color_format = properties.color_format
.. string.char(0, 0) -- X-origin assert(
.. string.char(0, 0) -- Y-origin "Y8" == color_format or -- (8 bit grayscale = 1 byte = 8 bits)
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width "A1R5G5B5" == color_format or -- (A1R5G5B5 = 2 bytes = 16 bits)
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height "B8G8R8" == color_format or -- (B8G8R8 = 3 bytes = 24 bits)
.. string.char(24) -- pixel depth (RGB = 3 bytes = 24 bits) "B8G8R8A8" == color_format -- (B8G8R8A8 = 4 bytes = 32 bits)
.. string.char(0) -- image descriptor )
local scanline_order = properties.scanline_order
assert (
"bottom-top" == scanline_order or
"top-bottom" == scanline_order
)
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)
-- the origin is the bottom left corner of the image (always)
local x_origin_lo = 0
local x_origin_hi = 0
local y_origin_lo = 0
local y_origin_hi = 0
local image_descriptor = 0 -- equal to bottom-top scanline order
local width_lo = self.width % 256
local width_hi = math.floor(self.width / 256)
local height_lo = self.height % 256
local height_hi = math.floor(self.height / 256)
if "top-bottom" == scanline_order then
image_descriptor = 32
y_origin_lo = height_lo
y_origin_hi = height_hi
end
self.data = self.data .. string.char (
x_origin_lo, x_origin_hi,
y_origin_lo, y_origin_hi,
width_lo, width_hi,
height_lo, height_hi,
pixel_depth,
image_descriptor
)
end end
function image:encode_header() 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 self.data = self.data
.. string.char(0) -- image id .. string.char(0) -- image id
.. string.char(0) -- color map type .. string.char(colormap_type)
.. string.char(10) -- image type (RLE RGB = 10) .. string.char(image_type)
self:encode_colormap_spec() -- color map specification self:encode_colormap_spec(properties) -- color map specification
self:encode_image_spec() -- image specification self:encode_image_spec(properties) -- image specification
self:encode_colormap(properties)
end end
function image:encode_data() function image:encode_data(properties)
local current_pixel = '' local color_format = properties.color_format
local previous_pixel = '' local colormap = properties.colormap
local count = 1 local compression = properties.compression
local packets = {}
local rle_packet = '' 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 _, row in ipairs(self.pixels) do
for _, pixel in ipairs(row) do for _, pixel in ipairs(row) do
current_pixel = string.char(pixel[3], pixel[2], pixel[1]) local raw_pixel = string.char(pixel[1])
if current_pixel ~= previous_pixel or count == 128 then raw_pixels[#raw_pixels + 1] = raw_pixel
packets[#packets +1] = rle_packet 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 count = 1
previous_pixel = current_pixel previous_r = pixel[1]
previous_g = pixel[2]
previous_b = pixel[3]
else else
count = count + 1 count = count + 1
end end
rle_packet = string.char(128 + count - 1) .. current_pixel
end end
end end
packets[#packets +1] = rle_packet 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) self.data = self.data .. table.concat(packets)
end end
@ -75,15 +581,45 @@ function image:encode_footer()
.. string.char(0) .. string.char(0)
end end
function image:encode() function image:encode(properties)
self:encode_header() -- header local properties = properties or {}
properties.colormap = properties.colormap or {}
properties.compression = properties.compression or "RAW"
properties.scanline_order = properties.scanline_order or "bottom-top"
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.data = ""
self:encode_header(properties) -- header
-- no color map and image id data -- no color map and image id data
self:encode_data() -- encode data self:encode_data(properties) -- encode data
-- no extension or developer area -- no extension or developer area
self:encode_footer() -- footer self:encode_footer() -- footer
end end
function image:save(filename) function image:save(filename, properties)
self:encode(properties)
local f = assert(io.open(filename, "wb")) local f = assert(io.open(filename, "wb"))
f:write(self.data) f:write(self.data)
f:close() f:close()

View File

@ -0,0 +1,51 @@
dofile("init.lua")
local colormap = {
{ 0, 0, 0 }, -- black
{ 255, 255, 255 }, -- white
{ 255, 0, 0 }, -- red
{ 0, 255, 0 }, -- green
{ 0, 0, 255 }, -- blue
}
local _ = { 0 }
local W = { 1 }
local R = { 2 }
local G = { 3 }
local B = { 4 }
local pixels_tiny = {
{ W, W, W, W, W, W, W, W, W, W, W, W, },
{ W, _, _, _, _, _, _, _, _, _, _, W, },
{ W, _, _, _, _, _, _, B, _, B, _, W, },
{ W, _, _, _, _, _, _, B, B, B, _, W, },
{ W, _, _, _, G, G, G, B, _, B, _, W, },
{ W, _, _, _, G, _, G, B, B, B, _, W, },
{ W, _, _, R, G, _, _, _, _, _, _, W, },
{ W, _, _, R, G, G, G, _, _, _, _, W, },
{ W, _, _, R, _, _, _, _, _, _, _, W, },
{ W, _, R, R, R, _, _, _, _, _, _, W, },
{ W, _, _, _, _, _, _, _, _, _, _, W, },
{ W, W, W, W, W, W, W, W, W, W, W, W, },
}
local pixels_huge = {}
local size_tiny = #pixels_tiny
local size_huge = 1200
local scale = size_huge / size_tiny
for x_huge = 1,size_huge,1 do
local x_tiny = math.ceil( x_huge / scale )
for z_huge = 1,size_huge,1 do
local z_tiny = math.ceil( z_huge / scale )
if nil == pixels_huge[z_huge] then
pixels_huge[z_huge] = {}
end
pixels_huge[z_huge][x_huge] = pixels_tiny[z_tiny][x_tiny]
end
end
tga_encoder.image(pixels_tiny):save("logo_tiny.tga", {colormap=colormap})
tga_encoder.image(pixels_huge):save("logo_huge.tga", {colormap=colormap})

View File

@ -1,3 +1,2 @@
name = tga_encoder name = tga_encoder
author = Fleckenstein
description = A TGA Encoder written in Lua without the use of external Libraries. description = A TGA Encoder written in Lua without the use of external Libraries.

View File

@ -0,0 +1,180 @@
#!/usr/bin/env lua5.1
dofile("../init.lua")
local _ = { 0 }
local R = { 1 }
local G = { 2 }
local B = { 3 }
local pixels_colormapped_bt = {
{ _, _, _, _, _, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, G, _, G, B, B, B, },
{ _, R, G, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ R, R, R, _, _, _, _, _, },
}
local pixels_colormapped_tb = {
{ R, R, R, _, _, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, G, _, _, _, _, _, },
{ _, _, G, _, G, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, _, _, _, B, _, B, },
}
image_colormapped_bt = tga_encoder.image(pixels_colormapped_bt)
image_colormapped_tb = tga_encoder.image(pixels_colormapped_tb)
colormap_32bpp = {
{ 0, 0, 0, 128 },
{ 255, 0, 0, 255 },
{ 0, 255, 0, 255 },
{ 0, 0, 255, 255 },
}
image_colormapped_bt:save(
"type1_32bpp_bt.tga",
{ colormap = colormap_32bpp, color_format = "B8G8R8A8", scanline_order = "bottom-top" }
)
image_colormapped_tb:save(
"type1_32bpp_tb.tga",
{ colormap = colormap_32bpp, color_format = "B8G8R8A8", scanline_order = "top-bottom" }
)
image_colormapped_bt:save(
"type1_16bpp_bt.tga",
{ colormap = colormap_32bpp, color_format = "A1R5G5B5", scanline_order = "bottom-top" }
)
image_colormapped_tb:save(
"type1_16bpp_tb.tga",
{ colormap = colormap_32bpp, color_format = "A1R5G5B5", scanline_order = "top-bottom" }
)
colormap_24bpp = {
{ 0, 0, 0 },
{ 255, 0, 0 },
{ 0, 255, 0 },
{ 0, 0, 255 },
}
image_colormapped_bt:save(
"type1_24bpp_bt.tga",
{ colormap = colormap_32bpp, color_format = "B8G8R8", scanline_order = "bottom-top" }
)
image_colormapped_tb:save(
"type1_24bpp_tb.tga",
{ colormap = colormap_32bpp, color_format = "B8G8R8", scanline_order = "top-bottom" }
)
local _ = { 0, 0, 0, 128 }
local R = { 255, 0, 0, 255 }
local G = { 0, 255, 0, 255 }
local B = { 0, 0, 255, 255 }
local pixels_rgba_bt = {
{ _, _, _, _, _, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, G, _, G, B, B, B, },
{ _, R, G, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ R, R, R, _, _, _, _, _, },
}
local pixels_rgba_tb = {
{ R, R, R, _, _, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, G, _, _, _, _, _, },
{ _, _, G, _, G, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, _, _, _, B, _, B, },
}
image_rgba_bt = tga_encoder.image(pixels_rgba_bt)
image_rgba_tb = tga_encoder.image(pixels_rgba_tb)
image_rgba_bt:save(
"type2_32bpp_bt.tga",
{ color_format="B8G8R8A8", compression="RAW", scanline_order = "bottom-top" }
)
image_rgba_tb:save(
"type2_32bpp_tb.tga",
{ color_format="B8G8R8A8", compression="RAW", scanline_order = "top-bottom" }
)
image_rgba_bt:save(
"type10_32bpp_bt.tga",
{ color_format="B8G8R8A8", compression="RLE", scanline_order = "bottom-top" }
)
image_rgba_tb:save(
"type10_32bpp_tb.tga",
{ color_format="B8G8R8A8", compression="RLE", scanline_order = "top-bottom" }
)
local _ = { 0, 0, 0 }
local R = { 255, 0, 0 }
local G = { 0, 255, 0 }
local B = { 0, 0, 255 }
local pixels_rgb_bt = {
{ _, _, _, _, _, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, G, _, G, B, B, B, },
{ _, R, G, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ R, R, R, _, _, _, _, _, },
}
local pixels_rgb_tb = {
{ R, R, R, _, _, _, _, _, },
{ _, R, _, _, _, _, _, _, },
{ _, R, G, G, G, _, _, _, },
{ _, R, G, _, _, _, _, _, },
{ _, _, G, _, G, B, B, B, },
{ _, _, G, G, G, B, _, B, },
{ _, _, _, _, _, B, B, B, },
{ _, _, _, _, _, B, _, B, },
}
image_rgb_bt = tga_encoder.image(pixels_rgb_bt)
image_rgb_tb = tga_encoder.image(pixels_rgb_tb)
image_rgb_bt:save(
"type2_24bpp_bt.tga",
{ color_format="B8G8R8", compression="RAW", scanline_order = "bottom-top" }
)
image_rgb_tb:save(
"type2_24bpp_tb.tga",
{ color_format="B8G8R8", compression="RAW", scanline_order = "top-bottom" }
)
image_rgb_bt:save(
"type10_24bpp_bt.tga",
{ color_format="B8G8R8", compression="RLE", scanline_order = "bottom-top" }
)
image_rgb_tb:save(
"type10_24bpp_tb.tga",
{ color_format="B8G8R8", compression="RLE", scanline_order = "top-bottom" }
)
image_rgb_bt:save(
"type2_16bpp_bt.tga",
{ color_format="A1R5G5B5", compression="RAW", scanline_order = "bottom-top" }
)
image_rgb_tb:save(
"type2_16bpp_tb.tga",
{ color_format="A1R5G5B5", compression="RAW", scanline_order = "top-bottom" }
)
image_rgb_bt:save(
"type10_16bpp_bt.tga",
{ color_format="A1R5G5B5", compression="RLE", scanline_order = "bottom-top" }
)
image_rgb_tb:save(
"type10_16bpp_tb.tga",
{ color_format="A1R5G5B5", compression="RLE", scanline_order = "top-bottom" }
)

View File

@ -113,6 +113,55 @@ local function disable_physics(object, luaentity, ignore_check, reset_movement)
end end
end end
local function try_object_pickup(player, inv, object, checkpos)
if not inv then return end
local le = object:get_luaentity()
-- Check magnet timer
if not (le._magnet_timer >= 0) then return end
if not (le._magnet_timer < item_drop_settings.magnet_time) then return end
-- Don't try to collect again
if le._removed then return end
-- Ignore if itemstring is not set yet
if le.itemstring == "" then return end
-- Add what we can to the inventory
local itemstack = ItemStack(le.itemstring)
local leftovers = inv:add_item("main", itemstack )
check_pickup_achievements(object, player)
if leftovers:is_empty() then
-- Destroy entity
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
le.target = checkpos
le._removed = true
-- Stop the object
object:set_velocity(vector.zero())
object:set_acceleration(vector.zero())
object:move_to(checkpos)
-- Update sound pool
local name = player:get_player_name()
pool[name] = ( pool[name] or 0 ) + 1
-- Make sure the object gets removed
minetest.after(0.25, function()
--safety check
if object and object:get_luaentity() then
object:remove()
end
end)
else
-- Update entity itemstring
le.itemstring = leftovers:to_string()
end
end
minetest.register_globalstep(function(_) minetest.register_globalstep(function(_)
tick = not tick tick = not tick
@ -147,40 +196,7 @@ minetest.register_globalstep(function(_)
object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer
and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
if object:get_luaentity()._magnet_timer >= 0 and try_object_pickup( player, inv, object, checkpos )
object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and
inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
-- Collection
if not object:get_luaentity()._removed then
-- Ignore if itemstring is not set yet
if object:get_luaentity().itemstring ~= "" then
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
check_pickup_achievements(object, player)
-- Destroy entity
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
object:get_luaentity().target = checkpos
object:get_luaentity()._removed = true
object:set_velocity(vector.zero())
object:set_acceleration(vector.zero())
object:move_to(checkpos)
pool[name] = pool[name] + 1
minetest.after(0.25, function()
--safety check
if object and object:get_luaentity() then
object:remove()
end
end)
end
end
end
elseif not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mcl_experience:orb" then elseif not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mcl_experience:orb" then
local entity = object:get_luaentity() local entity = object:get_luaentity()
entity.collector = player:get_player_name() entity.collector = player:get_player_name()

View File

@ -135,3 +135,33 @@ function mcl_minecarts:get_rail_direction(pos_, dir, ctrl, old_switch, railtype)
end end
return {x=0, y=0, z=0} return {x=0, y=0, z=0}
end end
local plane_adjacents = {
vector.new(-1,0,0),
vector.new(1,0,0),
vector.new(0,0,-1),
vector.new(0,0,1),
}
function mcl_minecarts:get_start_direction(pos)
local dir
local i = 0
while (not dir and i < #plane_adjacents) do
i = i+1
local node = minetest.get_node_or_nil(vector.add(pos, plane_adjacents[i]))
if node ~= nil
and minetest.get_item_group(node.name, "rail") == 0
and minetest.get_item_group(node.name, "solid") == 1
and minetest.get_item_group(node.name, "opaque") == 1
then
dir = mcl_minecarts:check_front_up_down(pos, vector.multiply(plane_adjacents[i], -1), true)
end
end
return dir
end
function mcl_minecarts:set_velocity(obj, dir, factor)
obj._velocity = vector.multiply(dir, factor or 3)
obj._old_pos = nil
obj._punched = true
end

View File

@ -241,9 +241,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
if vector.equals(cart_dir, {x=0, y=0, z=0}) then if vector.equals(cart_dir, {x=0, y=0, z=0}) then
return return
end end
self._velocity = vector.multiply(cart_dir, 3) mcl_minecarts:set_velocity(self, cart_dir)
self._old_pos = nil
self._punched = true
return return
end end
@ -300,9 +298,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
time_from_last_punch = math.min(time_from_last_punch, tool_capabilities.full_punch_interval) time_from_last_punch = math.min(time_from_last_punch, tool_capabilities.full_punch_interval)
local f = 3 * (time_from_last_punch / tool_capabilities.full_punch_interval) local f = 3 * (time_from_last_punch / tool_capabilities.full_punch_interval)
self._velocity = vector.multiply(cart_dir, f) mcl_minecarts:set_velocity(self, cart_dir, f)
self._old_pos = nil
self._punched = true
end end
cart.on_activate_by_rail = on_activate_by_rail cart.on_activate_by_rail = on_activate_by_rail
@ -470,7 +466,7 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
return return
end end
local dir, last_switch = nil, nil local dir, last_switch, restart_pos = nil, nil, nil
if not pos then if not pos then
pos = self.object:get_pos() pos = self.object:get_pos()
end end
@ -497,6 +493,9 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
minetest.swap_node(rou_pos, newnode) minetest.swap_node(rou_pos, newnode)
mesecon.receptor_on(rou_pos) mesecon.receptor_on(rou_pos)
end end
if node.name == "mcl_minecarts:golden_rail_on" then
restart_pos = rou_pos
end
if node_old.name == "mcl_minecarts:detector_rail_on" then if node_old.name == "mcl_minecarts:detector_rail_on" then
local newnode = {name="mcl_minecarts:detector_rail", param2 = node_old.param2} local newnode = {name="mcl_minecarts:detector_rail", param2 = node_old.param2}
minetest.swap_node(rou_old, newnode) minetest.swap_node(rou_old, newnode)
@ -647,6 +646,14 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
if update.pos then if update.pos then
self.object:set_pos(pos) self.object:set_pos(pos)
end end
-- stopped on "mcl_minecarts:golden_rail_on"
if vector.equals(vel, {x=0, y=0, z=0}) and restart_pos then
local dir = mcl_minecarts:get_start_direction(restart_pos)
if dir then
mcl_minecarts:set_velocity(self, dir)
end
end
end end
function cart:get_staticdata() function cart:get_staticdata()
@ -687,7 +694,15 @@ function mcl_minecarts.place_minecart(itemstack, pointed_thing, placer)
if le then if le then
le._railtype = railtype le._railtype = railtype
end end
local cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype) local cart_dir
if node.name == "mcl_minecarts:golden_rail_on" then
cart_dir = mcl_minecarts:get_start_direction(railpos)
end
if cart_dir then
mcl_minecarts:set_velocity(le, cart_dir)
else
cart_dir = mcl_minecarts:get_rail_direction(railpos, {x=1, y=0, z=0}, nil, nil, railtype)
end
cart:set_yaw(minetest.dir_to_yaw(cart_dir)) cart:set_yaw(minetest.dir_to_yaw(cart_dir))
local pname = "" local pname = ""

View File

@ -112,6 +112,22 @@ register_rail("mcl_minecarts:golden_rail_on",
onstate = "mcl_minecarts:golden_rail_on", onstate = "mcl_minecarts:golden_rail_on",
rules = rail_rules_long, rules = rail_rules_long,
}, },
effector = {
action_on = function(pos, node)
local dir = mcl_minecarts:get_start_direction(pos)
if not dir then return end
local objs = minetest.get_objects_inside_radius(pos, 1)
for _, o in pairs(objs) do
local l = o:get_luaentity()
local v = o:get_velocity()
if l and string.sub(l.name, 1, 14) == "mcl_minecarts:"
and v and vector.equals(v, vector.zero())
then
mcl_minecarts:set_velocity(l, dir)
end
end
end,
},
}, },
drop = "mcl_minecarts:golden_rail", drop = "mcl_minecarts:golden_rail",
}, },

View File

@ -322,7 +322,7 @@ function mob_class:toggle_sit(clicker,p)
particle = "mobs_mc_wolf_icon_roam.png" particle = "mobs_mc_wolf_icon_roam.png"
self.order = "roam" self.order = "roam"
self.state = "stand" self.state = "stand"
self.walk_chance = default_walk_chance self.walk_chance = 50
self.jump = true self.jump = true
self:set_animation("stand") self:set_animation("stand")
-- TODO: Add sitting model -- TODO: Add sitting model

View File

@ -325,12 +325,19 @@ function mcl_mobs.register_mob(name, def)
_spawner = def._spawner, _spawner = def._spawner,
} }
minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta))
if minetest.get_modpath("doc_identifier") ~= nil then if minetest.get_modpath("doc_identifier") ~= nil then
doc.sub.identifier.register_object(name, "basics", "mobs") doc.sub.identifier.register_object(name, "basics", "mobs")
if def.unused ~= true then
doc.add_entry("mobs", name, {
name = def.description or name,
data = final_def,
})
end
end end
minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta))
end -- END mcl_mobs.register_mob function end -- END mcl_mobs.register_mob function

View File

@ -13,7 +13,7 @@ local get_node = minetest.get_node
local get_item_group = minetest.get_item_group local get_item_group = minetest.get_item_group
local get_node_light = minetest.get_node_light local get_node_light = minetest.get_node_light
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
local get_biome_name = minetest.get_biome_name local mt_get_biome_name = minetest.get_biome_name
local get_objects_inside_radius = minetest.get_objects_inside_radius local get_objects_inside_radius = minetest.get_objects_inside_radius
local get_connected_players = minetest.get_connected_players local get_connected_players = minetest.get_connected_players
@ -664,7 +664,29 @@ local function has_room(self,pos)
return true return true
end end
mcl_mobs.custom_biomecheck = nil
function mcl_mobs.register_custom_biomecheck(custom_biomecheck)
mcl_mobs.custom_biomecheck = custom_biomecheck
end
local function get_biome_name(pos)
if mcl_mobs.custom_biomecheck then
return mcl_mobs.custom_biomecheck (pos)
else
local gotten_biome = minetest.get_biome_data(pos)
if not gotten_biome then
return
end
gotten_biome = mt_get_biome_name(gotten_biome.biome)
--minetest.log ("biome: " .. dump(gotten_biome))
return gotten_biome
end
end
local function spawn_check(pos, spawn_def) local function spawn_check(pos, spawn_def)
if not spawn_def or not pos then return end if not spawn_def or not pos then return end
@ -674,11 +696,10 @@ local function spawn_check(pos, spawn_def)
local mob_def = minetest.registered_entities[spawn_def.name] local mob_def = minetest.registered_entities[spawn_def.name]
local mob_type = mob_def.type local mob_type = mob_def.type
local gotten_node = get_node(pos).name local gotten_node = get_node(pos).name
local gotten_biome = minetest.get_biome_data(pos) if not gotten_node then return end
if not gotten_node or not gotten_biome then return end local biome_name = get_biome_name(pos)
if not biome_name then return end
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0 local is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
if not is_ground then if not is_ground then
@ -696,8 +717,9 @@ local function spawn_check(pos, spawn_def)
if pos.y >= spawn_def.min_height if pos.y >= spawn_def.min_height
and pos.y <= spawn_def.max_height and pos.y <= spawn_def.max_height
and spawn_def.dimension == dimension and spawn_def.dimension == dimension
and biome_check(spawn_def.biomes, gotten_biome) then and biome_check(spawn_def.biomes, biome_name) then
mcl_log("Spawn level 1 check - Passed")
if (is_ground or spawn_def.type_of_spawning ~= "ground") if (is_ground or spawn_def.type_of_spawning ~= "ground")
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf) and (spawn_def.type_of_spawning ~= "ground" or not is_leaf)
and (not is_farm_animal(spawn_def.name) or is_grass) and (not is_farm_animal(spawn_def.name) or is_grass)
@ -707,6 +729,7 @@ local function spawn_check(pos, spawn_def)
and (spawn_def.check_position and spawn_def.check_position(pos) or spawn_def.check_position == nil) and (spawn_def.check_position and spawn_def.check_position(pos) or spawn_def.check_position == nil)
and ( not spawn_protected or not minetest.is_protected(pos, "") ) then and ( not spawn_protected or not minetest.is_protected(pos, "") ) then
mcl_log("Spawn level 2 check - Passed")
local gotten_light = get_node_light(pos) local gotten_light = get_node_light(pos)
if modern_lighting then if modern_lighting then

View File

@ -1,6 +1,7 @@
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local axolotl = { local axolotl = {
description = S("Axolotl"),
type = "animal", type = "animal",
spawn_class = "axolotl", spawn_class = "axolotl",
can_despawn = true, can_despawn = true,

View File

@ -30,6 +30,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
--################### --###################
local cod = { local cod = {
description = S("Cod"),
type = "animal", type = "animal",
spawn_class = "water_ambient", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,

View File

@ -136,7 +136,7 @@ mcl_mobs.register_mob("mobs_mc:creeper", {
}) })
mcl_mobs.register_mob("mobs_mc:creeper_charged", { mcl_mobs.register_mob("mobs_mc:creeper_charged", {
description = S("Creeper"), description = S("Charged Creeper"),
type = "monster", type = "monster",
spawn_class = "hostile", spawn_class = "hostile",
hp_min = 20, hp_min = 20,

View File

@ -30,6 +30,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
--################### --###################
local dolphin = { local dolphin = {
description = S("Dolphin"),
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "water",
can_despawn = true, can_despawn = true,

View File

@ -30,6 +30,7 @@ for i=1,4 do
end end
mcl_mobs.register_mob("mobs_mc:glow_squid", { mcl_mobs.register_mob("mobs_mc:glow_squid", {
description = S("Glow Squid"),
type = "animal", type = "animal",
spawn_class = "water_underground", spawn_class = "water_underground",
can_despawn = true, can_despawn = true,

View File

@ -16,6 +16,13 @@ local trading_items = {
{ itemstring = "mcl_throwing:ender_pearl", amount_min = 2, amount_max = 6 }, { itemstring = "mcl_throwing:ender_pearl", amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_potions:fire_resistance", amount_min = 1, amount_max = 1 }, { itemstring = "mcl_potions:fire_resistance", amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_potions:fire_resistance_splash", amount_min = 1, amount_max = 1 }, { itemstring = "mcl_potions:fire_resistance_splash", amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_enchanting:book_enchanted", amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_armor:boots_iron_enchanted", amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_blackstone:blackstone", amount_min = 8, amount_max = 16 },
{ itemstring = "mcl_bows:arrow", amount_min = 6, amount_max = 12 },
{ itemstring = "mcl_core:crying_obsidian", amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_fire:fire_charge", amount_min = 1, amount_max = 1 },
--{ itemstring = "FIXME:spectral_arrow", amount_min = 6, amount_max = 12 },
} }
local S = minetest.get_translator("mobs_mc") local S = minetest.get_translator("mobs_mc")
@ -142,14 +149,18 @@ local piglin = {
local c_pos = self.object:get_pos() local c_pos = self.object:get_pos()
if c_pos then if c_pos then
self.what_traded = trading_items[math.random(#trading_items)] self.what_traded = trading_items[math.random(#trading_items)]
for x = 1, math.random(self.what_traded.amount_min, self.what_traded.amount_max) do local stack = ItemStack(self.what_traded.itemstring)
local p = c_pos stack:set_count(math.random(self.what_traded.amount_min, self.what_traded.amount_max))
local nn=minetest.find_nodes_in_area_under_air(vector.offset(c_pos,-1,-1,-1),vector.offset(c_pos,1,1,1),{"group:solid"}) if mcl_enchanting.is_enchanted(self.what_traded.itemstring) then
if nn and #nn > 0 then local enchantment = "soul_speed"
p = vector.offset(nn[math.random(#nn)],0,1,0) mcl_enchanting.enchant(stack, enchantment, mcl_enchanting.random(nil, 1, mcl_enchanting.enchantments[enchantment].max_level))
end
minetest.add_item(p, self.what_traded.itemstring)
end end
local p = c_pos
local nn=minetest.find_nodes_in_area_under_air(vector.offset(c_pos,-1,-1,-1),vector.offset(c_pos,1,1,1),{"group:solid"})
if nn and #nn > 0 then
p = vector.offset(nn[math.random(#nn)],0,1,0)
end
minetest.add_item(p, stack)
end end
end) end)
end end
@ -332,8 +343,13 @@ mcl_mobs.register_mob("mobs_mc:baby_zombified_piglin", baby_zombified_piglin)
-- Compatibility code. These were removed, and now are called zombie piglins. They don't spawn. -- Compatibility code. These were removed, and now are called zombie piglins. They don't spawn.
-- This is only to catch old cases. Maybe could be an alias? -- This is only to catch old cases. Maybe could be an alias?
mcl_mobs.register_mob("mobs_mc:pigman", zombified_piglin) local pigman_unused = table.copy(zombified_piglin)
mcl_mobs.register_mob("mobs_mc:baby_pigman", baby_zombified_piglin) pigman_unused.unused = true
local baby_pigman_unused = table.copy(baby_zombified_piglin)
baby_pigman_unused.unused = true
mcl_mobs.register_mob("mobs_mc:pigman", pigman_unused)
mcl_mobs.register_mob("mobs_mc:baby_pigman", baby_pigman_unused)
-- Piglin Brute -- -- Piglin Brute --

View File

@ -10,6 +10,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
--################### --###################
local salmon = { local salmon = {
description = S("Salmon"),
type = "animal", type = "animal",
spawn_class = "water_ambient", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,

View File

@ -44,6 +44,13 @@ local skeleton = {
"mcl_bows_bow_0.png", -- wielded_item "mcl_bows_bow_0.png", -- wielded_item
} }
}, },
-- TODO: change random to new api when min minetest version is 5.8
sounds = {
random = "mobs_mc_skeleton_random.2",
death = "mobs_mc_skeleton_death",
damage = "mobs_mc_skeleton_hurt",
distance = 16,
},
walk_velocity = 1.2, walk_velocity = 1.2,
run_velocity = 2.0, run_velocity = 2.0,
damage = 2, damage = 2,

View File

@ -37,7 +37,7 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", {
visual_size = {x=1.2, y=1.2}, visual_size = {x=1.2, y=1.2},
makes_footstep_sound = true, makes_footstep_sound = true,
sounds = { sounds = {
random = "mobs_mc_skeleton_random", random = "mobs_mc_skeleton_random.1",
death = "mobs_mc_skeleton_death", death = "mobs_mc_skeleton_death",
damage = "mobs_mc_skeleton_hurt", damage = "mobs_mc_skeleton_hurt",
distance = 16, distance = 16,

View File

@ -176,7 +176,7 @@ end
-- Slime -- Slime
local slime_big = { local slime_big = {
description = S("Slime"), description = S("Slime - big"),
type = "monster", type = "monster",
spawn_class = "hostile", spawn_class = "hostile",
group_attack = { "mobs_mc:slime_big", "mobs_mc:slime_small", "mobs_mc:slime_tiny" }, group_attack = { "mobs_mc:slime_big", "mobs_mc:slime_small", "mobs_mc:slime_tiny" },
@ -231,6 +231,7 @@ local slime_big = {
mcl_mobs.register_mob("mobs_mc:slime_big", slime_big) mcl_mobs.register_mob("mobs_mc:slime_big", slime_big)
local slime_small = table.copy(slime_big) local slime_small = table.copy(slime_big)
slime_small.description = S("Slime - small")
slime_small.sounds.base_pitch = 1.15 slime_small.sounds.base_pitch = 1.15
slime_small.hp_min = 4 slime_small.hp_min = 4
slime_small.hp_max = 4 slime_small.hp_max = 4
@ -248,6 +249,7 @@ slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 0.6, 1.0)
mcl_mobs.register_mob("mobs_mc:slime_small", slime_small) mcl_mobs.register_mob("mobs_mc:slime_small", slime_small)
local slime_tiny = table.copy(slime_big) local slime_tiny = table.copy(slime_big)
slime_tiny.description = S("Slime - tiny")
slime_tiny.sounds.base_pitch = 1.3 slime_tiny.sounds.base_pitch = 1.3
slime_tiny.hp_min = 1 slime_tiny.hp_min = 1
slime_tiny.hp_max = 1 slime_tiny.hp_max = 1
@ -397,7 +399,7 @@ swamp_max)
-- Magma cube -- Magma cube
local magma_cube_big = { local magma_cube_big = {
description = S("Magma Cube"), description = S("Magma Cube - big"),
type = "monster", type = "monster",
spawn_class = "hostile", spawn_class = "hostile",
hp_min = 16, hp_min = 16,
@ -458,6 +460,7 @@ local magma_cube_big = {
mcl_mobs.register_mob("mobs_mc:magma_cube_big", magma_cube_big) mcl_mobs.register_mob("mobs_mc:magma_cube_big", magma_cube_big)
local magma_cube_small = table.copy(magma_cube_big) local magma_cube_small = table.copy(magma_cube_big)
magma_cube_small.description = S("Magma Cube - small")
magma_cube_small.sounds.jump = "mobs_mc_magma_cube_small" magma_cube_small.sounds.jump = "mobs_mc_magma_cube_small"
magma_cube_small.sounds.death = "mobs_mc_magma_cube_small" magma_cube_small.sounds.death = "mobs_mc_magma_cube_small"
magma_cube_small.hp_min = 4 magma_cube_small.hp_min = 4
@ -479,6 +482,7 @@ magma_cube_small.on_die = spawn_children_on_die("mobs_mc:magma_cube_tiny", 0.6,
mcl_mobs.register_mob("mobs_mc:magma_cube_small", magma_cube_small) mcl_mobs.register_mob("mobs_mc:magma_cube_small", magma_cube_small)
local magma_cube_tiny = table.copy(magma_cube_big) local magma_cube_tiny = table.copy(magma_cube_big)
magma_cube_tiny.description = S("Magma Cube - tiny")
magma_cube_tiny.sounds.jump = "mobs_mc_magma_cube_small" magma_cube_tiny.sounds.jump = "mobs_mc_magma_cube_small"
magma_cube_tiny.sounds.death = "mobs_mc_magma_cube_small" magma_cube_tiny.sounds.death = "mobs_mc_magma_cube_small"
magma_cube_tiny.sounds.base_pitch = 1.25 magma_cube_tiny.sounds.base_pitch = 1.25

View File

@ -11,6 +11,7 @@ local S = minetest.get_translator("mobs_mc")
local strider = { local strider = {
description = S("Strider"),
type = "animal", type = "animal",
passive = true, passive = true,
spawn_class = "passive", spawn_class = "passive",
@ -30,6 +31,8 @@ local strider = {
} }, } },
visual_size = {x=3, y=3}, visual_size = {x=3, y=3},
sounds = { sounds = {
eat = "mobs_mc_animal_eat_generic",
distance = 16,
}, },
jump = true, jump = true,
makes_footstep_sound = true, makes_footstep_sound = true,
@ -51,6 +54,7 @@ local strider = {
walk_start = 1, walk_start = 1,
walk_end = 20, walk_end = 20,
}, },
follow = { "mcl_crimson:warped_fungus" },
lava_damage = 0, lava_damage = 0,
fire_damage = 0, fire_damage = 0,
light_damage = 0, light_damage = 0,
@ -67,8 +71,13 @@ local strider = {
do_custom = function(self, dtime) do_custom = function(self, dtime)
if minetest.find_node_near(self.object:get_pos(), 2, {"mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing"}) then if minetest.find_node_near(self.object:get_pos(), 2, {"mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing"}) then
self.walk_velocity = 2 if self.driver then
self.run_velocity = 4 self.walk_velocity = 4
self.run_velocity = 8
else
self.walk_velocity = 2
self.run_velocity = 4
end
self.base_texture[1] = "extra_mobs_strider.png" self.base_texture[1] = "extra_mobs_strider.png"
self.shaking = false self.shaking = false
else else
@ -122,7 +131,7 @@ local strider = {
local wielditem = clicker:get_wielded_item() local wielditem = clicker:get_wielded_item()
if wielditem:get_name() ~= "mcl_crimson:warped_fungus" then if wielditem:get_name() == "mcl_crimson:warped_fungus" then
if self:feed_tame(clicker, 1, true, true) then return end if self:feed_tame(clicker, 1, true, true) then return end
end end
@ -197,6 +206,7 @@ mcl_mobs.register_mob("mobs_mc:strider", strider)
-- Baby strider. -- Baby strider.
local baby_strider = table.copy(strider) local baby_strider = table.copy(strider)
baby_strider.description = S("Baby Strider")
baby_strider.collisionbox = {-.3, -0.01, -.3, .3, 0.94, .3} baby_strider.collisionbox = {-.3, -0.01, -.3, .3, 0.94, .3}
baby_strider.xp_min = 13 baby_strider.xp_min = 13
baby_strider.xp_max = 13 baby_strider.xp_max = 13
@ -206,7 +216,7 @@ textures = { {
} } } }
baby_strider.walk_velocity = 1.2 baby_strider.walk_velocity = 1.2
baby_strider.run_velocity = 2.4 baby_strider.run_velocity = 2.4
baby_strider.child = 1 baby_strider.child = true
mcl_mobs.register_mob("mobs_mc:baby_strider", baby_strider) mcl_mobs.register_mob("mobs_mc:baby_strider", baby_strider)

View File

@ -58,6 +58,7 @@ local function set_textures(self)
end end
local tropical_fish = { local tropical_fish = {
description = S("Tropical Fish"),
type = "animal", type = "animal",
spawn_class = "water_ambient", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,

View File

@ -135,6 +135,7 @@ end
-- Tamed wolf (aka “dog”) -- Tamed wolf (aka “dog”)
local dog = table.copy(wolf) local dog = table.copy(wolf)
dog.description = S("Dog")
dog.can_despawn = false dog.can_despawn = false
dog.passive = true dog.passive = true
dog.hp_min = 20 dog.hp_min = 20

View File

@ -53,7 +53,7 @@ doc.data = {}
doc.data.categories = {} doc.data.categories = {}
doc.data.aliases = {} doc.data.aliases = {}
-- Default order (includes categories of other mods from the Docuentation System modpack) -- Default order (includes categories of other mods from the Docuentation System modpack)
doc.data.category_order = {"basics", "nodes", "tools", "craftitems", "advanced"} doc.data.category_order = {"basics", "nodes", "tools", "craftitems", "advanced", "mobs"}
doc.data.category_count = 0 doc.data.category_count = 0
doc.data.players = {} doc.data.players = {}

View File

@ -116,7 +116,11 @@ function doc_identifier.identify(itemstack, user, pointed_thing)
end end
-- A known registered object -- A known registered object
elseif ro then elseif ro then
doc.show_entry(username, ro.category, ro.entry, true) if doc.entry_exists("mobs", le.name) then
doc.show_entry(username, "mobs", le.name, true)
else
doc.show_entry(username, ro.category, ro.entry, true)
end
-- Undefined object (error) -- Undefined object (error)
elseif minetest.registered_entities[le.name] == nil then elseif minetest.registered_entities[le.name] == nil then
show_message(username, "error_unknown", le.name) show_message(username, "error_unknown", le.name)

View File

@ -1136,6 +1136,86 @@ doc.add_category("craftitems", {
end end
}) })
doc.add_category("mobs", {
name = S("Mobs"),
description = S("different mobs"),
build_formspec = function(data, playername)
if data then
local datastring = ""
if data.description then
datastring = datastring .. S("Description: @1", data.description)
datastring = newline2(datastring)
end
if data.type then
datastring = datastring .. S("Type: @1", data.type)
datastring = newline2(datastring)
end
if data.spawn_class then
datastring = datastring .. S("spawn class: @1", data.spawn_class)
datastring = newline2(datastring)
end
if data.jump then
datastring = datastring .. S("Can Jump")
datastring = newline2(datastring)
end
if data.fly then
datastring = datastring .. S("Can Fly")
datastring = newline2(datastring)
end
if data.drops then
count = 0
for _,item in ipairs(data.drops) do
count = count + 1
end
if count > 0 then
datastring = datastring .. S("drops: ")
datastring = newline(datastring)
for _,item in ipairs(data.drops) do
local itemDescription = ItemStack(item.name):get_short_description()
datastring = datastring .. itemDescription
datastring = newline(datastring)
end
datastring = newline2(datastring)
end
end
if data.follow then
datastring = datastring .. S("follows player when these items are held:")
datastring = newline(datastring)
if type(data.follow) == "string" then
datastring = datastring .. data.follow
datastring = newline(datastring)
else
for i=1, #data.follow do
local itemstring = data.follow[i]
local itemDescription = ItemStack(itemstring):get_short_description()
datastring = datastring .. itemDescription
datastring = newline(datastring)
end
end
datastring = newline2(datastring)
end
local formstring = doc.widgets.text(datastring, nil, nil, doc.FORMSPEC.ENTRY_WIDTH - 1.2)
return formstring
else
return "label[0,1;NO DATA AVALIABLE!]"
end
end
})
-- Register group definition stuff -- Register group definition stuff
-- More (user-)friendly group names to replace the rather technical names -- More (user-)friendly group names to replace the rather technical names
-- for better understanding -- for better understanding

View File

@ -177,7 +177,6 @@ minetest.register_entity("mcl_experience:orb", {
delete_timer = 0, delete_timer = 0,
radius = 4, radius = 4,
on_activate = function(self, staticdata, dtime_s) on_activate = function(self, staticdata, dtime_s)
self.object:set_velocity(vector.new( self.object:set_velocity(vector.new(
math.random(-2,2)*math.random(), math.random(-2,2)*math.random(),
@ -187,10 +186,14 @@ minetest.register_entity("mcl_experience:orb", {
self.object:set_armor_groups({immortal = 1}) self.object:set_armor_groups({immortal = 1})
self.object:set_velocity({x = 0, y = 2, z = 0}) self.object:set_velocity({x = 0, y = 2, z = 0})
self.object:set_acceleration(gravity) self.object:set_acceleration(gravity)
local xp = tonumber(staticdata)
-- Assign 0 xp in case the entity was persisted even though it should not have been (static_save = false)
-- This was a minetest bug for a while: https://github.com/minetest/minetest/issues/14420
local xp = tonumber(staticdata) or 0
self._xp = xp self._xp = xp
size = xp_to_size(xp) size = xp_to_size(xp)
self.object:set_properties({
self.object:set_properties({
visual_size = {x = size, y = size}, visual_size = {x = size, y = size},
glow = 14, glow = 14,
}) })

View File

@ -27,11 +27,11 @@ local function damage_explosion(self, damagemulitplier)
for _,obj in pairs(objects) do for _,obj in pairs(objects) do
if obj:is_player() then if obj:is_player() then
mcl_util.deal_damage(obj, damagemulitplier - vector.distance(p, obj:get_pos()), {type = "explosion"}) mcl_util.deal_damage(obj, damagemulitplier - vector.distance(p, obj:get_pos()), {type = "explosion"})
elseif obj:get_luaentity().is_mob then elseif obj:get_luaentity() and obj:get_luaentity().is_mob then
obj:punch(self.object, 1.0, { obj:punch(self.object, 1.0, {
full_punch_interval=1.0, full_punch_interval=1.0,
damage_groups={fleshy=damagemulitplier - vector.distance(p, obj:get_pos())}, damage_groups={fleshy=damagemulitplier - vector.distance(p, obj:get_pos())},
}, self.object:get_velocity()) }, self.object:get_velocity()) -- TODO possibly change the punch dir to be outwards instead of rocket velocity
end end
end end
end end

View File

@ -2,6 +2,8 @@
local planks = "mcl_cherry_blossom:cherrywood" local planks = "mcl_cherry_blossom:cherrywood"
local logs = "mcl_cherry_blossom:cherrytree" local logs = "mcl_cherry_blossom:cherrytree"
local stripped_logs = "mcl_cherry_blossom:stripped_cherrytree" local stripped_logs = "mcl_cherry_blossom:stripped_cherrytree"
local wood = "mcl_cherry_blossom:cherrytree_bark"
local stripped_wood = "mcl_cherry_blossom:stripped_cherrytree_bark"
minetest.register_craft({ minetest.register_craft({
output = "mcl_cherry_blossom:cherrytree_bark 3", output = "mcl_cherry_blossom:cherrytree_bark 3",
@ -26,6 +28,27 @@ minetest.register_craft({
} }
}) })
minetest.register_craft({
output = "mcl_cherry_blossom:cherrywood 4",
recipe = {
{ wood },
}
})
minetest.register_craft({
output = "mcl_cherry_blossom:cherrywood 4",
recipe = {
{ stripped_logs },
}
})
minetest.register_craft({
output = "mcl_cherry_blossom:cherrywood 4",
recipe = {
{ stripped_wood },
}
})
minetest.register_craft({ minetest.register_craft({
output = "mcl_cherry_blossom:cherry_door 3", output = "mcl_cherry_blossom:cherry_door 3",
recipe = { recipe = {

View File

@ -61,9 +61,9 @@ for _, row in ipairs(block.dyes) do
local sdesc_gtp = row[4] local sdesc_gtp = row[4]
local sdesc_cp = row[5] local sdesc_cp = row[5]
local sdesc_c = row[6] local sdesc_c = row[6]
local ldesc_hc, ldesc_gt, ldesc_cp, ldesc_c local ldesc_hc, ldesc_gt, ldesc_cp, ldesc_c, ldesc_gtp
local create_entry local create_entry
local ename_hc, ename_gt, ename_cp, ename_c local ename_hc, ename_gt, ename_cp, ename_c, ename_gtp
local ltt_cp = cp_tt local ltt_cp = cp_tt
if is_canonical then if is_canonical then
ldesc_hc = hc_desc ldesc_hc = hc_desc

View File

@ -5,8 +5,8 @@ local modpath = minetest.get_modpath(modname)
-- by debiankaios -- by debiankaios
-- adapted for mcl2 by cora -- adapted for mcl2 by cora
local wood_slab_groups = {handy = 1, axey = 1, flammable = 3, material_wood = 1, fire_encouragement = 5, fire_flammability = 20, wood_slab = 1} local wood_slab_groups = {handy = 1, axey = 1, material_wood = 1, wood_slab = 1}
local wood_stair_groups = {handy = 1, axey = 1, flammable = 3, material_wood = 1, fire_encouragement = 5, fire_flammability = 20, wood_stairs = 1} local wood_stair_groups = {handy = 1, axey = 1, material_wood = 1, wood_stairs = 1}
local function generate_warped_tree(pos) local function generate_warped_tree(pos)
minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_1.mts","random",nil,false,"place_center_x,place_center_z") minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_1.mts","random",nil,false,"place_center_x,place_center_z")
@ -463,7 +463,7 @@ minetest.register_craft({
minetest.register_node("mcl_crimson:warped_hyphae_wood", { minetest.register_node("mcl_crimson:warped_hyphae_wood", {
description = S("Warped Hyphae Wood"), description = S("Warped Hyphae Wood"),
tiles = {"mcl_crimson_warped_hyphae_wood.png"}, tiles = {"mcl_crimson_warped_hyphae_wood.png"},
groups = {handy = 5,axey = 1, flammable = 3, wood=1,building_block = 1, material_wood = 1, fire_encouragement = 5, fire_flammability = 20}, groups = {handy = 5,axey = 1, wood=1,building_block = 1, material_wood = 1},
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
_mcl_hardness = 2, _mcl_hardness = 2,
}) })
@ -478,6 +478,27 @@ minetest.register_craft({
}, },
}) })
minetest.register_craft({
output = "mcl_crimson:warped_hyphae_wood 4",
recipe = {
{"mcl_crimson:warped_hyphae_bark"},
},
})
minetest.register_craft({
output = "mcl_crimson:warped_hyphae_wood 4",
recipe = {
{"mcl_crimson:stripped_warped_hyphae"},
},
})
minetest.register_craft({
output = "mcl_crimson:warped_hyphae_wood 4",
recipe = {
{"mcl_crimson:stripped_warped_hyphae_bark"},
},
})
minetest.register_craft({ minetest.register_craft({
output = "mcl_crimson:warped_nylium 2", output = "mcl_crimson:warped_nylium 2",
recipe = { recipe = {
@ -670,6 +691,27 @@ minetest.register_craft({
}, },
}) })
minetest.register_craft({
output = "mcl_crimson:crimson_hyphae_wood 4",
recipe = {
{"mcl_crimson:crimson_hyphae_bark"},
},
})
minetest.register_craft({
output = "mcl_crimson:crimson_hyphae_wood 4",
recipe = {
{"mcl_crimson:stripped_crimson_hyphae"},
},
})
minetest.register_craft({
output = "mcl_crimson:crimson_hyphae_wood 4",
recipe = {
{"mcl_crimson:stripped_crimson_hyphae_bark"},
},
})
minetest.register_craft({ minetest.register_craft({
output = "mcl_crimson:crimson_nylium 2", output = "mcl_crimson:crimson_nylium 2",
recipe = { recipe = {
@ -715,7 +757,7 @@ mcl_doors:register_door("mcl_crimson:crimson_door", {
_doc_items_longdesc = S("Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal."), _doc_items_longdesc = S("Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal."),
_doc_items_usagehelp = S("To open or close a wooden door, rightclick it or supply its lower half with a redstone signal."), _doc_items_usagehelp = S("To open or close a wooden door, rightclick it or supply its lower half with a redstone signal."),
inventory_image = "mcl_crimson_crimson_door.png", inventory_image = "mcl_crimson_crimson_door.png",
groups = {handy=1,axey=1, material_wood=1, flammable=-1}, groups = {handy=1,axey=1, material_wood=1},
_mcl_hardness = 3, _mcl_hardness = 3,
_mcl_blast_resistance = 3, _mcl_blast_resistance = 3,
tiles_bottom = "mcl_crimson_crimson_door_bottom.png", tiles_bottom = "mcl_crimson_crimson_door_bottom.png",
@ -730,7 +772,7 @@ mcl_doors:register_trapdoor("mcl_crimson:crimson_trapdoor", {
tile_front = "mcl_crimson_crimson_trapdoor.png", tile_front = "mcl_crimson_crimson_trapdoor.png",
tile_side = "mcl_crimson_crimson_trapdoor_side.png", tile_side = "mcl_crimson_crimson_trapdoor_side.png",
wield_image = "mcl_crimson_crimson_trapdoor.png", wield_image = "mcl_crimson_crimson_trapdoor.png",
groups = {handy=1,axey=1, mesecon_effector_on=1, material_wood=1, flammable=-1}, groups = {handy=1,axey=1, mesecon_effector_on=1, material_wood=1},
_mcl_hardness = 3, _mcl_hardness = 3,
_mcl_blast_resistance = 3, _mcl_blast_resistance = 3,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
@ -741,7 +783,7 @@ mcl_fences.register_fence_and_fence_gate(
S("Crimson Fence"), S("Crimson Fence"),
S("Crimson Fence Gate"), S("Crimson Fence Gate"),
"mcl_crimson_crimson_fence.png", "mcl_crimson_crimson_fence.png",
{handy=1,axey=1, flammable=2,fence_wood=1, fire_encouragement=5, fire_flammability=20}, {handy=1,axey=1,fence_wood=1},
minetest.registered_nodes["mcl_crimson:crimson_hyphae"]._mcl_hardness, minetest.registered_nodes["mcl_crimson:crimson_hyphae"]._mcl_hardness,
minetest.registered_nodes["mcl_crimson:crimson_hyphae"]._mcl_blast_resistance, minetest.registered_nodes["mcl_crimson:crimson_hyphae"]._mcl_blast_resistance,
{"group:fence_wood"}, {"group:fence_wood"},
@ -753,7 +795,7 @@ mcl_doors:register_door("mcl_crimson:warped_door", {
_doc_items_longdesc = S("Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal."), _doc_items_longdesc = S("Wooden doors are 2-block high barriers which can be opened or closed by hand and by a redstone signal."),
_doc_items_usagehelp = S("To open or close a wooden door, rightclick it or supply its lower half with a redstone signal."), _doc_items_usagehelp = S("To open or close a wooden door, rightclick it or supply its lower half with a redstone signal."),
inventory_image = "mcl_crimson_warped_door.png", inventory_image = "mcl_crimson_warped_door.png",
groups = {handy=1,axey=1, material_wood=1, flammable=-1}, groups = {handy=1,axey=1, material_wood=1},
_mcl_hardness = 3, _mcl_hardness = 3,
_mcl_blast_resistance = 3, _mcl_blast_resistance = 3,
tiles_bottom = "mcl_crimson_warped_door_bottom.png", tiles_bottom = "mcl_crimson_warped_door_bottom.png",
@ -768,7 +810,7 @@ mcl_doors:register_trapdoor("mcl_crimson:warped_trapdoor", {
tile_front = "mcl_crimson_warped_trapdoor.png", tile_front = "mcl_crimson_warped_trapdoor.png",
tile_side = "mcl_crimson_warped_trapdoor_side.png", tile_side = "mcl_crimson_warped_trapdoor_side.png",
wield_image = "mcl_crimson_warped_trapdoor.png", wield_image = "mcl_crimson_warped_trapdoor.png",
groups = {handy=1,axey=1, mesecon_effector_on=1, material_wood=1, flammable=-1}, groups = {handy=1,axey=1, mesecon_effector_on=1, material_wood=1},
_mcl_hardness = 3, _mcl_hardness = 3,
_mcl_blast_resistance = 3, _mcl_blast_resistance = 3,
sounds = mcl_sounds.node_sound_wood_defaults(), sounds = mcl_sounds.node_sound_wood_defaults(),
@ -779,7 +821,7 @@ mcl_fences.register_fence_and_fence_gate(
S("Warped Fence"), S("Warped Fence"),
S("Warped Fence Gate"), S("Warped Fence Gate"),
"mcl_crimson_warped_fence.png", "mcl_crimson_warped_fence.png",
{handy=1,axey=1, flammable=2,fence_wood=1, fire_encouragement=5, fire_flammability=20}, {handy=1,axey=1,fence_wood=1},
minetest.registered_nodes["mcl_crimson:warped_hyphae"]._mcl_hardness, minetest.registered_nodes["mcl_crimson:warped_hyphae"]._mcl_hardness,
minetest.registered_nodes["mcl_crimson:warped_hyphae"]._mcl_blast_resistance, minetest.registered_nodes["mcl_crimson:warped_hyphae"]._mcl_blast_resistance,
{"group:fence_wood"}, {"group:fence_wood"},

View File

@ -182,6 +182,10 @@ local function apply_bone_meal(pointed_thing, user)
local n = minetest.get_node(pos) local n = minetest.get_node(pos)
if n.name == "" then return false end if n.name == "" then return false end
if mcl_util.check_area_protection(pos, pointed_thing.above, user) then
return false
end
for _, func in pairs(mcl_dye.bone_meal_callbacks) do for _, func in pairs(mcl_dye.bone_meal_callbacks) do
if func(pointed_thing, user) then if func(pointed_thing, user) then
return true return true

View File

@ -60,32 +60,20 @@ minetest.register_node("mcl_end:purpur_pillar", {
}) })
local end_rod_name = "mcl_end:end_rod" local end_rod_name = "mcl_end:end_rod"
local end_rod_side_tex = "mcl_end_end_rod_side.png"
local end_rod_def = { local end_rod_def = {
description = S("End Rod"), description = S("End Rod"),
_doc_items_longdesc = S("End rods are decorative light sources."), _doc_items_longdesc = S("End rods are decorative light sources."),
tiles = { tiles = {
"mcl_end_end_rod_top.png", "mcl_end_end_rod.png",
"mcl_end_end_rod_bottom.png",
end_rod_side_tex,
end_rod_side_tex,
end_rod_side_tex,
end_rod_side_tex,
}, },
drawtype = "nodebox", drawtype = "mesh",
mesh = "mcl_end_rod.obj",
is_ground_content = false, is_ground_content = false,
paramtype = "light", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
light_source = minetest.LIGHT_MAX, light_source = minetest.LIGHT_MAX,
sunlight_propagates = true, sunlight_propagates = true,
groups = { dig_immediate=3, deco_block=1, destroy_by_lava_flow=1, end_rod=1 }, groups = { dig_immediate=3, deco_block=1, destroy_by_lava_flow=1, end_rod=1 },
node_box = {
type = "fixed",
fixed = {
{-0.125, -0.5, -0.125, 0.125, -0.4375, 0.125}, -- Base
{-0.0625, -0.4375, -0.0625, 0.0625, 0.5, 0.0625}, -- Rod
},
},
selection_box = { selection_box = {
type = "fixed", type = "fixed",
fixed = { fixed = {
@ -156,8 +144,7 @@ local colored_end_rods = {
{"lime", S("Lime End Rod"), "green"}, {"lime", S("Lime End Rod"), "green"},
{"lightblue", S("Light Blue End Rod"), "lightblue"}, {"lightblue", S("Light Blue End Rod"), "lightblue"},
} }
local top_mask = "^[mask:mobs_mc_empty.png\\^[fill\\:2x2\\:7,7\\:white" local end_rod_mask = "^[mask:mcl_end_end_rod_mask.png"
local side_mask = "^[mask:mobs_mc_empty.png\\^[fill\\:16x15\\:0,0\\:white"
for num, row in ipairs(colored_end_rods) do for num, row in ipairs(colored_end_rods) do
local name = row[1] local name = row[1]
local desc = row[2] local desc = row[2]
@ -168,16 +155,12 @@ for num, row in ipairs(colored_end_rods) do
def._doc_items_create_entry = false def._doc_items_create_entry = false
local side_tex local side_tex
if name == "pink" then if name == "pink" then
def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. top_mask .. "^[multiply:" .. name .. "^[hsl:0:300)" def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. end_rod_mask .. "^[multiply:" .. name .. "^[hsl:0:300)"
side_tex = end_rod_side_tex .. "^(" .. end_rod_side_tex .. side_mask .. "^[multiply:" .. name .. "^[hsl:0:300)"
elseif num > 4 then elseif num > 4 then
def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. top_mask .. "^[multiply:" .. name .. "^[hsl:0:300^[opacity:120)" def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. end_rod_mask .. "^[multiply:" .. name .. "^[hsl:0:300^[opacity:120)"
side_tex = end_rod_side_tex .. "^(" .. end_rod_side_tex .. side_mask .. "^[multiply:" .. name .. "^[hsl:0:300^[opacity:120)"
else else
def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. top_mask .. "^[multiply:" .. name .. "^[hsl:0:-100^[opacity:170)" def.tiles[1] = def.tiles[1] .. "^(" .. def.tiles[1] .. end_rod_mask .. "^[multiply:" .. name .. "^[hsl:0:-100^[opacity:170)"
side_tex = end_rod_side_tex .. "^(" .. end_rod_side_tex .. side_mask .. "^[multiply:" .. name .. "^[hsl:0:-100^[opacity:170)"
end end
for i=3, 6 do def.tiles[i] = side_tex end
minetest.register_node(end_rod_name.."_"..name, def) minetest.register_node(end_rod_name.."_"..name, def)
minetest.register_craft({ minetest.register_craft({
type = "shapeless", type = "shapeless",

View File

@ -0,0 +1,67 @@
# Blender 3.6.7
# www.blender.org
o Cube
v 0.125000 -0.437500 -0.125000
v 0.125000 -0.500000 -0.125000
v 0.125000 -0.437500 0.125000
v 0.125000 -0.500000 0.125000
v -0.125000 -0.437500 -0.125000
v -0.125000 -0.500000 -0.125000
v -0.125000 -0.437500 0.125000
v -0.125000 -0.500000 0.125000
v -0.062500 -0.437500 0.062500
v -0.062500 0.500000 0.062500
v -0.062500 -0.437500 -0.062500
v -0.062500 0.500000 -0.062500
v 0.062500 -0.437500 0.062500
v 0.062500 0.500000 0.062500
v 0.062500 -0.437500 -0.062500
v 0.062500 0.500000 -0.062500
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.250000 1.000000
vt -0.000000 1.000000
vt -0.000000 0.750000
vt 0.250000 0.750000
vt 0.250000 0.000000
vt 0.250000 0.062500
vt 0.000000 0.062500
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 0.062500
vt 0.750000 0.062500
vt 0.750000 0.000000
vt 0.000000 0.437500
vt 0.250000 0.437500
vt 0.250000 0.687500
vt 0.000000 0.687500
vt 0.500000 0.000000
vt 0.500000 0.062500
vt 1.000000 1.000000
vt 0.875000 1.000000
vt 0.875000 0.062500
vt 0.750000 1.000000
vt 0.625000 1.000000
vt 0.625000 0.062500
vt 0.500000 1.000000
vt 0.187500 0.812500
vt 0.187500 0.937500
vt 0.062500 0.937500
vt 0.062500 0.812500
s 0
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/6/2 7/7/2 8/8/2
f 8/9/3 7/10/3 5/11/3 6/12/3
f 6/13/4 2/14/4 4/15/4 8/16/4
f 2/17/5 1/18/5 3/6/5 4/5/5
f 6/12/6 5/11/6 1/18/6 2/17/6
f 9/10/3 10/19/3 12/20/3 11/21/3
f 11/21/6 12/20/6 16/22/6 15/11/6
f 15/11/5 16/22/5 14/23/5 13/24/5
f 13/24/2 14/23/2 10/25/2 9/18/2
f 11/26/4 15/27/4 13/28/4 9/29/4
f 16/27/1 12/28/1 10/29/1 14/26/1

View File

@ -183,6 +183,36 @@ def_fern.groups.compostability = 65
minetest.register_node("mcl_flowers:fern", def_fern) minetest.register_node("mcl_flowers:fern", def_fern)
--- Clover ---
-- Similar deal as fern, we can copy a lot from tall grass
local def_clover = table.copy(def_tallgrass)
def_clover.description = S("Clover")
def_clover._doc_items_longdesc = S("Clovers are small plants which occur naturally in plains and other temperate biomes. They can be picked up and planted again.")
def_clover.drawtype = "mesh"
def_clover.mesh = "mcl_clover_3leaf.obj"
def_clover.tiles = { "mcl_flowers_clover.png" }
def_clover.inventory_image = "mcl_flowers_clover_inv.png"
def_clover.wield_image = "mcl_flowers_clover_inv.png"
def_clover.drop = nil
def_clover.selection_box = {
type = "fixed",
fixed = { -4/16, -0.5, -4/16, 4/16, 0, 4/16 },
}
def_clover.groups.compostability = 30
minetest.register_node("mcl_flowers:clover", def_clover)
local def_4l_clover = table.copy(def_clover)
def_4l_clover.description = S("Four-leaf Clover")
def_4l_clover._doc_items_longdesc = S("Clovers are small plants which occur naturally in plains and other temperate biomes. They can be picked up and planted again.")
def_4l_clover.mesh = "mcl_clover_4leaf.obj"
def_4l_clover.tiles = { "mcl_flowers_fourleaf_clover.png" }
def_4l_clover.inventory_image = "mcl_flowers_fourleaf_clover_inv.png"
def_4l_clover.wield_image = "mcl_flowers_fourleaf_clover_inv.png"
minetest.register_node("mcl_flowers:fourleaf_clover", def_4l_clover)
if has_mcl_flowerpots then if has_mcl_flowerpots then
mcl_flowerpots.register_potted_flower("mcl_flowers:fern", { mcl_flowerpots.register_potted_flower("mcl_flowers:fern", {
name = "fern", name = "fern",

View File

@ -0,0 +1,38 @@
# Blender 3.6.7
# www.blender.org
mtllib mcl_clover_3leaf.mtl
o Plane
v 0.000000 -0.280000 0.000000
v 0.000000 -0.280000 0.000000
v -0.250000 -0.280000 -0.250000
v 0.250000 -0.280000 -0.250000
v 0.035355 -0.500000 0.035355
v 0.035355 -0.300000 0.035355
v -0.035355 -0.500000 -0.035355
v -0.035355 -0.300000 -0.035355
v 0.035355 -0.500000 -0.035355
v 0.035355 -0.300000 -0.035355
v -0.035355 -0.500000 0.035355
v -0.035355 -0.300000 0.035355
v -0.091506 -0.280000 0.341506
v -0.341506 -0.280000 -0.091506
v 0.000000 -0.280000 0.000000
v 0.341506 -0.280000 -0.091506
v 0.091506 -0.280000 0.341506
vn -0.0000 1.0000 -0.0000
vn -0.7071 -0.0000 0.7071
vn 0.7071 -0.0000 0.7071
vt 0.500000 0.437500
vt 1.000000 0.937500
vt 0.000000 0.937500
vt 0.437500 0.062500
vt 0.562500 0.062500
vt 0.562500 0.437500
vt 0.437500 0.437500
s 0
usemtl Material.001
f 2/1/1 4/2/1 3/3/1
f 5/4/2 6/5/2 8/6/2 7/7/2
f 9/4/3 10/5/3 12/6/3 11/7/3
f 1/1/1 14/2/1 13/3/1
f 15/1/1 17/2/1 16/3/1

View File

@ -0,0 +1,32 @@
# Blender 3.6.7
# www.blender.org
mtllib mcl_clover_4leaf.mtl
o Plane
v -0.250000 -0.280000 0.250000
v 0.250000 -0.280000 0.250000
v -0.250000 -0.280000 -0.250000
v 0.250000 -0.280000 -0.250000
v 0.035355 -0.500000 0.035355
v 0.035355 -0.300000 0.035355
v -0.035355 -0.500000 -0.035355
v -0.035355 -0.300000 -0.035355
v 0.035355 -0.500000 -0.035355
v 0.035355 -0.300000 -0.035355
v -0.035355 -0.500000 0.035355
v -0.035355 -0.300000 0.035355
vn -0.0000 1.0000 -0.0000
vn -0.7071 -0.0000 0.7071
vn 0.7071 -0.0000 0.7071
vt 0.000000 0.125000
vt 0.875000 0.125000
vt 0.875000 1.000000
vt 0.000000 1.000000
vt 0.875000 0.000000
vt 1.000000 0.000000
vt 1.000000 0.375000
vt 0.875000 0.375000
s 0
usemtl Material.001
f 1/1/1 2/2/1 4/3/1 3/4/1
f 5/5/2 6/6/2 8/7/2 7/8/2
f 9/5/3 10/6/3 12/7/3 11/8/3

View File

@ -399,6 +399,27 @@ minetest.register_craft({
} }
}) })
minetest.register_craft({
output = "mcl_mangrove:mangrove_wood 4",
recipe = {
{"mcl_mangrove:mangrove_tree_bark"},
}
})
minetest.register_craft({
output = "mcl_mangrove:mangrove_wood 4",
recipe = {
{"mcl_mangrove:mangrove_stripped"},
}
})
minetest.register_craft({
output = "mcl_mangrove:mangrove_wood 4",
recipe = {
{"mcl_mangrove:mangrove_stripped_bark"},
}
})
minetest.register_craft({ minetest.register_craft({
type = "fuel", type = "fuel",
recipe = "group:fence_wood", recipe = "group:fence_wood",

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,7 @@
--- DateTime: 10/14/22 4:05 PM --- DateTime: 10/14/22 4:05 PM
--- ---
--local logging = minetest.settings:get_bool("mcl_logging_mcl_signs",true)
local DEBUG = minetest.settings:get_bool("mcl_logging_mcl_signs", false) -- special debug setting. local DEBUG = minetest.settings:get_bool("mcl_logging_mcl_signs", false) -- special debug setting.
if DEBUG then if DEBUG then
minetest.log("action", "[mcl_signs] Signs API Loading") minetest.log("action", "[mcl_signs] Signs API Loading")
end end
@ -115,9 +112,6 @@ mcl_signs.registered_signs = {}
mcl_signs.registered_signs.wall_signs = {} mcl_signs.registered_signs.wall_signs = {}
mcl_signs.registered_signs.standing_signs = {} mcl_signs.registered_signs.standing_signs = {}
mcl_signs.registered_signs.hanging_signs = {} -- unused. prepping for future use. mcl_signs.registered_signs.hanging_signs = {} -- unused. prepping for future use.
-- DEFINE SIGN BASE TYPES
mcl_signs.wall_standard = {} -- initialize
mcl_signs.standing_standard = {} -- initialize
function mcl_signs.build_signs_info() function mcl_signs.build_signs_info()
local n = 23 / 56 - 1 / 128 -- some required magic number from the original code. local n = 23 / 56 - 1 / 128 -- some required magic number from the original code.
@ -141,37 +135,110 @@ function mcl_signs.build_signs_info()
end end
-- wall signs' & hanging signs' base (definition) -- DEFINE SIGN BASE TYPES
mcl_signs.wall_standard = { local common_definition = {
description = S("Sign"), _mcl_hardness = 1,
_tt_help = S("Can be written"), _mcl_blast_resistance = 1,
_doc_items_longdesc = S("Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them."),
_doc_items_usagehelp = S("After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again. Can be colored and made to glow."),
inventory_image = "mcl_signs_default_sign.png",
walkable = false,
is_ground_content = false,
wield_image = "mcl_signs_default_sign.png",
node_placement_prediction = "",
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "wallmounted",
drawtype = "mesh",
mesh = "mcl_signs_signonwallmount.obj",
selection_box = { type = "wallmounted", wall_side = { -0.5, -7 / 28, -0.5, -23 / 56, 7 / 28, 0.5 } },
tiles = { "mcl_signs_sign.png" },
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
groups = mcl_signs.sign_groups,
stack_max = 16, stack_max = 16,
sounds = node_sounds, sounds = node_sounds,
groups = mcl_signs.sign_groups,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
drawtype = "mesh",
paramtype = "light",
tiles = { "mcl_signs_sign.png" },
sunlight_propagates = true,
walkable = false,
is_ground_content = false,
on_timer = function(pos) on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Right Click event.")
end
-- make sure player is clicking
if not clicker or not clicker:is_player() then
return
end
local item = clicker:get_wielded_item()
local iname = item:get_name()
local protected = mcl_util.check_position_protection(pos, clicker)
if node and not protected then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Right Click event on valid node.")
end
-- handle glow from glow_ink_sac *first*
if (iname == "mcl_mobitems:glow_ink_sac") then
clicker:set_wielded_item(item)
local success = mcl_signs:glow_sign(pos)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Glow Success.")
end
itemstack:take_item()
end
return
end
-- check the wielded item to make sure that it is a dye.
local txt_color = mcl_signs:get_color_for_sign(iname)
if txt_color ~= "false" then
clicker:set_wielded_item(item)
local success = mcl_signs:color_sign(pos, txt_color)
-- "mcl_dye:black" is a special case: it makes the sign's lettering black AND removes glow.
if (iname == "mcl_dye:black") then
success = mcl_signs:glow_sign(pos, true)
if success and DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Glow removal Success.")
end
end
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Color Success.")
end
itemstack:take_item()
end
return
end
-- No modifier item in hand, open the sign for edition
local old_text = minetest.get_meta(pos):get_string("text")
mcl_signs:show_formspec(clicker, pos, old_text)
end
end,
on_destruct = function(pos)
mcl_signs:destruct_sign(pos)
end,
-- Not Useless Code. this force updates the sign.
on_punch = function(pos, node, puncher)
mcl_signs:update_sign(pos)
end,
}
-- wall signs' & hanging signs' base (definition)
mcl_signs.wall_standard = table.copy(common_definition)
mcl_signs.wall_standard.description = S("Sign")
mcl_signs.wall_standard._tt_help = S("Can be written")
mcl_signs.wall_standard._doc_items_longdesc = S("Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them.")
mcl_signs.wall_standard._doc_items_usagehelp = S("After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again. Can be colored and made to glow.")
mcl_signs.wall_standard.inventory_image = "mcl_signs_default_sign.png"
mcl_signs.wall_standard.wield_image = "mcl_signs_default_sign.png"
mcl_signs.wall_standard.node_placement_prediction = ""
mcl_signs.wall_standard.paramtype2 = "wallmounted"
mcl_signs.wall_standard.mesh = "mcl_signs_signonwallmount.obj"
mcl_signs.wall_standard.selection_box = { type = "wallmounted", wall_side = { -0.5, -7 / 28, -0.5, -23 / 56, 7 / 28, 0.5 } }
mcl_signs.wall_standard.on_timer = function(pos)
-- fix for /ClearObjects -- fix for /ClearObjects
mcl_signs:update_sign(pos) mcl_signs:update_sign(pos)
-- note: update_sign decides to keep the timer running based on if there is text. -- note: update_sign decides to keep the timer running based on if there is text.
-- This prevents every sign from having a timer, when not needed. -- This prevents every sign from having a timer, when not needed.
end, end
mcl_signs.wall_standard.on_place = function(itemstack, placer, pointed_thing)
on_place = function(itemstack, placer, pointed_thing)
local above = pointed_thing.above local above = pointed_thing.above
local under = pointed_thing.under local under = pointed_thing.under
@ -192,9 +259,6 @@ mcl_signs.wall_standard = {
end end
local wdir = minetest.dir_to_wallmounted(dir) local wdir = minetest.dir_to_wallmounted(dir)
--local placer_pos = placer:get_pos()
local fdir = minetest.dir_to_facedir(dir) local fdir = minetest.dir_to_facedir(dir)
local sign_info local sign_info
@ -272,18 +336,10 @@ mcl_signs.wall_standard = {
minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true) minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)
mcl_signs:show_formspec(placer, place_pos) mcl_signs:show_formspec(placer, place_pos, "")
return itemstack return itemstack
end, end
on_destruct = function(pos) mcl_signs.wall_standard.on_rotate = function(pos, node, user, mode)
mcl_signs:destruct_sign(pos)
end,
-- Not Useless Code. force updates the sign.
on_punch = function(pos, node, puncher)
mcl_signs:update_sign(pos)
end,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_FACE then if mode == screwdriver.ROTATE_FACE then
local r = screwdriver.rotate.wallmounted(pos, node, mode) local r = screwdriver.rotate.wallmounted(pos, node, mode)
node.param2 = r node.param2 = r
@ -293,105 +349,21 @@ mcl_signs.wall_standard = {
else else
return false return false
end end
end, end
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if DEBUG then
minetest.log("verbose", "[mcl_signs] Wall_Sign Right Click event.")
end
-- make sure player is clicking
if not clicker or not clicker:is_player() then
return
end
local item = clicker:get_wielded_item()
local iname = item:get_name()
local protected = mcl_util.check_position_protection(pos, clicker)
if node and not protected then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Wall_Sign Right Click event on valid node.")
end
-- handle glow from glow_ink_sac *first*
if (iname == "mcl_mobitems:glow_ink_sac") then
clicker:set_wielded_item(item)
local success = mcl_signs:glow_sign(pos)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Glow Success.")
end
itemstack:take_item()
end
return
end
-- "mcl_dye:black" is a special case: it makes the sign's lettering black AND removes glow.
if (iname == "mcl_dye:black") then
clicker:set_wielded_item(item)
local success = mcl_signs:glow_sign(pos, true)
mcl_signs:color_sign(pos, mcl_colors.BLACK)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Glow removal Success.")
end
itemstack:take_item()
end
return
end
-- check the wielded item to make sure that it is a dye.
local txt_color = mcl_signs:get_color_for_sign(iname)
if txt_color ~= "false" then
clicker:set_wielded_item(item)
local success = mcl_signs:color_sign(pos, txt_color)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Color Success.")
end
itemstack:take_item()
end
end
end
end,
_mcl_hardness = 1,
_mcl_blast_resistance = 1,
}
-- standing sign base (definition) -- standing sign base (definition)
mcl_signs.standing_standard = { mcl_signs.standing_standard = table.copy(common_definition)
paramtype = "light", mcl_signs.standing_standard.paramtype2 = "facedir"
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false, mcl_signs.standing_standard.mesh = "mcl_signs_sign.obj"
sunlight_propagates = true, mcl_signs.standing_standard.selection_box = { type = "fixed", fixed = { -0.2, -0.5, -0.2, 0.2, 0.5, 0.2 } }
walkable = false, mcl_signs.standing_standard.drop = "mcl_signs:wall_sign"
is_ground_content = false, mcl_signs.standing_standard.on_timer = function(pos)
paramtype2 = "facedir",
drawtype = "mesh",
mesh = "mcl_signs_sign.obj",
selection_box = { type = "fixed", fixed = { -0.2, -0.5, -0.2, 0.2, 0.5, 0.2 } },
tiles = { "mcl_signs_sign.png" },
groups = mcl_signs.sign_groups,
drop = "mcl_signs:wall_sign",
stack_max = 16,
sounds = node_sounds,
on_destruct = function(pos)
mcl_signs:destruct_sign(pos)
end,
on_timer = function(pos)
-- fix for /ClearObjects -- fix for /ClearObjects
mcl_signs:update_sign(pos) mcl_signs:update_sign(pos)
minetest.get_node_timer(pos):start(40.0) minetest.get_node_timer(pos):start(40.0)
end, end
mcl_signs.standing_standard.on_rotate = function(pos, node, user, mode)
-- Not Useless Code. this force updates the sign.
on_punch = function(pos, node, puncher)
mcl_signs:update_sign(pos)
end,
on_rotate = function(pos, node, user, mode)
if mode == screwdriver.ROTATE_FACE then if mode == screwdriver.ROTATE_FACE then
node.name = "mcl_signs:standing_sign22_5" node.name = "mcl_signs:standing_sign22_5"
minetest.swap_node(pos, node) minetest.swap_node(pos, node)
@ -400,60 +372,7 @@ mcl_signs.standing_standard = {
end end
mcl_signs:update_sign(pos, nil, nil, true) mcl_signs:update_sign(pos, nil, nil, true)
return true return true
end, end
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if DEBUG then
minetest.log("verbose", "[mcl_signs] Standing_Sign Right Click event.")
end
-- make sure player is clicking
if not clicker or not clicker:is_player() then
return
end
local item = clicker:get_wielded_item()
local iname = item:get_name()
local protected = mcl_util.check_position_protection(pos, clicker)
if node and not protected then
-- handle glow from glow_ink_sac *first*
if DEBUG then
minetest.log("verbose", "[mcl_signs] Standing_Sign Right Click event on valid node.")
end
if (iname == "mcl_mobitems:glow_ink_sac") then
clicker:set_wielded_item(item)
local success = mcl_signs:glow_sign(pos)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Glow Success.")
end
itemstack:take_item()
end
return
end
-- check the wielded item to make sure that it is a dye.
local txt_color = mcl_signs:get_color_for_sign(iname)
if txt_color ~= "false" then
clicker:set_wielded_item(item)
local success = mcl_signs:color_sign(pos, txt_color)
if success then
if DEBUG then
minetest.log("verbose", "[mcl_signs] Sign Color Success.")
end
itemstack:take_item()
end
end
end
end,
_mcl_hardness = 1,
_mcl_blast_resistance = 1,
}
-- HELPER FUNCTIONS' VARIABLES -- HELPER FUNCTIONS' VARIABLES
local sign_glow = 6 local sign_glow = 6
@ -663,7 +582,7 @@ function mcl_signs.register_sign (modname, color, _name, ttsign)
minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true) minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)
mcl_signs:show_formspec(placer, place_pos) mcl_signs:show_formspec(placer, place_pos, "")
return itemstack return itemstack
end end
@ -916,7 +835,7 @@ function mcl_signs.register_sign_custom (modname, _name, tiles, color, inventory
minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true) minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)
mcl_signs:show_formspec(placer, place_pos) mcl_signs:show_formspec(placer, place_pos, "")
return itemstack return itemstack
end end
minetest.register_node(":mcl_signs:wall_sign" .. _name, new_sign) minetest.register_node(":mcl_signs:wall_sign" .. _name, new_sign)
@ -1152,7 +1071,7 @@ function mcl_signs.reregister_sign (modname, color, _name, ttsign)
minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true) minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)
mcl_signs:show_formspec(placer, place_pos) mcl_signs:show_formspec(placer, place_pos, "")
return itemstack return itemstack
end end
@ -1401,7 +1320,7 @@ function mcl_signs.reregister_sign_custom (modname, _name, tiles, color, invento
minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true) minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)
mcl_signs:show_formspec(placer, place_pos) mcl_signs:show_formspec(placer, place_pos, "")
return itemstack return itemstack
end end
minetest.override_item("mcl_signs:wall_sign" .. _name, new_sign) minetest.override_item("mcl_signs:wall_sign" .. _name, new_sign)
@ -1883,7 +1802,7 @@ function mcl_signs:update_sign(pos, fields, sender, force_remove, text_color)
return false return false
end end
local text = meta:get_string("text", "") local text = meta:get_string("text", "")
if fields and (text == "" and fields.text) then if fields and fields.text then
meta:set_string("text", fields.text) meta:set_string("text", fields.text)
text = fields.text text = fields.text
end end
@ -2040,11 +1959,13 @@ function mcl_signs:update_sign(pos, fields, sender, force_remove, text_color)
end end
function mcl_signs:show_formspec(player, pos) function mcl_signs:show_formspec(player, pos, old_text)
minetest.show_formspec( minetest.show_formspec(
player:get_player_name(), player:get_player_name(),
"mcl_signs:set_text_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z, "mcl_signs:set_text_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z,
"size[6,3]textarea[0.25,0.25;6,1.5;text;" .. F(S("Enter sign text:")) .. ";]label[0,1.5;" .. F(S("Maximum line length: 15")) .. "\n" .. F(S("Maximum lines: 4")) .. "]button_exit[0,2.5;6,1;submit;" .. F(S("Done")) .. "]" "size[6,3]textarea[0.25,0.25;6,1.5;text;" .. F(S("Enter sign text:")) .. ";".. F(old_text) .. "]" ..
"label[0,1.5;" .. F(S("Maximum line length: 15")) ..
"\n" .. F(S("Maximum lines: 4")) .. "]button_exit[0,2.5;6,1;submit;" .. F(S("Done")) .. "]"
) )
end end

View File

@ -5186,7 +5186,7 @@ local function register_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
decoration = "mcl_farming:melon", decoration = "mcl_farming:melon",
biomes = {"Jungle"}, biomes = {"Jungle", "BambooJungle"},
}) })
minetest.register_decoration({ minetest.register_decoration({
deco_type = "simple", deco_type = "simple",
@ -5203,7 +5203,7 @@ local function register_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
decoration = "mcl_farming:melon", decoration = "mcl_farming:melon",
biomes = {"JungleM"}, biomes = {"JungleM", "BambooJungleM"},
}) })
minetest.register_decoration({ minetest.register_decoration({
deco_type = "simple", deco_type = "simple",
@ -5220,7 +5220,7 @@ local function register_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
decoration = "mcl_farming:melon", decoration = "mcl_farming:melon",
biomes = {"JungleEdge", "JungleEdgeM"}, biomes = {"JungleEdge", "JungleEdgeM", "BambooJungleEdge", "BambooJungleEdgeM"},
}) })
-- Lots of melons in Jungle Edge M -- Lots of melons in Jungle Edge M
@ -5260,6 +5260,7 @@ local function register_decorations()
}, },
y_min = 1, y_min = 1,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
biomes = {"ExtremeHills", "ExtremeHillsM", "ExtremeHills+", "Taiga", "MegaTaiga", "MegaSpruceTaiga", "Plains", "SunflowerPlains", "Swampland", "MangroveSwamp"},
}) })
-- Grasses and ferns -- Grasses and ferns
@ -5589,7 +5590,7 @@ local function register_decorations()
num_spawn_by = 1, num_spawn_by = 1,
}) })
end end
local function register_flower(name, biomes, seed, is_in_flower_forest) local function register_flower(name, biomes, seed, is_in_flower_forest, custom_rarity_mod)
if is_in_flower_forest == nil then if is_in_flower_forest == nil then
is_in_flower_forest = true is_in_flower_forest = true
end end
@ -5599,7 +5600,7 @@ local function register_decorations()
place_on = {"group:grass_block_no_snow", "mcl_core:dirt"}, place_on = {"group:grass_block_no_snow", "mcl_core:dirt"},
sidelen = 16, sidelen = 16,
noise_params = { noise_params = {
offset = 0.0008, offset = 0.0008 + (custom_rarity_mod or 0),
scale = 0.006, scale = 0.006,
spread = {x = 100, y = 100, z = 100}, spread = {x = 100, y = 100, z = 100},
seed = seed, seed = seed,
@ -5651,6 +5652,9 @@ local function register_decorations()
register_flower("lily_of_the_valley", nil, 325) register_flower("lily_of_the_valley", nil, 325)
register_flower("cornflower", flower_biomes2, 486) register_flower("cornflower", flower_biomes2, 486)
register_flower("clover", flower_biomes1, 3, false, 0.04)
register_flower("fourleaf_clover", flower_biomes1, 13, false, -0.002)
end end
-- Decorations in non-Overworld dimensions -- Decorations in non-Overworld dimensions

View File

@ -266,7 +266,7 @@ minetest.register_globalstep(function(dtime)
food_tick_timers[player] = food_tick_timer -- update food_tick_timer table food_tick_timers[player] = food_tick_timer -- update food_tick_timer table
-- Eating delay code -- Eating delay code
if mcl_hunger.eat_internal[player_name].is_eating or mcl_hunger.eat_internal[player_name]._custom_do_delayed then if mcl_hunger.eat_internal[player_name] and mcl_hunger.eat_internal[player_name].is_eating or mcl_hunger.eat_internal[player_name]._custom_do_delayed then
mcl_hunger.eat_internal[player_name].is_eating = true mcl_hunger.eat_internal[player_name].is_eating = true
mcl_hunger.eat_internal[player_name].is_eating_no_padding = true mcl_hunger.eat_internal[player_name].is_eating_no_padding = true

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

View File

@ -338,17 +338,17 @@ Source path,Source file,Target file,xs,ys,xl,yl,xt,yt,Blacklisted?
/assets/minecraft/textures/block,mycelium_top.png,mcl_core_mycelium_top.png,,,,,,, /assets/minecraft/textures/block,mycelium_top.png,mcl_core_mycelium_top.png,,,,,,,
/assets/minecraft/textures/block,red_sand.png,mcl_core_red_sand.png,,,,,,, /assets/minecraft/textures/block,red_sand.png,mcl_core_red_sand.png,,,,,,,
/assets/minecraft/textures/block,red_sandstone_bottom.png,mcl_core_red_sandstone_bottom.png,,,,,,, /assets/minecraft/textures/block,red_sandstone_bottom.png,mcl_core_red_sandstone_bottom.png,,,,,,,
/assets/minecraft/textures/block,cut_red_sandstone.png,mcl_core_red_sandstone_carved.png,,,,,,, /assets/minecraft/textures/block,cut_red_sandstone.png,mcl_core_red_sandstone_smooth.png,,,,,,,
/assets/minecraft/textures/block,red_sandstone.png,mcl_core_red_sandstone_normal.png,,,,,,, /assets/minecraft/textures/block,red_sandstone.png,mcl_core_red_sandstone_normal.png,,,,,,,
/assets/minecraft/textures/block,chiseled_red_sandstone.png,mcl_core_red_sandstone_smooth.png,,,,,,, /assets/minecraft/textures/block,chiseled_red_sandstone.png,mcl_core_red_sandstone_carved.png,,,,,,,
/assets/minecraft/textures/block,red_sandstone_top.png,mcl_core_red_sandstone_top.png,,,,,,, /assets/minecraft/textures/block,red_sandstone_top.png,mcl_core_red_sandstone_top.png,,,,,,,
/assets/minecraft/textures/block,redstone_ore.png,mcl_core_redstone_ore.png,,,,,,, /assets/minecraft/textures/block,redstone_ore.png,mcl_core_redstone_ore.png,,,,,,,
/assets/minecraft/textures/item,sugar_cane.png,mcl_core_reeds.png,,,,,,, /assets/minecraft/textures/item,sugar_cane.png,mcl_core_reeds.png,,,,,,,
/assets/minecraft/textures/block,sandstone_bottom.png,mcl_core_sandstone_bottom.png,,,,,,, /assets/minecraft/textures/block,sandstone_bottom.png,mcl_core_sandstone_bottom.png,,,,,,,
/assets/minecraft/textures/block,cut_sandstone.png,mcl_core_sandstone_carved.png,,,,,,,
/assets/minecraft/textures/block,chiseled_sandstone.png,mcl_core_sandstone_normal.png,,,,,,,
/assets/minecraft/textures/block,cut_sandstone.png,mcl_core_sandstone_smooth.png,,,,,,, /assets/minecraft/textures/block,cut_sandstone.png,mcl_core_sandstone_smooth.png,,,,,,,
/assets/minecraft/textures/block,sandstone.png,mcl_core_sandstone_top.png,,,,,,, /assets/minecraft/textures/block,chiseled_sandstone.png,mcl_core_sandstone_carved.png,,,,,,,
/assets/minecraft/textures/block,sandstone.png,mcl_core_sandstone_normal.png,,,,,,,
/assets/minecraft/textures/block,sandstone_top.png,mcl_core_sandstone_top.png,,,,,,,
/assets/minecraft/textures/block,slime_block.png,mcl_core_slime.png,,,,,,, /assets/minecraft/textures/block,slime_block.png,mcl_core_slime.png,,,,,,,
/assets/minecraft/textures/block,smooth_stone.png,mcl_core_stonebrick_carved.png,,,,,,, /assets/minecraft/textures/block,smooth_stone.png,mcl_core_stonebrick_carved.png,,,,,,,
/assets/minecraft/textures/block,cracked_stone_bricks.png,mcl_core_stonebrick_cracked.png,,,,,,, /assets/minecraft/textures/block,cracked_stone_bricks.png,mcl_core_stonebrick_cracked.png,,,,,,,

1 Source path Source file Target file xs ys xl yl xt yt Blacklisted?
338 /assets/minecraft/textures/block mycelium_top.png mcl_core_mycelium_top.png
339 /assets/minecraft/textures/block red_sand.png mcl_core_red_sand.png
340 /assets/minecraft/textures/block red_sandstone_bottom.png mcl_core_red_sandstone_bottom.png
341 /assets/minecraft/textures/block cut_red_sandstone.png mcl_core_red_sandstone_carved.png mcl_core_red_sandstone_smooth.png
342 /assets/minecraft/textures/block red_sandstone.png mcl_core_red_sandstone_normal.png
343 /assets/minecraft/textures/block chiseled_red_sandstone.png mcl_core_red_sandstone_smooth.png mcl_core_red_sandstone_carved.png
344 /assets/minecraft/textures/block red_sandstone_top.png mcl_core_red_sandstone_top.png
345 /assets/minecraft/textures/block redstone_ore.png mcl_core_redstone_ore.png
346 /assets/minecraft/textures/item sugar_cane.png mcl_core_reeds.png
347 /assets/minecraft/textures/block sandstone_bottom.png mcl_core_sandstone_bottom.png
/assets/minecraft/textures/block cut_sandstone.png mcl_core_sandstone_carved.png
/assets/minecraft/textures/block chiseled_sandstone.png mcl_core_sandstone_normal.png
348 /assets/minecraft/textures/block cut_sandstone.png mcl_core_sandstone_smooth.png
349 /assets/minecraft/textures/block sandstone.png chiseled_sandstone.png mcl_core_sandstone_top.png mcl_core_sandstone_carved.png
350 /assets/minecraft/textures/block sandstone.png mcl_core_sandstone_normal.png
351 /assets/minecraft/textures/block sandstone_top.png mcl_core_sandstone_top.png
352 /assets/minecraft/textures/block slime_block.png mcl_core_slime.png
353 /assets/minecraft/textures/block smooth_stone.png mcl_core_stonebrick_carved.png
354 /assets/minecraft/textures/block cracked_stone_bricks.png mcl_core_stonebrick_cracked.png