diff --git a/docs/contributing.rst b/docs/contributing.rst index 329c532b3..bbbd493cc 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -127,10 +127,10 @@ Let's take a closer look at the ``overviewer_core/`` directory: * ``signals.py`` is multiprocessing communication code. Scary stuff. -* ``textures.py`` contains all the block definitions and how Overviewer should - render them. If you want to add a new block to the Overviewer, this is where - you'll want to do it. Additionally, this code also controls how the textures - are loaded. +* ``textures.py`` contains the custom block definitions and how Overviewer + should render them. If a block is not rendering properly in Overviewer, + this is where you'll want to fix it. Additionally, this code also controls + how the textures are loaded. * ``tileset.py`` contains code that maps a render dict entry to the output tiled image structure. @@ -138,8 +138,10 @@ Let's take a closer look at the ``overviewer_core/`` directory: * ``util.py`` contains random utility code that has no home anywhere else. * ``world.py`` is a whole lot of code that does things like choosing which - chunks to load and to cache, and general functionality revolving around the - concept of Minecraft worlds. + chunks to load and to cache, how to interpret block properties, and + general functionality revolving around the concept of Minecraft worlds. + If a block is not behaving like you expect this is probably where + you'll fix that. docs ---- @@ -184,8 +186,8 @@ Adding a Block Let's assume you want to add support for a new block to the Overviewer. This is probably one of the most common ways people start contributing to the project, -as all blocks in the Overviewer are currently hardcoded and code to handle them -needs to be added by hand. +as anything but the simplest blocks in the Overviewer are currently hardcoded +and code to handle them needs to be added by hand. The place to look here is ``textures.py``. It contains the block definitions, which are assisted by Python decorators_, which make it quite a bit simpler to @@ -202,46 +204,26 @@ as ``solid=True`` to indicate that the block is a solid block. Simple Solid 6-Sided Block ~~~~~~~~~~~~~~~~~~~~~~~~~~ -A lot of times, new blocks are basically just your standard full-height block -with a new texture. For a block this simple, we don't even really need to use -the material decorator. As an example, check out the definition of the coal -block:: +Most blocks are simple full height solid blocks. These blocks are automatically +picked up by the ``unbound_models()`` method from the minecraft assets. +Sometimes these blocks have special properties that can not be picked up +from the assets, like nospawn, or because you want to include them in an overview. +These blocks can be added like this:: - block(blockid=173, top_image="assets/minecraft/textures/blocks/coal_block.png") - -Block with a Different Top -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Another common theme is a block where the top is a different texture than the -sides. Here we use the ``@material`` decorator to create the jukebox block:: - - @material(blockid=84, data=range(16), solid=True) - def jukebox(self, blockid, data): - return self.build_block(self.load_image_texture("assets/minecraft/textures/blocks/jukebox_top.png"), self.load_image_texture("assets/minecraft/textures/blocks/noteblock.png")) - -As you can see, we define a method called ``jukebox``, taking the parameters -``blockid`` and ``data``, decorated by a decorator stating that the following -definition is a material with a ``blockid`` of ``84`` and a data value range -from ``0`` to ``15`` (or ``range(16)``), which we won't use as it doesn't affect -the rendering of the block. We also specify that the block is solid. - -Inside the method, we then return the return value of ``self.build_block()``, -which is a helper method that takes a texture for the top and a texture for the -side as its arguments. + solidmodelblock(blockid=1125, name="mangrove_roots") Block with Variable Colors ~~~~~~~~~~~~~~~~~~~~~~~~~~ Occasionally, blocks can have colors stored in their data values. ``textures.py`` includes an easy mapping list, called ``color_map``, to map -between data values and Minecraft color names. Let's take stained hardened clay +between data values and Minecraft color names. Let's take carpet as an example of how this is used:: - @material(blockid=159, data=range(16), solid=True) - def stained_clay(self, blockid, data): - texture = self.load_image_texture("assets/minecraft/textures/blocks/hardened_clay_stained_%s.png" % color_map[data]) - - return self.build_block(texture,texture) + @material(blockid=171, data=list(range(17)), transparent=True) + def carpet(self, blockid, data): + if data < 16: + texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data]) As you can see, we specify that the block has 16 data values, then depending on the data value we load the right block texture by looking up the color name diff --git a/overviewer_core/src/block_class.c b/overviewer_core/src/block_class.c index 27299160c..8df171843 100644 --- a/overviewer_core/src/block_class.c +++ b/overviewer_core/src/block_class.c @@ -150,7 +150,9 @@ const mc_block_t block_class_stair[] = { block_cobbled_deepslate_stairs, block_polished_deepslate_stairs, block_deepslate_brick_stairs, - block_deepslate_tile_stairs}; + block_deepslate_tile_stairs, + block_mangrove_stairs, + block_mud_brick_stairs}; const size_t block_class_stair_len = COUNT_OF(block_class_stair); const mc_block_t block_class_door[] = { @@ -162,6 +164,7 @@ const mc_block_t block_class_door[] = { block_acacia_door, block_dark_oak_door, block_crimson_door, + block_mangrove_door, block_warped_door}; const size_t block_class_door_len = COUNT_OF(block_class_door); @@ -174,6 +177,7 @@ const mc_block_t block_class_ancil[] = { block_acacia_door, block_dark_oak_door, block_crimson_door, + block_mangrove_door, block_oak_stairs, block_brick_stairs, block_stone_brick_stairs, @@ -222,6 +226,8 @@ const mc_block_t block_class_ancil[] = { block_polished_deepslate_stairs, block_deepslate_brick_stairs, block_deepslate_tile_stairs, + block_mangrove_stairs, + block_mud_brick_stairs, block_grass, block_flowing_water, block_water, @@ -252,7 +258,8 @@ const mc_block_t block_class_ancil[] = { block_cobbled_deepslate_wall, block_polished_deepslate_wall, block_deepslate_brick_wall, - block_deepslate_tile_wall}; + block_deepslate_tile_wall, + block_mud_brick_wall}; const size_t block_class_ancil_len = COUNT_OF(block_class_ancil); const mc_block_t block_class_alt_height[] = { @@ -291,7 +298,8 @@ const mc_block_t block_class_alt_height[] = { block_smooth_sandstone_stairs, block_blackstone_stairs, block_polished_blackstone_stairs, - block_polished_blackstone_brick_stairs, + block_polished_blackstone_brick_stairs, + block_mud_brick_stairs, block_prismarine_slab, block_dark_prismarine_slab, block_prismarine_brick_slab, @@ -337,12 +345,14 @@ const mc_block_t block_class_alt_height[] = { block_cobbled_deepslate_stairs, block_polished_deepslate_stairs, block_deepslate_brick_stairs, - block_deepslate_tile_stairs, + block_deepslate_tile_stairs, + block_mangrove_stairs, block_cobbled_deepslate_slab, block_polished_deepslate_slab, block_deepslate_brick_slab, block_deepslate_tile_slab, - block_mud_brick_slab}; + block_mud_brick_slab, + block_mangrove_slab}; const size_t block_class_alt_height_len = COUNT_OF(block_class_alt_height); const mc_block_t block_class_nether_roof[] = { diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index b3251e92c..b509eceb1 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -26,11 +26,11 @@ uint32_t max_data = 0; uint8_t* block_properties = NULL; static PyObject* known_blocks = NULL; +static PyObject* model_blocks = NULL; static PyObject* transparent_blocks = NULL; static PyObject* solid_blocks = NULL; static PyObject* fluid_blocks = NULL; static PyObject* nospawn_blocks = NULL; -static PyObject* nodata_blocks = NULL; PyObject* init_chunk_render(void) { @@ -64,6 +64,9 @@ PyObject* init_chunk_render(void) { known_blocks = PyObject_GetAttrString(textures, "known_blocks"); if (!known_blocks) return NULL; + model_blocks = PyObject_GetAttrString(textures, "block_models"); + if (!model_blocks) + return NULL; transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; @@ -76,9 +79,6 @@ PyObject* init_chunk_render(void) { nospawn_blocks = PyObject_GetAttrString(textures, "nospawn_blocks"); if (!nospawn_blocks) return NULL; - nodata_blocks = PyObject_GetAttrString(textures, "nodata_blocks"); - if (!nodata_blocks) - return NULL; block_properties = calloc(max_blockid, sizeof(uint8_t)); for (i = 0; i < max_blockid; i++) { @@ -94,8 +94,6 @@ PyObject* init_chunk_render(void) { block_properties[i] |= 1 << FLUID; if (PySequence_Contains(nospawn_blocks, block)) block_properties[i] |= 1 << NOSPAWN; - if (PySequence_Contains(nodata_blocks, block)) - block_properties[i] |= 1 << NODATA; Py_DECREF(block); } @@ -143,6 +141,8 @@ bool load_chunk(RenderState* state, int32_t x, int32_t z, uint8_t required) { x += state->chunkx; z += state->chunkz; + PyObject_CallMethod(state->regionset, "add_to_blockmap","O", model_blocks); + chunk = PyObject_CallMethod(state->regionset, "get_chunk", "ii", x, z); if (chunk == NULL) { // An exception is already set. RegionSet.get_chunk sets @@ -269,7 +269,7 @@ generate_pseudo_data(RenderState* state, uint16_t ancilData) { * Note that stained glass encodes 16 colors using 4 bits. this pushes us over the 8-bits of an uint8_t, * forcing us to use an uint16_t to hold 16 bits of pseudo ancil data * */ - if ((get_data(state, BLOCKS, x, y + 1, z) == 20) || (get_data(state, BLOCKS, x, y + 1, z) == 95)) { + if ((get_data(state, BLOCKS, x, y + 1, z) == block_glass) || (get_data(state, BLOCKS, x, y + 1, z) == block_stained_glass)) { data = 0; } else { data = 16; @@ -284,15 +284,15 @@ generate_pseudo_data(RenderState* state, uint16_t ancilData) { uint8_t above_level_data = 0, same_level_data = 0, below_level_data = 0, possibly_connected = 0, final_data = 0; /* check for air in y+1, no air = no connection with upper level */ - if (get_data(state, BLOCKS, x, y + 1, z) == 0) { + if (get_data(state, BLOCKS, x, y + 1, z) == block_air) { above_level_data = check_adjacent_blocks(state, x, y + 1, z, state->block); } /* else above_level_data = 0 */ /* check connection with same level (other redstone and trapped chests */ - same_level_data = check_adjacent_blocks(state, x, y, z, 55) | check_adjacent_blocks(state, x, y, z, 146); + same_level_data = check_adjacent_blocks(state, x, y, z, block_redstone_wire) | check_adjacent_blocks(state, x, y, z, block_trapped_chest); /* check the posibility of connection with y-1 level, check for air */ - possibly_connected = check_adjacent_blocks(state, x, y, z, 0); + possibly_connected = check_adjacent_blocks(state, x, y, z, block_air ); /* check connection with y-1 level */ below_level_data = check_adjacent_blocks(state, x, y - 1, z, state->block); @@ -601,27 +601,18 @@ chunk_render(PyObject* self, PyObject* args) { continue; } - /* everything stored here will be a borrowed ref */ - - if (block_has_property(state.block, NODATA)) { - /* block shouldn't have data associated with it, set it to 0 */ - ancilData = 0; - state.block_data = 0; - state.block_pdata = 0; + /* block has associated data, use it */ + ancilData = getArrayByte3D(state.blockdatas, state.x, state.y, state.z); + state.block_data = ancilData; + /* block that need pseudo ancildata: + * grass, water, glass, chest, restone wire, + * ice, portal, iron bars, + * trapped chests, stairs */ + if (block_class_is_subset(state.block, block_class_ancil, block_class_ancil_len)) { + ancilData = generate_pseudo_data(&state, ancilData); + state.block_pdata = ancilData; } else { - /* block has associated data, use it */ - ancilData = getArrayByte3D(state.blockdatas, state.x, state.y, state.z); - state.block_data = ancilData; - /* block that need pseudo ancildata: - * grass, water, glass, chest, restone wire, - * ice, portal, iron bars, - * trapped chests, stairs */ - if (block_class_is_subset(state.block, block_class_ancil, block_class_ancil_len)) { - ancilData = generate_pseudo_data(&state, ancilData); - state.block_pdata = ancilData; - } else { - state.block_pdata = 0; - } + state.block_pdata = 0; } /* make sure our block info is in-bounds */ @@ -637,7 +628,7 @@ chunk_render(PyObject* self, PyObject* args) { /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { PyObject *src, *mask, *mask_light; - int32_t do_rand = (state.block == block_tallgrass /*|| state.block == block_red_flower || state.block == block_double_plant*/); + int32_t do_rand = (state.block == block_tallgrass); int32_t randx = 0, randy = 0; src = PyTuple_GetItem(t, 0); mask = PyTuple_GetItem(t, 0); diff --git a/overviewer_core/src/mc_id.h b/overviewer_core/src/mc_id.h index dd3126fc6..f0e6d413a 100644 --- a/overviewer_core/src/mc_id.h +++ b/overviewer_core/src/mc_id.h @@ -3,21 +3,18 @@ #include +// enum to keep track of blocks that have special rendering effects + enum mc_block_id { block_air = 0, - block_stone = 1, block_grass = 2, - block_dirt = 3, block_cobblestone = 4, - block_planks = 5, block_sapling = 6, block_bedrock = 7, block_flowing_water = 8, block_water = 9, block_flowing_lava = 10, block_lava = 11, - block_sand = 12, - block_gravel = 13, block_gold_ore = 14, block_iron_ore = 15, block_coal_ore = 16, @@ -28,8 +25,6 @@ enum mc_block_id { block_lapis_ore = 21, block_lapis_block = 22, block_dispenser = 23, - block_sandstone = 24, - block_noteblock = 25, block_bed = 26, block_golden_rail = 27, block_detector_rail = 28, @@ -39,7 +34,6 @@ enum mc_block_id { block_deadbush = 32, block_piston = 33, block_piston_head = 34, - block_wool = 35, block_yellow_flower = 37, block_red_flower = 38, block_brown_mushroom = 39, @@ -48,11 +42,8 @@ enum mc_block_id { block_iron_block = 42, block_double_stone_slab = 43, block_stone_slab = 44, - block_brick_block = 45, block_tnt = 46, - block_bookshelf = 47, block_mossy_cobblestone = 48, - block_obsidian = 49, block_torch = 50, block_fire = 51, block_mob_spawner = 52, @@ -61,7 +52,6 @@ enum mc_block_id { block_redstone_wire = 55, block_diamond_ore = 56, block_diamond_block = 57, - block_crafting_table = 58, block_wheat = 59, block_farmland = 60, block_furnace = 61, @@ -77,22 +67,16 @@ enum mc_block_id { block_iron_door = 71, block_wooden_pressure_plate = 72, block_redstone_ore = 73, - block_lit_redstone_ore = 74, block_unlit_redstone_torch = 75, block_redstone_torch = 76, block_stone_button = 77, block_snow_layer = 78, block_ice = 79, - block_snow = 80, block_cactus = 81, - block_clay = 82, block_reeds = 83, - block_jukebox = 84, block_fence = 85, - block_pumpkin = 86, block_netherrack = 87, block_soul_sand = 88, - block_glowstone = 89, block_portal = 90, block_lit_pumpkin = 91, block_cake = 92, @@ -101,21 +85,18 @@ enum mc_block_id { block_stained_glass = 95, block_trapdoor = 96, block_monster_egg = 97, - block_stonebrick = 98, + block_infested_stone_bricks = 98, block_brown_mushroom_block = 99, block_red_mushroom_block = 100, block_iron_bars = 101, block_glass_pane = 102, - block_melon_block = 103, block_pumpkin_stem = 104, block_melon_stem = 105, block_vine = 106, block_fence_gate = 107, block_brick_stairs = 108, block_stone_brick_stairs = 109, - block_mycelium = 110, block_waterlily = 111, - block_nether_brick = 112, block_nether_brick_fence = 113, block_nether_brick_stairs = 114, block_nether_wart = 115, @@ -124,10 +105,7 @@ enum mc_block_id { block_cauldron = 118, block_end_portal = 119, block_end_portal_frame = 120, - block_end_stone = 121, - block_dragon_egg = 122, block_redstone_lamp = 123, - block_lit_redstone_lamp = 124, block_double_wooden_slab = 125, block_wooden_slab = 126, block_cocoa = 127, @@ -136,12 +114,9 @@ enum mc_block_id { block_ender_chest = 130, block_tripwire_hook = 131, block_tripwire_wire = 132, - block_emerald_block = 133, block_spruce_stairs = 134, block_birch_stairs = 135, block_jungle_stairs = 136, - block_command_block = 137, - block_beacon = 138, block_mushroom_stem = 139, block_flower_pot = 140, block_carrots = 141, @@ -155,14 +130,11 @@ enum mc_block_id { block_unpowered_comparator = 149, block_powered_comparator = 150, block_daylight_detector = 151, - block_redstone_block = 152, block_quartz_ore = 153, block_hopper = 154, - block_quartz_block = 155, block_quartz_stairs = 156, block_activator_rail = 157, block_dropper = 158, - block_stained_hardened_clay = 159, block_stained_glass_pane = 160, block_leaves2 = 161, block_log2 = 162, @@ -170,21 +142,13 @@ enum mc_block_id { block_dark_oak_stairs = 164, block_crimson_stairs = 509, block_warped_stairs = 510, - block_slime = 165, block_barrier = 166, block_iron_trapdoor = 167, - block_prismarine = 168, - block_sea_lantern = 169, block_hay_block = 170, - block_carpet = 171, - block_hardened_clay = 172, - block_coal_block = 173, - block_packed_ice = 174, block_double_plant = 175, block_standing_banner = 176, block_wall_banner = 177, block_daylight_detector_inverted = 178, - block_red_sandstone = 179, block_red_sandstone_stairs = 180, block_smooth_red_sandstone_stairs = 11415, block_double_stone_slab2 = 181, @@ -196,6 +160,8 @@ enum mc_block_id { block_acacia_fence_gate = 187, block_crimson_fence_gate = 513, block_warped_fence_gate = 514, + block_mangrove_fence_gate = 455, + block_mangrove_fence = 456, block_spruce_fence = 188, block_birch_fence = 189, block_jungle_fence = 190, @@ -207,13 +173,13 @@ enum mc_block_id { block_birch_door = 194, block_jungle_door = 195, block_acacia_door = 196, + block_mangrove_door = 457, block_crimson_door = 499, block_warped_door = 500, block_dark_oak_door = 197, block_end_rod = 198, block_chorus_plant = 199, block_chorus_flower = 200, - block_purpur_block = 201, block_purpur_pillar = 202, block_purpur_stairs = 203, block_purpur_double_slab = 204, @@ -222,12 +188,7 @@ enum mc_block_id { block_beetroots = 207, block_dirt_path = 208, block_end_gateway = 209, - block_repeating_command_block = 210, - block_chain_command_block = 211, block_frosted_ice = 212, - block_magma = 213, - block_nether_wart_block = 214, - block_red_nether_brick = 215, block_bone_block = 216, block_structure_void = 217, block_observer = 218, @@ -263,22 +224,18 @@ enum mc_block_id { block_green_glazed_terracotta = 248, block_red_glazed_terracotta = 249, block_black_glazed_terracotta = 250, - block_concrete = 251, - block_concrete_powder = 252, block_structure_block = 255, block_jigsaw = 256, block_shulker_box = 257, + + block_mangrove_trapdoor = 451, + block_mangrove_wall_sign = 454, // 1.16 stuff block_ancient_debris = 1000, block_basalt = 1001, block_polished_basalt = 1002, block_soul_campfire = 1003, block_blackstone = 1004, - block_netherite_block = 1005, - block_warped_nylium = 1006, - block_crimson_nylium = 1007, - block_warped_wart_block = 1010, - block_shroomlight = 1011, block_twisting_vines = 1012, block_twisting_vines_plant = 1013, block_weeping_vines = 1014, @@ -289,13 +246,6 @@ enum mc_block_id { block_crimson_roots = 1019, block_soul_soil = 1020, block_nether_gold_ore = 1021, - // Solid Nether stone blocks - block_polished_blackstone = 1022, - block_chiseled_polished_blackstone = 1023, - block_gilded_blackstone = 1024, - block_cracked_polished_blackstone_bricks = 1025, - block_polished_blackstone_bricks = 1026, - block_quartz_bricks = 1041, // Nether stone stairs block_blackstone_stairs = 1030, block_polished_blackstone_stairs = 1031, @@ -304,8 +254,6 @@ enum mc_block_id { block_polished_blackstone_pressure_plate = 1033, block_polished_blackstone_button = 1034, // advanced nether blocks - block_crying_obsidian = 1035, - block_lodestone = 1036, block_respawn_anchor = 1037, // soul lightning block_soul_lantern = 1038, @@ -313,29 +261,16 @@ enum mc_block_id { block_soul_fire = 1040, // 1.17 - block_amethyst = 1042, - block_raw_iron = 1043, - block_raw_gold = 1044, - block_budding_amethyst = 1045, - block_copper = 1046, - block_exposed_copper = 1047, - block_weathered_copper = 1048, - block_oxidized_copper = 1049, block_waxed_copper = 1050, block_waxed_exposed_copper = 1051, block_waxed_weathered_copper = 1052, block_waxed_oxidized_copper = 1053, - block_cut_copper = 1054, - block_exposed_cut_copper = 1055, - block_weathered_cut_copper = 1056, - block_oxidized_cut_copper = 1057, block_waxed_cut_copper = 1058, block_waxed_exposed_cut_copper = 1059, block_waxed_weathered_cut_copper = 1060, block_waxed_oxidized_cut_copper = 1061, - block_raw_copper = 1062, block_copper_ore = 1063, block_cut_copper_stairs = 1064, @@ -358,13 +293,7 @@ enum mc_block_id { block_waxed_weathered_cut_copper_slab = 1078, block_waxed_oxidized_cut_copper_slab = 1079, - block_moss = 1080, - block_calcite = 1081, - block_rooted_dirt = 1082, - block_deepslate = 1083, - block_cobbled_deepslate = 1084, - block_polished_deepslate = 1085, block_deepslate_coal_ore = 1086, block_deepslate_iron_ore = 1087, block_deepslate_copper_ore = 1088, @@ -373,28 +302,20 @@ enum mc_block_id { block_deepslate_lapis_ore = 1091, block_deepslate_diamond_ore = 1092, block_deepslate_redstone_ore = 1093, - block_deepslate_bricks = 1094, - block_cracked_deepslate_bricks = 1095, - block_deepslate_tiles = 1096, - block_cracked_deepslate_tiles = 1097, - block_chiseled_deepslate = 1098, block_cobbled_deepslate_stairs = 1099, block_polished_deepslate_stairs = 1100, block_deepslate_brick_stairs = 1101, block_deepslate_tile_stairs = 1102, + block_mangrove_stairs = 1108, block_cobbled_deepslate_slab = 1103, block_polished_deepslate_slab = 1104, block_deepslate_brick_slab = 1105, block_deepslate_tile_slab = 1106, - block_dripstone = 1107, - block_smooth_basalt = 1108, - block_tuff = 1109, block_pointed_dripstone = 1110, - block_powder_snow = 1111, block_hanging_roots = 1112, block_small_amethyst_bud = 1113, block_medium_amethyst_bud = 1114, @@ -405,13 +326,10 @@ enum mc_block_id { block_glow_lichen = 1119, block_spore_blossom = 1120, - block_mud = 1121, - block_packed_mud = 1122, - block_mud_bricks = 1123, block_mud_brick_slab = 1124, - block_mangrove_roots = 1125, block_mangrove_log = 1126, - block_muddy_mangrove_roots = 1127, + block_mud_brick_stairs = 1129, + block_mangrove_slab = 1789, // adding a gap in the numbering of walls to keep them all // in one numbering block starting at 1792 @@ -438,6 +356,8 @@ enum mc_block_id { block_polished_deepslate_wall = 1810, block_deepslate_brick_wall = 1811, block_deepslate_tile_wall = 1812, + block_mud_brick_wall = 1813, + // end of walls block_prismarine_stairs = 11337, @@ -465,13 +385,8 @@ enum mc_block_id { block_mossy_stone_brick_slab = 11356, block_smooth_quartz_slab = 11357, block_smooth_stone_slab = 11358, - block_fletching_table = 11359, - block_cartography_table = 11360, - block_smithing_table = 11361, block_blast_furnace = 11362, - // block_blast_furnace lit 11363 block_smoker = 11364, - // block_smoker lit = 11365, block_lectern = 11366, block_loom = 11367, block_stonecutter = 11368, @@ -519,8 +434,6 @@ enum mc_block_id { // 1.15 blocks below block_beehive = 11501, block_bee_nest = 11502, - block_honeycomb_block = 11503, - block_honey_block = 11504, block_sweet_berry_bush = 11505, block_campfire = 11506, block_bell = 11507, diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index e4c1beb7f..3173bdf78 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -31,7 +31,7 @@ // increment this value if you've made a change to the c extension // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 113 +#define OVERVIEWER_EXTENSION_VERSION 114 #include #include @@ -131,7 +131,6 @@ typedef enum { SOLID, FLUID, NOSPAWN, - NODATA, } BlockProperty; /* globals set in init_chunk_render, here because they're used in block_has_property */ diff --git a/overviewer_core/src/primitives/base.c b/overviewer_core/src/primitives/base.c index bd37ff0b6..31d1bda65 100644 --- a/overviewer_core/src/primitives/base.c +++ b/overviewer_core/src/primitives/base.c @@ -103,7 +103,7 @@ base_draw(void* data, RenderState* state, PyObject* src, PyObject* mask, PyObjec * biome-compliant ones! The tinting is now all done here. */ if (/* grass, but not snowgrass */ - (state->block == block_grass && get_data(state, BLOCKS, state->x, state->y + 1, state->z) != 78) || + (state->block == block_grass && get_data(state, BLOCKS, state->x, state->y + 1, state->z) != block_snow_layer) || block_class_is_subset(state->block, (mc_block_t[]){block_vine, block_waterlily, block_flowing_water, block_water, block_leaves, block_leaves2}, 6) || /* tallgrass, but not dead shrubs */ diff --git a/overviewer_core/src/primitives/cave.c b/overviewer_core/src/primitives/cave.c index 66d976efc..8d6a815e3 100644 --- a/overviewer_core/src/primitives/cave.c +++ b/overviewer_core/src/primitives/cave.c @@ -83,14 +83,14 @@ cave_hidden(void* data, RenderState* state, int32_t x, int32_t y, int32_t z) { blockID = getArrayShort3D(state->blocks, x, y, z); blockUpID = get_data(state, BLOCKS, x, y + 1, z); - if (blockID == 9 || blockID == 8 || blockUpID == 9 || blockUpID == 8) { + if (blockID == block_water || blockID == block_flowing_water || blockUpID == block_water || blockUpID == block_flowing_water) { for (dy = y + 1; dy < (SECTIONS_PER_CHUNK - state->chunky) * 16; dy++) { /* go up and check for skylight */ if (get_data(state, SKYLIGHT, x, dy, z) != 0) { return true; } blockUpID = get_data(state, BLOCKS, x, dy, z); - if (blockUpID != 8 && blockUpID != 9) { + if (blockUpID != block_flowing_water && blockUpID != block_water) { /* we are out of the water! and there's no skylight * , i.e. is a cave lake or something similar */ break; diff --git a/overviewer_core/src/primitives/overlay-mineral.c b/overviewer_core/src/primitives/overlay-mineral.c index f8bba9006..66043acee 100644 --- a/overviewer_core/src/primitives/overlay-mineral.c +++ b/overviewer_core/src/primitives/overlay-mineral.c @@ -33,16 +33,27 @@ struct MineralColor { /* put more valuable ores first -- they take precedence */ static struct MineralColor default_minerals[] = { {block_mossy_cobblestone, 31, 153, 9}, + {block_deepslate_emerald_ore, 0, 255, 0}, {block_diamond_ore, 32, 230, 220}, + {block_deepslate_diamond_ore, 32, 230, 220}, {block_lapis_ore, 0, 23, 176}, + {block_deepslate_lapis_ore, 0, 23, 176}, {block_gold_ore, 255, 234, 0}, + {block_deepslate_gold_ore, 255, 234, 0}, + + {block_emerald_ore, 0, 255, 0}, {block_iron_ore, 204, 204, 204}, + {block_deepslate_iron_ore, 204, 204, 204}, {block_redstone_ore, 186, 0, 0}, - {block_lit_redstone_ore, 186, 0, 0}, + {block_deepslate_redstone_ore, 186, 0, 0}, {block_coal_ore, 54, 54, 54}, + {block_deepslate_coal_ore, 54, 54, 54}, + + {block_copper_ore, 254, 154, 54}, + {block_deepslate_copper_ore, 254, 154, 54}, /* end of list marker */ {0, 0, 0, 0}}; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index b809d242f..7ac3fdeda 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -14,8 +14,12 @@ # with the Overviewer. If not, see . from collections import OrderedDict +from copy import deepcopy +from pathlib import Path +import re import sys import imp +import json import os import os.path import zipfile @@ -29,22 +33,20 @@ from . import util - -BLOCKTEX = "assets/minecraft/textures/block/" - # global variables to collate information in @material decorators blockmap_generators = {} +block_models = {} known_blocks = set() used_datas = set() max_blockid = 0 max_data = 0 +next_unclaimed_id = 2048 transparent_blocks = set() solid_blocks = set() fluid_blocks = set() nospawn_blocks = set() -nodata_blocks = set() # This is here for circular import reasons. @@ -73,6 +75,7 @@ class Textures(object): rendering. It accepts a background color, north direction, and local textures path. """ + def __init__(self, texturepath=None, bgcolor=(26, 26, 26, 0), northdirection=0): self.bgcolor = bgcolor self.rotation = northdirection @@ -105,6 +108,7 @@ def __getstate__(self): pass attributes['jars'] = OrderedDict() return attributes + def __setstate__(self, attrs): # regenerate textures, if needed for attr, val in list(attrs.items()): @@ -135,9 +139,8 @@ def generate(self): # generate the blocks global blockmap_generators - global known_blocks, used_datas self.blockmap = [None] * max_blockid * max_data - + for (blockid, data), texgen in list(blockmap_generators.items()): tex = texgen(self, blockid, data) self.blockmap[blockid * max_data + data] = self.generate_texture_tuple(tex) @@ -147,15 +150,59 @@ def generate(self): self.biome_grass_texture = self.biome_grass_texture.resize(self.texture_dimensions, Image.ANTIALIAS) # rescale the rest - for i, tex in enumerate(blockmap): + for i, tex in enumerate(self.blockmap): if tex is None: continue block = tex[0] scaled_block = block.resize(self.texture_dimensions, Image.ANTIALIAS) - blockmap[i] = self.generate_texture_tuple(scaled_block) + self.blockmap[i] = self.generate_texture_tuple(scaled_block) self.generated = True + # TODO: load models from resource packs, for now only client jars are used + # TODO: load blockstate before models + def find_models(self, verbose=False): + filename = 'assets/minecraft/models/' + versiondir = self.versiondir(verbose) + available_versions = self.available_versions(versiondir, verbose) + + if not available_versions: + if verbose: + logging.info("Did not find any non-snapshot minecraft jars >=1.8.0") + while(available_versions): + most_recent_version = available_versions.pop(0) + if verbose: + logging.info("Trying {0}. Searching it for the file...".format( + ".".join(str(x) for x in most_recent_version))) + + jarname = ".".join(str(x) for x in most_recent_version) + jarpath = os.path.join(versiondir, jarname, jarname + ".jar") + + jar = {} + + if jarpath in self.jars: + jar = self.jars[jarpath] + elif os.path.isfile(jarpath): + try: + jar = zipfile.ZipFile(jarpath) + except (KeyError, IOError) as e: + pass + except (zipfile.BadZipFile) as e: + logging.warning("Your jar {0} is corrupted, I'll be skipping it, but you " + "should probably look into that.".format(jarpath)) + else: + if verbose: + logging.info("Did not find file {0} in jar {1}".format(filename, jarpath)) + continue + + models = [] + for file in jar.namelist(): + if file.startswith('assets/minecraft/models/block'): + model = Path(file).stem + models.append(model) + + return models + ## ## Helpers for opening textures ## @@ -264,6 +311,37 @@ def find_file(self, filename, mode="rb", verbose=False): # Find an installed minecraft client jar and look in it for the texture # file we need. + versiondir = self.versiondir(verbose) + available_versions = self.available_versions(versiondir, verbose) + + if not available_versions: + if verbose: logging.info("Did not find any non-snapshot minecraft jars >=1.8.0") + while(available_versions): + most_recent_version = available_versions.pop(0) + if verbose: logging.info("Trying {0}. Searching it for the file...".format(".".join(str(x) for x in most_recent_version))) + + jarname = ".".join(str(x) for x in most_recent_version) + jarpath = os.path.join(versiondir, jarname, jarname + ".jar") + + if os.path.isfile(jarpath): + try: + jar = zipfile.ZipFile(jarpath) + jar.getinfo(filename) + if verbose: logging.info("Found %s in '%s'", filename, jarpath) + self.jars[jarpath] = jar + return jar.open(filename) + except (KeyError, IOError) as e: + pass + except (zipfile.BadZipFile) as e: + logging.warning("Your jar {0} is corrupted, I'll be skipping it, but you " + "should probably look into that.".format(jarpath)) + + if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath)) + + + raise TextureException("Could not find the textures while searching for '{0}'. Try specifying the 'texturepath' option in your config file.\nSet it to the path to a Minecraft Resource pack.\nAlternately, install the Minecraft client (which includes textures)\nAlso see \n(Remember, this version of Overviewer requires a 1.19-compatible resource pack)\n(Also note that I won't automatically use snapshots; you'll have to use the texturepath option to use a snapshot jar)".format(filename)) + + def versiondir(self, verbose): versiondir = "" if "APPDATA" in os.environ and sys.platform.startswith("win"): versiondir = os.path.join(os.environ['APPDATA'], ".minecraft", "versions") @@ -274,7 +352,9 @@ def find_file(self, filename, mode="rb", verbose=False): # For Mac: versiondir = os.path.join(os.environ['HOME'], "Library", "Application Support", "minecraft", "versions") + return versiondir + def available_versions(self, versiondir, verbose): try: if verbose: logging.info("Looking in the following directory: \"%s\"" % versiondir) versions = os.listdir(versiondir) @@ -306,32 +386,8 @@ def find_file(self, filename, mode="rb", verbose=False): available_versions.append(versionparts) available_versions.sort(reverse=True) - if not available_versions: - if verbose: logging.info("Did not find any non-snapshot minecraft jars >=1.8.0") - while(available_versions): - most_recent_version = available_versions.pop(0) - if verbose: logging.info("Trying {0}. Searching it for the file...".format(".".join(str(x) for x in most_recent_version))) - jarname = ".".join(str(x) for x in most_recent_version) - jarpath = os.path.join(versiondir, jarname, jarname + ".jar") - - if os.path.isfile(jarpath): - try: - jar = zipfile.ZipFile(jarpath) - jar.getinfo(filename) - if verbose: logging.info("Found %s in '%s'", filename, jarpath) - self.jars[jarpath] = jar - return jar.open(filename) - except (KeyError, IOError) as e: - pass - except (zipfile.BadZipFile) as e: - logging.warning("Your jar {0} is corrupted, I'll be skipping it, but you " - "should probably look into that.".format(jarpath)) - - if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath)) - - - raise TextureException("Could not find the textures while searching for '{0}'. Try specifying the 'texturepath' option in your config file.\nSet it to the path to a Minecraft Resource pack.\nAlternately, install the Minecraft client (which includes textures)\nAlso see \n(Remember, this version of Overviewer requires a 1.19-compatible resource pack)\n(Also note that I won't automatically use snapshots; you'll have to use the texturepath option to use a snapshot jar)".format(filename)) + return available_versions def load_image_texture(self, filename): # Textures may be animated or in a different resolution than 16x16. @@ -376,8 +432,6 @@ def load_image(self, filename): self.texture_cache[filename] = img return img - - def load_water(self): """Special-case function for loading water.""" watertexture = getattr(self, "watertexture", None) @@ -525,7 +579,6 @@ def transform_image_slope(img): return newimg - @staticmethod def transform_image_angle(img, angle): """Takes an image an shears it in arbitrary angle with the axis of @@ -566,7 +619,6 @@ def transform_image_angle(img, angle): return newimg - def build_block(self, top, side): """From a top texture and a side texture, build a block image. top and side should be 16x16 image objects. Returns a 24x24 image @@ -767,26 +819,6 @@ def build_full_block(self, top, side1, side2, side3, side4, bottom=None): return img - def build_axis_block(self, top, side, data): - """ - Build an block with Axis property. - data = {'y': 0, 'x': 1, 'z': 2}[axis] - """ - def draw_x(): - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) - - def draw_y(): - return self.build_full_block(side, None, None, side.rotate(270), top) - - draw_funcs = [draw_x, draw_y] - - if data == 0: # up - return self.build_block(top, side) - elif data == 1: # x - return draw_funcs[(self.rotation + 0) % len(draw_funcs)]() - elif data == 2: # y - return draw_funcs[(self.rotation + 1) % len(draw_funcs)]() - def build_sprite(self, side): """From a side texture, create a sprite-like texture such as those used for spiderwebs or flowers.""" @@ -830,6 +862,331 @@ def generate_texture_tuple(self, img): return None return (img, self.generate_opaque_mask(img)) + # + # This part if for reading the models/*.json assets. + # + + models = {} + + def load_model(self, modelname): + if modelname in self.models: + return self.models[modelname] + + fileobj = self.find_file('assets/minecraft/models/' + modelname + + '.json', verbose=logging.getLogger().isEnabledFor(logging.DEBUG)) + self.models[modelname] = json.load(fileobj) + fileobj.close() + + if 'parent' in self.models[modelname]: + parent = self.load_model(re.sub('.*:', '', self.models[modelname]['parent'])) + if 'textures' in parent: + self.models[modelname]['textures'].update(parent['textures']) + if 'elements' in parent: + if 'elements' in self.models[modelname]: + self.models[modelname]['elements'] += parent['elements'] + else: + self.models[modelname]['elements'] = parent['elements'] + del self.models[modelname]['parent'] + + self.models[modelname] = self.normalize_model(modelname) + return self.models[modelname] + + # fix known inconsistencies in model info + def normalize_model(self, modelname): + if modelname == 'block/observer': + self.models[modelname] = deepcopy(self.models[modelname]) + self.models[modelname]['elements'][0]['faces']['up']['uv'] = [0, 0, 16, 16] + elif modelname == 'block/loom': + self.models[modelname] = deepcopy(self.models[modelname]) + self.models[modelname]['elements'][0]['faces']['up']['texturerotation'] = 180 + self.models[modelname]['elements'][0]['faces']['down']['texturerotation'] = 180 + elif modelname == 'block/barrel' or modelname == 'block/barrel_open': + self.models[modelname] = deepcopy(self.models[modelname]) + self.models[modelname]['elements'][0]['faces']['north']['texture'] = '#up' + self.models[modelname]['elements'][0]['faces']['south']['texture'] = '#down' + self.models[modelname]['elements'][0]['faces']['down']['texture'] = '#north' + self.models[modelname]['elements'][0]['faces']['up']['texture'] = '#south' + self.models[modelname]['elements'][0]['faces']['east']['texturerotation'] = 90 + self.models[modelname]['elements'][0]['faces']['west']['texturerotation'] = 90 + elif modelname == 'block/dropper_vertical' or modelname == 'block/dispenser_vertical': + self.models[modelname] = deepcopy(self.models[modelname]) + self.models[modelname]['elements'][0]['faces']['north']['texture'] = '#up' + self.models[modelname]['elements'][0]['faces']['up']['texture'] = '#north' + + return self.models[modelname] + + def build_block_from_model(self, modelname, blockstate={}): + modelname = 'block/' + modelname + + colmodel = self.load_model(modelname) + + if 'elements' not in colmodel: + return None + + elements = colmodel['elements'] + elements.sort(key=lambda x: (x['to'][1], 16 - (x['from'][0]+x['to'][0]),16 - (x['from'][2]+x['to'][2]))) + + img = Image.new("RGBA", (24, 24), self.bgcolor) + + # for each elements + for elem in elements: + try: + if 'west' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'west') + elif 'east' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'east') + + if 'north' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'north') + elif 'south' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'south') + + if 'up' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'up') + elif 'down' in elem['faces']: + self.draw_blockface(img, elem, colmodel, blockstate, modelname, 'down') + except KeyError as e: + continue + + # Manually touch up 6 pixels that leave a gap because of how the + # shearing works out. This makes the blocks perfectly tessellate-able + for x, y in [(13, 23), (17, 21), (21, 19)]: + # Copy a pixel to x,y from x-1,y + img.putpixel((x, y), img.getpixel((x - 1, y))) + for x, y in [(3, 4), (7, 2), (11, 0)]: + # Copy a pixel to x,y from x+1,y + img.putpixel((x, y), img.getpixel((x + 1, y))) + + return img + + def draw_blockface(self, img, elem, colmodel, blockstate, modelname, direction): + if 'facing' in blockstate: + facing = self.map_facing_to_real(blockstate['facing'], direction) + elif 'axis' in blockstate: + facing = self.map_axis_to_real(blockstate['axis'], direction) + else: + facing = direction + texture = self.build_texture(direction, elem, colmodel, blockstate, modelname, facing) + alpha_over(img, texture, self.image_pos(direction, elem, facing, modelname), texture) + + def image_pos(self, elementdirection, element, facing, modelname): + + if elementdirection == 'east': + return (0, 0) + elif elementdirection == 'south': + return (12, 0) + elif elementdirection == 'west': + setback = 16 - self.setback(element, facing) + toya = int(math.ceil(12/16 * (setback))) + toy = int(math.ceil(6/16 * (setback))) + return (toya, toy) + elif elementdirection == 'north': + setback = self.setback(element, facing) + toya = int(math.ceil((12/16 * (setback)))) + toy = int(math.ceil(6 - (6/16 * (setback)))) + return (toya, toy) + elif elementdirection == 'down': + toy = 12 + if 'from' in element: + toy = int(math.ceil(((16 - element['from'][1])/16*12.))) + return (0, toy) + else: # up + toy = 0 + if 'to' in element: + toy = int(math.ceil(((16 - element['to'][1])/16*12.))) + return (0, toy) + + def setback(self, element, facing): + return {'up': 16, 'down': 0, + 'north': element['from'][2], 'south': 16 - element['to'][2], + 'east': 16 - element['to'][0], 'west': element['from'][0]}[facing] + + def numvalue_orientation(self, orientation): + return {'south': 0, 'west': 1, 'north': 2, 'east': 3, 'up': 4, 'down': 6}[orientation] + + def orientation_from_numvalue(self, orientation): + return {0: 'south', 1: 'west', 2: 'north', 3: 'east', 4: 'up', 6: 'down'}[orientation] + + # translates rotation to real face value + # facing is the blockproperty + # targetfacing is the direction in witch the north is rotated + def map_facing_to_real(self, blockfacing, targetblockface): + resultface = blockfacing + if blockfacing == 'up': + if targetblockface == 'up': + resultface = 'north' + elif targetblockface == 'north': + resultface = 'west' + elif targetblockface == 'west': + resultface = 'down' + elif blockfacing == 'down': + if targetblockface == 'west': + resultface = 'down' + elif targetblockface == 'north': + resultface = 'west' + elif targetblockface == 'up': + resultface = 'south' + elif targetblockface == 'up': + resultface = 'up' + elif targetblockface == 'down': + resultface = 'down' + else: + resultface = self.orientation_from_numvalue((self.numvalue_orientation( + targetblockface) + [0, 3, 2, 1][self.numvalue_orientation(blockfacing)] + 1 + self.rotation + (self.rotation % 2) * 2) % 4) + return resultface + + def map_axis_to_real(self, axis, textureface): + if axis == 'x': + return {'up': 'north', 'north': 'down', 'down': 'south', 'south': 'up', 'east': 'east', 'west': 'west'}[textureface] + elif axis == 'z': + return {'up': 'west', 'west': 'down', 'down': 'east', 'east': 'up', 'north': 'north', 'south': 'south'}[textureface] + else: + return textureface + + def axis_rotation(self, axis, face, texture): + if axis == 'x': + rotation = {'up': 270, 'north': 0, 'down': 0, + 'south': 0, 'east': 270, 'west': 270}[face] + return texture.rotate(rotation) + elif axis == 'z': + rotation = {'up': 0, 'west': 0, 'down': 0, + 'east': 0, 'north': 90, 'south': 90}[face] + return texture.rotate(rotation) + else: + return texture + + def build_texture(self, direction, elem, data, blockstate, modelname, textureface): + + texture = self.find_texture_from_model( + elem['faces'][textureface]['texture'], data['textures']).copy() + + if 'axis' in blockstate: + texture = self.axis_rotation(blockstate['axis'], direction, texture) + texture = self.texture_rotation(direction, texture, blockstate, elem['faces'][textureface], modelname) + + if 'from' in elem and 'to' in elem and (elem['from'] != [0,0,0] or elem['to'] != [16,16,16]): + area = [0,0,16,16] + + if textureface == 'west': + area = [elem['from'][2],elem['from'][1],elem['to'][2],elem['to'][1]] + texture = self.crop_to_transparancy(texture, area) + elif textureface == 'east': + area = [16-elem['from'][2],elem['from'][1],16-elem['to'][2],elem['to'][1]] + texture = self.crop_to_transparancy(texture, area) + elif textureface == 'north': + area = [16-elem['from'][0],elem['from'][1],16-elem['to'][0],elem['to'][1]] + texture = self.crop_to_transparancy(texture, area) + elif textureface == 'south': + area = [elem['from'][0],elem['from'][1],elem['to'][0],elem['to'][1]] + texture = self.crop_to_transparancy(texture, area) + elif textureface in {'up', 'down'}: + if 'facing' in blockstate and blockstate['facing'] in {'north'}: + area = [elem['from'][0],16-elem['from'][2],elem['to'][0],16-elem['to'][2]] + elif 'facing' in blockstate and blockstate['facing'] in {'south'}: + area = [16-elem['from'][0],elem['from'][2],16-elem['to'][0],elem['to'][2]] + elif 'facing' in blockstate and blockstate['facing'] in {'west'}: + area = [elem['from'][2],elem['from'][0],elem['to'][2],elem['to'][0]] + elif 'facing' in blockstate and blockstate['facing'] in {'east'}: + area = [16-elem['from'][2],16-elem['from'][0],16-elem['to'][2],16-elem['to'][0]] + else: + area = [elem['from'][2],elem['from'][0],elem['to'][2],elem['to'][0]] + texture = self.crop_to_transparancy(texture, area) + + # TODO: deal with rotation + + texture = self.transform_texture(direction, texture, blockstate, elem['faces'][textureface]) + texture = self.adjust_lighting(direction, texture) + + return texture + + # TODO: use uv instead of from-to values + def crop_to_transparancy(self, img, area): + # PIL image coordinates do not match MC texture coordinates + # PIL starts in lower left + # MC starts in upper left + + if area[0] > area[2]: + area = [area[2], area[1], area[0], area[3]] + if area[1] > area[3]: + area = [area[0], area[3], area[2], area[1]] + if area == [ 0, 0, 16, 16 ]: + return img + + # cut from top + if area[3] != 16: + ImageDraw.Draw(img).rectangle((0, 0, 16, 16 - area[3]-1), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) + + # cut from bottom + if area[1] != 0: + ImageDraw.Draw(img).rectangle((0, 16 - (area[1]-2), 16, 16), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) + + # cut from right + if area[2] != 16: + ImageDraw.Draw(img).rectangle((area[2]-1, 0, 16, 16), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) + + # cut from left + if area[0] != 0: + ImageDraw.Draw(img).rectangle((0, 0, area[0]-2, 16), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) + + return img + + def adjust_lighting(self, direction, texture): + if direction in {'south', 'west'}: + sidealpha = texture.split()[3] + texture = ImageEnhance.Brightness(texture).enhance(0.8) + texture.putalpha(sidealpha) + return texture + elif direction in {'north', 'east'}: + sidealpha = texture.split()[3] + texture = ImageEnhance.Brightness(texture).enhance(0.9) + texture.putalpha(sidealpha) + return texture + else: + return texture + + def texture_rotation(self, direction, texture, blockstate, faceinfo, modelname): + rotation = 0 + if 'texturerotation' in faceinfo: + rotation += faceinfo['texturerotation'] + if direction in {'down', 'up'}: + if 'facing' in blockstate: + if self.numvalue_orientation(blockstate['facing']) < 4: + rotation += [0, 270, 180, + 90][(self.numvalue_orientation(blockstate['facing']) + self.rotation) % 4] + else: + rotation += [180, 0][({'up': 0, 'down': 1}[blockstate['facing']])] + elif direction in {'north', 'south'}: + if 'rotation' in faceinfo: + rotation = {0: 180, 90: 90, 180: 0, 270: 270}[faceinfo['rotation']] + if 'facing' in blockstate and blockstate['facing'] in {'up', 'down'}: + rotation += [90, 90][({'up': 0, 'down': 1}[blockstate['facing']])] + elif direction in {'west', 'east'}: + if 'rotation' in faceinfo: + rotation = {0: 180, 90: 270, 180: 0, 270: 90}[faceinfo['rotation']] + if 'facing' in blockstate and blockstate['facing'] in {'up', 'down'}: + rotation += [180, 0][({'up': 0, 'down': 1}[blockstate['facing']])] + + return texture.rotate(rotation % 360) + + def transform_texture(self, direction, texture, blockstate, faceinfo): + + if direction in {'down', 'up'}: + return self.transform_image_top(texture) + elif direction in {'north', 'south'}: + texture = self.transform_image_side(texture) + elif direction in {'west', 'east'}: + texture = texture.transpose(Image.FLIP_LEFT_RIGHT) + texture = self.transform_image_side(texture) + texture = texture.transpose(Image.FLIP_LEFT_RIGHT) + + return texture + + def find_texture_from_model(self, face, textureset): + if face.startswith('#'): + return self.find_texture_from_model(textureset[face[1:]], textureset) + else: + return self.load_image_texture("assets/minecraft/textures/" + re.sub('.*:', '', face) + '.png') + ## ## The other big one: @material and associated framework ## @@ -838,7 +1195,8 @@ def generate_texture_tuple(self, img): # the material registration decorator def material(blockid=[], data=[0], **kwargs): # mapping from property name to the set to store them in - properties = {"transparent" : transparent_blocks, "solid" : solid_blocks, "fluid" : fluid_blocks, "nospawn" : nospawn_blocks, "nodata" : nodata_blocks} + properties = {"transparent": transparent_blocks, "solid": solid_blocks, + "fluid": fluid_blocks, "nospawn": nospawn_blocks} # make sure blockid and data are iterable try: @@ -875,7 +1233,7 @@ def func_wrapper(texobj, blockid, data): except TypeError: if kwargs.get(prop, False): properties[prop].update([block]) - + # populate blockmap_generators with our function for d in data: blockmap_generators[(block, d)] = func_wrapper @@ -883,25 +1241,25 @@ def func_wrapper(texobj, blockid, data): return func_wrapper return inner_material -# shortcut function for pure blocks, default to solid, nodata -def block(blockid=[], top_image=None, side_image=None, **kwargs): - new_kwargs = {'solid' : True, 'nodata' : True} + +def solidmodelblock(blockid=[], name=None, **kwargs): + new_kwargs = {'solid': True} new_kwargs.update(kwargs) + return modelblock(blockid=blockid, name=name, **new_kwargs) + + +def modelblock(blockid=[], name=None, **kwargs): + if name is None: + raise ValueError("block name was not provided") - if top_image is None: - raise ValueError("top_image was not provided") - - if side_image is None: - side_image = top_image - - @material(blockid=blockid, **new_kwargs) + @material(blockid=blockid, **kwargs) def inner_block(self, unused_id, unused_data): - return self.build_block(self.load_image_texture(top_image), self.load_image_texture(side_image)) + return self.build_block_from_model(name) return inner_block -# shortcut function for sprite blocks, defaults to transparent, nodata +# shortcut function for sprite blocks, defaults to transparent def sprite(blockid=[], imagename=None, **kwargs): - new_kwargs = {'transparent' : True, 'nodata' : True} + new_kwargs = {'transparent' : True} new_kwargs.update(kwargs) if imagename is None: @@ -912,9 +1270,9 @@ def inner_sprite(self, unused_id, unused_data): return self.build_sprite(self.load_image_texture(imagename)) return inner_sprite -# shortcut function for billboard blocks, defaults to transparent, nodata +# shortcut function for billboard blocks, defaults to transparent def billboard(blockid=[], imagename=None, **kwargs): - new_kwargs = {'transparent' : True, 'nodata' : True} + new_kwargs = {'transparent' : True} new_kwargs.update(kwargs) if imagename is None: @@ -925,77 +1283,52 @@ def inner_billboard(self, unused_id, unused_data): return self.build_billboard(self.load_image_texture(imagename)) return inner_billboard + +def unbound_models(): + global max_blockid, block_models, next_unclaimed_id + tex = Textures() + + models = Textures.find_models(tex) + for model in models: + # determine transparency + + colmodel = tex.load_model('block/' + model) + if 'elements' not in colmodel: + continue + transp = False + + # for each elements + for elem in colmodel['elements']: + if 'from' in elem and 'to' in elem and (elem['from'] != [0,0,0] or elem['to'] != [16,16,16]): + transp = True + break + + # find next unclaimed id to keep id values as low as possible + while (next_unclaimed_id, 0) in blockmap_generators: + next_unclaimed_id = next_unclaimed_id + 1 + # 1792 to 2047 is a reserved range for wall blocks + if next_unclaimed_id < 2048 and next_unclaimed_id > 1791: + next_unclaimed_id = 2048 + id = next_unclaimed_id + modelblock(blockid=id, name=model, transparent=transp, solid=True) + block_models['minecraft:' + model] = (id, 0) + ## -## and finally: actual texture definitions +## the texture definitions for all blocks that have special rendering conditions ## -# stone -@material(blockid=1, data=list(range(7)), solid=True) -def stone(self, blockid, data): - if data == 0: # regular old-school stone - img = self.load_image_texture("assets/minecraft/textures/block/stone.png") - elif data == 1: # granite - img = self.load_image_texture("assets/minecraft/textures/block/granite.png") - elif data == 2: # polished granite - img = self.load_image_texture("assets/minecraft/textures/block/polished_granite.png") - elif data == 3: # diorite - img = self.load_image_texture("assets/minecraft/textures/block/diorite.png") - elif data == 4: # polished diorite - img = self.load_image_texture("assets/minecraft/textures/block/polished_diorite.png") - elif data == 5: # andesite - img = self.load_image_texture("assets/minecraft/textures/block/andesite.png") - elif data == 6: # polished andesite - img = self.load_image_texture("assets/minecraft/textures/block/polished_andesite.png") - return self.build_block(img, img) - -@material(blockid=2, data=list(range(11))+[0x10,], solid=True) + +@material(blockid=2, data=list(range(11)) + [0x10, ], solid=True) def grass(self, blockid, data): - # 0x10 bit means SNOW - side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_side.png") if data & 0x10: - side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_snow.png") - img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/grass_block_top.png"), side_img) - if not data & 0x10: + return self.build_block_from_model('grass_block_snow') + else: + side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_side.png") + img = self.build_block(self.load_image_texture( + "assets/minecraft/textures/block/grass_block_top.png"), side_img) alpha_over(img, self.biome_grass_texture, (0, 0), self.biome_grass_texture) - return img - + return img -# dirt -@material(blockid=3, data=list(range(3)), solid=True) -def dirt_blocks(self, blockid, data): - texture_map = [{"top": "dirt", "side": "dirt"}, # Normal - {"top": "coarse_dirt", "side": "coarse_dirt"}, # Coarse - {"top": "podzol_top", "side": "podzol_side"}] # Podzol - top_img = self.load_image_texture("assets/minecraft/textures/block/%s.png" - % texture_map[data]["top"]) - side_img = self.load_image_texture("assets/minecraft/textures/block/%s.png" - % texture_map[data]["side"]) - - return self.build_block(top_img, side_img) - - -# cobblestone -block(blockid=4, top_image="assets/minecraft/textures/block/cobblestone.png") - -# wooden planks -@material(blockid=5, data=list(range(8)), solid=True) -def wooden_planks(self, blockid, data): - if data == 0: # normal - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"), self.load_image_texture("assets/minecraft/textures/block/oak_planks.png")) - if data == 1: # pine - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png"),self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png")) - if data == 2: # birch - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/birch_planks.png"),self.load_image_texture("assets/minecraft/textures/block/birch_planks.png")) - if data == 3: # jungle wood - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png"),self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png")) - if data == 4: # acacia - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png"),self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png")) - if data == 5: # dark oak - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"),self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png")) - if data == 6: # crimson - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png"),self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png")) - if data == 7: # warped - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/warped_planks.png"),self.load_image_texture("assets/minecraft/textures/block/warped_planks.png")) @material(blockid=6, data=list(range(16)), transparent=True) def saplings(self, blockid, data): @@ -1014,16 +1347,18 @@ def saplings(self, blockid, data): tex = self.load_image_texture("assets/minecraft/textures/block/dark_oak_sapling.png") return self.build_sprite(tex) + sprite(blockid=11385, imagename="assets/minecraft/textures/block/oak_sapling.png") sprite(blockid=11386, imagename="assets/minecraft/textures/block/spruce_sapling.png") sprite(blockid=11387, imagename="assets/minecraft/textures/block/birch_sapling.png") sprite(blockid=11388, imagename="assets/minecraft/textures/block/jungle_sapling.png") sprite(blockid=11389, imagename="assets/minecraft/textures/block/acacia_sapling.png") sprite(blockid=11390, imagename="assets/minecraft/textures/block/dark_oak_sapling.png") + sprite(blockid=11413, imagename="assets/minecraft/textures/block/bamboo_stage0.png") # bedrock -block(blockid=7, top_image="assets/minecraft/textures/block/bedrock.png") +solidmodelblock(blockid=7, name='bedrock') # water, glass, and ice (no inner surfaces) # uses pseudo-ancildata found in iterate.c @@ -1046,17 +1381,7 @@ def no_inner_surfaces(self, blockid, data): top = texture else: top = None - - if (data & 0b0001) == 1: - side1 = texture # top left - else: - side1 = None - - if (data & 0b1000) == 8: - side2 = texture # top right - else: - side2 = None - + if (data & 0b0010) == 2: side3 = texture # bottom left else: @@ -1079,231 +1404,188 @@ def lava(self, blockid, data): lavatex = self.load_lava() return self.build_block(lavatex, lavatex) -# sand -@material(blockid=12, data=list(range(2)), solid=True) -def sand_blocks(self, blockid, data): - if data == 0: # normal - img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/sand.png"), self.load_image_texture("assets/minecraft/textures/block/sand.png")) - if data == 1: # red - img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/red_sand.png"), self.load_image_texture("assets/minecraft/textures/block/red_sand.png")) - return img -# gravel -block(blockid=13, top_image="assets/minecraft/textures/block/gravel.png") +# Mineral overlay blocks # gold ore -block(blockid=14, top_image="assets/minecraft/textures/block/gold_ore.png") +solidmodelblock(blockid=14, name='gold_ore') # iron ore -block(blockid=15, top_image="assets/minecraft/textures/block/iron_ore.png") +solidmodelblock(blockid=15, name='iron_ore') # coal ore -block(blockid=16, top_image="assets/minecraft/textures/block/coal_ore.png") +solidmodelblock(blockid=16, name='coal_ore') + @material(blockid=[17, 162, 11306, 11307, 11308, 11309, 11310, 11311, 1008, 1009, 1126], data=list(range(12)), solid=True) def wood(self, blockid, data): - # extract orientation and wood type frorm data bits - wood_type = data & 3 - wood_orientation = data & 12 - if self.rotation == 1: - if wood_orientation == 4: wood_orientation = 8 - elif wood_orientation == 8: wood_orientation = 4 - elif self.rotation == 3: - if wood_orientation == 4: wood_orientation = 8 - elif wood_orientation == 8: wood_orientation = 4 - - # dictionary of blockid : { wood_type : (top, side) } - wood_tex = { - 17: { - 0: ("oak_log_top.png", "oak_log.png"), - 1: ("spruce_log_top.png", "spruce_log.png"), - 2: ("birch_log_top.png", "birch_log.png"), - 3: ("jungle_log_top.png", "jungle_log.png"), - }, - 162: { - 0: ("acacia_log_top.png", "acacia_log.png"), - 1: ("dark_oak_log_top.png", "dark_oak_log.png"), - }, - 11306: { - 0: ("stripped_oak_log_top.png", "stripped_oak_log.png"), - 1: ("stripped_spruce_log_top.png", "stripped_spruce_log.png"), - 2: ("stripped_birch_log_top.png", "stripped_birch_log.png"), - 3: ("stripped_jungle_log_top.png", "stripped_jungle_log.png"), - }, - 11307: { - 0: ("stripped_acacia_log_top.png", "stripped_acacia_log.png"), - 1: ("stripped_dark_oak_log_top.png", "stripped_dark_oak_log.png"), - }, - 11308: { - 0: ("oak_log.png", None), - 1: ("spruce_log.png", None), - 2: ("birch_log.png", None), - 3: ("jungle_log.png", None), - }, - 11309: { - 0: ("acacia_log.png", None), - 1: ("dark_oak_log.png", None), - }, - 11310: { - 0: ("stripped_oak_log.png", None), - 1: ("stripped_spruce_log.png", None), - 2: ("stripped_birch_log.png", None), - 3: ("stripped_jungle_log.png", None), - }, - 11311: { - 0: ("stripped_acacia_log.png", None), - 1: ("stripped_dark_oak_log.png", None), - }, - 1008: { - 0: ("warped_stem_top.png", "warped_stem.png"), - 1: ("warped_stem_top.png", "stripped_warped_stem.png"), - 2: ("crimson_stem_top.png", "crimson_stem.png"), - 3: ("crimson_stem_top.png", "stripped_crimson_stem.png"), - }, - 1009: { - 0: ("warped_stem.png", None), - 1: ("stripped_warped_stem.png", None), - 2: ("crimson_stem.png", None), - 3: ("stripped_crimson_stem.png", None), - }, - 1126: { - 0: ("mangrove_log_top.png", "mangrove_log.png"), - 1: ("stripped_mangrove_log_top.png", "stripped_mangrove_log.png"), - }, - } + type = data & 3 + + blockstate = {} + blockstate['axis'] = {0: 'y', 4: 'x', 8: 'z'}[data & 12] + + if blockid == 17: + if type == 0: + return self.build_block_from_model('oak_log', blockstate) + if type == 1: + return self.build_block_from_model('spruce_log', blockstate) + if type == 2: + return self.build_block_from_model('birch_log', blockstate) + if type == 3: + return self.build_block_from_model('jungle_log', blockstate) + + if blockid == 162: + if type == 0: + return self.build_block_from_model('acacia_log', blockstate) + if type == 1: + return self.build_block_from_model('dark_oak_log', blockstate) + + if blockid == 11306: + if type == 0: + return self.build_block_from_model('stripped_oak_log', blockstate) + if type == 1: + return self.build_block_from_model('stripped_spruce_log', blockstate) + if type == 2: + return self.build_block_from_model('stripped_birch_log', blockstate) + if type == 3: + return self.build_block_from_model('stripped_jungle_log', blockstate) + + if blockid == 11307: + if type == 0: + return self.build_block_from_model('stripped_acacia_log', blockstate) + if type == 1: + return self.build_block_from_model('stripped_dark_oak_log', blockstate) + + if blockid == 11308: + if type == 0: + return self.build_block_from_model('oak_wood', blockstate) + if type == 1: + return self.build_block_from_model('spruce_wood', blockstate) + if type == 2: + return self.build_block_from_model('birch_wood', blockstate) + if type == 3: + return self.build_block_from_model('jungle_wood', blockstate) + + if blockid == 11309: + if type == 0: + return self.build_block_from_model('acacia_wood', blockstate) + if type == 1: + return self.build_block_from_model('dark_oak_wood', blockstate) + + if blockid == 11310: + if type == 0: + return self.build_block_from_model('stripped_oak_wood', blockstate) + if type == 1: + return self.build_block_from_model('stripped_spruce_wood', blockstate) + if type == 2: + return self.build_block_from_model('stripped_birch_wood', blockstate) + if type == 3: + return self.build_block_from_model('stripped_jungle_wood', blockstate) + + if blockid == 11311: + if type == 0: + return self.build_block_from_model('stripped_acacia_wood', blockstate) + if type == 1: + return self.build_block_from_model('stripped_dark_oak_wood', blockstate) + + if blockid == 1008: + if type == 0: + return self.build_block_from_model('warped_stem', blockstate) + if type == 1: + return self.build_block_from_model('stripped_warped_stem', blockstate) + if type == 2: + return self.build_block_from_model('crimson_stem', blockstate) + if type == 3: + return self.build_block_from_model('stripped_crimson_stem', blockstate) + + if blockid == 1009: + if type == 0: + return self.build_block_from_model('warped_hyphae', blockstate) + if type == 1: + return self.build_block_from_model('stripped_warped_hyphae', blockstate) + if type == 2: + return self.build_block_from_model('crimson_hyphae', blockstate) + if type == 3: + return self.build_block_from_model('stripped_crimson_hyphae', blockstate) + + if blockid == 1126: + if type == 0: + return self.build_block_from_model('mangrove_log', blockstate) + if type == 1: + return self.build_block_from_model('stripped_mangrove_log', blockstate) + + return self.build_block_from_model('oak_log', blockstate) - top_f, side_f = wood_tex[blockid].get(wood_type, wood_tex[blockid][0]) - if not side_f: - side_f = top_f - top = self.load_image_texture(BLOCKTEX + top_f) - side = self.load_image_texture(BLOCKTEX + side_f) - - # choose orientation and paste textures - if wood_orientation == 0: - return self.build_block(top, side) - elif wood_orientation == 4: # east-west orientation - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) - elif wood_orientation == 8: # north-south orientation - return self.build_full_block(side, None, None, side.rotate(270), top) @material(blockid=[18, 161], data=list(range(16)), transparent=True, solid=True) def leaves(self, blockid, data): # mask out the bits 4 and 8 # they are used for player placed and check-for-decay blocks data = data & 0x7 - t = self.load_image_texture("assets/minecraft/textures/block/oak_leaves.png") - if (blockid, data) == (18, 1): # pine! - t = self.load_image_texture("assets/minecraft/textures/block/spruce_leaves.png") - elif (blockid, data) == (18, 2): # birth tree - t = self.load_image_texture("assets/minecraft/textures/block/birch_leaves.png") - elif (blockid, data) == (18, 3): # jungle tree - t = self.load_image_texture("assets/minecraft/textures/block/jungle_leaves.png") - elif (blockid, data) == (161, 4): # acacia tree - t = self.load_image_texture("assets/minecraft/textures/block/acacia_leaves.png") + if (blockid, data) == (18, 0): + return self.build_block_from_model("oak_leaves") + elif (blockid, data) == (18, 1): + return self.build_block_from_model("spruce_leaves") + elif (blockid, data) == (18, 2): + return self.build_block_from_model("birch_leaves") + elif (blockid, data) == (18, 3): + return self.build_block_from_model("jungle_leaves") + elif (blockid, data) == (161, 4): + return self.build_block_from_model("acacia_leaves") elif (blockid, data) == (161, 5): - t = self.load_image_texture("assets/minecraft/textures/block/dark_oak_leaves.png") + return self.build_block_from_model("dark_oak_leaves") elif (blockid, data) == (18, 6): - t = self.load_image_texture("assets/minecraft/textures/block/flowering_azalea_leaves.png") + return self.build_block_from_model("flowering_azalea_leaves") elif (blockid, data) == (18, 7): - t = self.load_image_texture("assets/minecraft/textures/block/azalea_leaves.png") - elif (blockid, data) == (18, 8): - t = self.load_image_texture("assets/minecraft/textures/block/mangrove_leaves.png") - return self.build_block(t, t) + return self.build_block_from_model("azalea_leaves") + else: + return self.build_block_from_model("mangrove_leaves") + -# sponge -block(blockid=19, top_image="assets/minecraft/textures/block/sponge.png") -# lapis lazuli ore -block(blockid=21, top_image="assets/minecraft/textures/block/lapis_ore.png") -# lapis lazuli block -block(blockid=22, top_image="assets/minecraft/textures/block/lapis_block.png") +@material(blockid=19, data=list(range(2)), solid=True) +def sponge(self, blockid, data): + if data == 0: + return self.build_block_from_model('sponge') + if data == 1: + return self.build_block_from_model('wet_sponge') -# dispenser, dropper, furnace, blast furnace, and smoker -@material(blockid=[23, 61, 158, 11362, 11364], data=list(range(14)), solid=True) +# mineral overlay +# lapis lazuli ore +solidmodelblock(blockid=21, name="lapis_ore") +# lapis lazuli block +solidmodelblock(blockid=22, name="lapis_block") + +@material(blockid=[23, 158], data=list(range(6)), solid=True) +def dropper(self, blockid, data): + facing = {0: 'down', 1: 'up', 2: 'north', 3: 'south', 4: 'west', 5: 'east'}[data] + + if blockid == 158: + if data in {0, 1}: + return self.build_block_from_model('dropper_vertical', {'facing': facing}) + return self.build_block_from_model('dropper', {'facing': facing}) + if blockid == 23: + if data in {0, 1}: + return self.build_block_from_model('dispenser_vertical', {'facing': facing}) + return self.build_block_from_model('dispenser', {'facing': facing}) + +# furnace, blast furnace, and smoker +@material(blockid=[61, 11362, 11364], data=list(range(14)), solid=True) def furnaces(self, blockid, data): - # first, do the rotation if needed - # Masked as bit 4 indicates whether the block is lit/triggered or not - if self.rotation in [1, 2, 3] and data & 0b111 in [2, 3, 4, 5]: - rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3}, - 2: {2: 3, 3: 2, 4: 5, 5: 4}, - 3: {2: 4, 3: 5, 4: 3, 5: 2}} - data = data & 0b1000 | rotation_map[self.rotation][data & 0b111] - - # Rotation angles for top texture using data & 0b111 as an index - top_rotation_map = [0, 0, 180, 0, 270, 90, 0, 0] - - # Dispenser - texture_map = {23: {'top': 'furnace_top', 'side': 'furnace_side', - 'front': 'dispenser_front', 'top_vert': 'dispenser_front_vertical'}, - # Furnace - 61: {'top': 'furnace_top', 'side': 'furnace_side', - 'front': 'furnace_front', 'front_on': 'furnace_front_on'}, - # Dropper - 158: {'top': 'furnace_top', 'side': 'furnace_side', - 'front': 'dropper_front', 'top_vert': 'dropper_front_vertical'}, - # Blast furance - 11362: {'top': 'blast_furnace_top', 'side': 'blast_furnace_side', - 'front': 'blast_furnace_front', 'front_on': 'blast_furnace_front_on'}, - # Smoker - 11364: {'top': 'smoker_top', 'side': 'smoker_side', - 'front': 'smoker_front', 'front_on': 'smoker_front_on'}} - - if data & 0b111 in [0, 1] and 'top_vert' in texture_map[blockid]: - # Block has a special top texture when it faces up/down - # This also affects which texture is used for the sides/front - top_name = 'top_vert' if data & 0b111 == 1 else 'top' - side_name = 'top' - front_name = 'top' - else: - top_name = 'top' - side_name = 'side' - # Use block's lit/on front texture if it is defined & bit 4 is set - # Note: Some front_on texture images have multiple frames, - # but load_image_texture() crops this appropriately - # as long as the image width is 16px - if data & 0b1000 == 8 and 'front_on' in texture_map[blockid]: - front_name = 'front_on' - else: - front_name = 'front' - - top = self.load_image_texture("assets/minecraft/textures/block/%s.png" % - texture_map[blockid][top_name]).copy() - top = top.rotate(top_rotation_map[data & 0b111]) - side = self.load_image_texture("assets/minecraft/textures/block/%s.png" % - texture_map[blockid][side_name]) - front = self.load_image_texture("assets/minecraft/textures/block/%s.png" % - texture_map[blockid][front_name]) - - if data & 0b111 == 3: # pointing west - return self.build_full_block(top, None, None, side, front) - elif data & 0b111 == 4: # pointing north - return self.build_full_block(top, None, None, front, side) - else: # in any other direction the front can't be seen - return self.build_full_block(top, None, None, side, side) - -# sandstone -@material(blockid=24, data=list(range(3)), solid=True) -def sandstone(self, blockid, data): - top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png") - if data == 0: # normal - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/sandstone.png")) - if data == 1: # hieroglyphic - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/chiseled_sandstone.png")) - if data == 2: # soft - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_sandstone.png")) - -# red sandstone -@material(blockid=179, data=list(range(3)), solid=True) -def sandstone(self, blockid, data): - top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png") - if data == 0: # normal - side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone.png") - return self.build_full_block(top, None, None, side, side, self.load_image_texture("assets/minecraft/textures/block/red_sandstone_bottom.png") ) - if data == 1: # hieroglyphic - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/chiseled_red_sandstone.png")) - if data == 2: # soft - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_red_sandstone.png")) - -# note block -block(blockid=25, top_image="assets/minecraft/textures/block/note_block.png") + lit = data & 0b1000 == 8 + oriention = data & 0b111 + + facing = {0: '', 1: '', 2: 'north', 3: 'south', 4: 'west', 5: 'east', 6: '', 7: ''}[oriention] + if blockid == 61: + if lit: + return self.build_block_from_model('furnace_on', {'facing': facing}) + return self.build_block_from_model('furnace', {'facing': facing}) + if blockid == 11362: + if lit: + return self.build_block_from_model('blast_furnace_on', {'facing': facing}) + return self.build_block_from_model('blast_furnace', {'facing': facing}) + if blockid == 11364: + if lit: + return self.build_block_from_model('smoker_on', {'facing': facing}) + return self.build_block_from_model('smoker', {'facing': facing}) # Bed @material(blockid=26, data=list(range(256)), transparent=True, nospawn=True) @@ -1665,12 +1947,6 @@ def tall_grass(self, blockid, data): # dead bush billboard(blockid=32, imagename="assets/minecraft/textures/block/dead_bush.png") -@material(blockid=35, data=list(range(16)), solid=True) -def wool(self, blockid, data): - texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data]) - - return self.build_block(texture, texture) - # dandelion sprite(blockid=37, imagename="assets/minecraft/textures/block/dandelion.png") @@ -1696,18 +1972,19 @@ def flower(self, blockid, data): # crimson roots sprite(blockid=1019, imagename="assets/minecraft/textures/block/crimson_roots.png") +# mineral overlay # block of gold -block(blockid=41, top_image="assets/minecraft/textures/block/gold_block.png") +solidmodelblock(blockid=41, name="gold_block") # block of iron -block(blockid=42, top_image="assets/minecraft/textures/block/iron_block.png") +solidmodelblock(blockid=42, name="iron_block") # double slabs and slabs # these wooden slabs are unobtainable without cheating, they are still # here because lots of pre-1.3 worlds use this blocks, add prismarine slabs -@material(blockid=[43, 44, 181, 182, 204, 205, 1124] + list(range(11340, 11359)) + +@material(blockid=[43, 44, 181, 182, 204, 205, 1124, 1789] + list(range(11340, 11359)) + list(range(1027, 1030)) + list(range(1072, 1080)) + list(range(1103, 1107)), data=list(range(16)), - transparent=[44, 182, 205, 1124] + list(range(11340, 11359)) + list(range(1027, 1030)) + + transparent=[44, 182, 205, 1124, 1789] + list(range(11340, 11359)) + list(range(1027, 1030)) + list(range(1072, 1080)) + list(range(1103, 1107)), solid=True) def slabs(self, blockid, data): if blockid == 44 or blockid == 182: @@ -1826,22 +2103,19 @@ def slabs(self, blockid, data): top = side = self.load_image_texture(deepslate_tex[blockid]).copy() elif blockid == 1124: top = side = self.load_image_texture("assets/minecraft/textures/block/mud_bricks.png").copy() + elif blockid == 1789: + top = side = self.load_image_texture("assets/minecraft/textures/block/mangrove_planks.png").copy() if blockid == 43 or blockid == 181 or blockid == 204: # double slab return self.build_block(top, side) - return self.build_slab_block(top, side, data & 8 == 8); + return self.build_slab_block(top, side, data & 8 == 8) -# brick block -block(blockid=45, top_image="assets/minecraft/textures/block/bricks.png") # TNT -block(blockid=46, top_image="assets/minecraft/textures/block/tnt_top.png", side_image="assets/minecraft/textures/block/tnt_side.png", nospawn=True) -# bookshelf -block(blockid=47, top_image="assets/minecraft/textures/block/oak_planks.png", side_image="assets/minecraft/textures/block/bookshelf.png") +solidmodelblock(blockid=46, name="tnt", nospawn=True) +# mineral overlay # moss stone -block(blockid=48, top_image="assets/minecraft/textures/block/mossy_cobblestone.png") -# obsidian -block(blockid=49, top_image="assets/minecraft/textures/block/obsidian.png") +solidmodelblock(blockid=48, name="mossy_cobblestone") # torch, redstone torch (off), redstone torch(on), soul_torch @material(blockid=[50, 75, 76, 1039], data=[1, 2, 3, 4, 5], transparent=True) @@ -2077,10 +2351,10 @@ def fire(self, blockid, data): return img # monster spawner -block(blockid=52, top_image="assets/minecraft/textures/block/spawner.png", transparent=True) +modelblock(blockid=52, name="spawner", solid=True, transparent=True) # wooden, cobblestone, red brick, stone brick, netherbrick, sandstone, spruce, birch, -# jungle, quartz, red sandstone, purper_stairs, crimson_stairs, warped_stairs, (dark) prismarine, +# jungle, quartz, red sandstone, purpur_stairs, crimson_stairs, warped_stairs, (dark) prismarine, # mossy brick and mossy cobblestone, stone smooth_quartz # polished_granite polished_andesite polished_diorite granite diorite andesite end_stone_bricks red_nether_brick stairs # smooth_red_sandstone blackstone polished_blackstone polished_blackstone_brick @@ -2089,7 +2363,7 @@ def fire(self, blockid, data): @material(blockid=[53, 67, 108, 109, 114, 128, 134, 135, 136, 156, 163, 164, 180, 203, 509, 510, 11337, 11338, 11339, 11370, 11371, 11374, 11375, 11376, 11377, 11378, 11379, 11380, 11381, 11382, 11383, 11384, 11415, 1030, 1031, 1032, 1064, 1065, 1066, - 1067, 1068, 1069, 1070, 1071, 1099, 1100, 1101, 1102], + 1067, 1068, 1069, 1070, 1071, 1099, 1100, 1101, 1102, 1108, 1129], data=list(range(128)), transparent=True, solid=True, nospawn=True) def stairs(self, blockid, data): # preserve the upside-down bit @@ -2140,6 +2414,7 @@ def stairs(self, blockid, data): 1030: "assets/minecraft/textures/block/blackstone.png", 1031: "assets/minecraft/textures/block/polished_blackstone.png", 1032: "assets/minecraft/textures/block/polished_blackstone_bricks.png", + 1129: "assets/minecraft/textures/block/mud_bricks.png", # Cut copper stairs 1064: "assets/minecraft/textures/block/cut_copper.png", 1065: "assets/minecraft/textures/block/exposed_cut_copper.png", @@ -2155,6 +2430,7 @@ def stairs(self, blockid, data): 1100: "assets/minecraft/textures/block/polished_deepslate.png", 1101: "assets/minecraft/textures/block/deepslate_bricks.png", 1102: "assets/minecraft/textures/block/deepslate_tiles.png", + 1108: "assets/minecraft/textures/block/mangrove_planks.png", } texture = self.load_image_texture(stair_id_to_tex[blockid]).copy() @@ -2547,44 +2823,11 @@ def wire(self, blockid, data): return img +# mineral overlay # diamond ore -block(blockid=56, top_image="assets/minecraft/textures/block/diamond_ore.png") +solidmodelblock(blockid=56, name="diamond_ore") # diamond block -block(blockid=57, top_image="assets/minecraft/textures/block/diamond_block.png") - - -# Table blocks with no facing or other properties where sides are not all the same -# Includes: Crafting table, fletching table, cartography table, smithing table -@material(blockid=[58, 11359, 11360, 11361], solid=True, nodata=True) -def block_table(self, blockid, data): - block_name = {58: "crafting_table", - 11359: "fletching_table", - 11360: "cartography_table", - 11361: "smithing_table"}[blockid] - - # Top texture doesn't vary with self.rotation, but texture rotation does - top_tex = block_name + "_top" - top_rot = [0, 270, 180, 90][self.rotation] - - # List of side textures from side 1 to 4 for each blockid - side_tex_map = {58: ["front", "side", "front", "side"], - 11359: ["front", "side", "side", "front"], - 11360: ["side3", "side3", "side2", "side1"], - 11361: ["front", "side", "side", "front"]}[blockid] - # Determine which side textures to use - side3_id = [2, 3, 1, 0][self.rotation] - side4_id = [3, 1, 0, 2][self.rotation] - side3_tex = block_name + "_" + side_tex_map[side3_id] - side4_tex = block_name + "_" + side_tex_map[side4_id] - - tex_path = "assets/minecraft/textures/block" - top = self.load_image_texture("{}/{}.png".format(tex_path, top_tex)).copy() - side3 = self.load_image_texture("{}/{}.png".format(tex_path, side3_tex)) - side4 = self.load_image_texture("{}/{}.png".format(tex_path, side4_tex)).copy() - top = top.rotate(top_rot) - side4 = side4.transpose(Image.FLIP_LEFT_RIGHT) - - return self.build_full_block(top, None, None, side3, side4, None) +solidmodelblock(blockid=57, name="diamond_block") @material(blockid=11366, data=list(range(8)), transparent=True, solid=True, nospawn=True) @@ -2726,22 +2969,10 @@ def darken_image(img_src, darken_value): @material(blockid=11367, data=list(range(4)), solid=True) def loom(self, blockid, data): - # Do rotation - data = (self.rotation + data) % 4 - - top_rot = [180, 90, 0, 270][data] - side3_tex = "front" if data == 1 else "side" - side4_tex = "front" if data == 0 else "side" - - tex_path = "assets/minecraft/textures/block" - top = self.load_image_texture("{}/loom_top.png".format(tex_path)).copy() - side3 = self.load_image_texture("{}/loom_{}.png".format(tex_path, side3_tex)) - side4 = self.load_image_texture("{}/loom_{}.png".format(tex_path, side4_tex)).copy() - top = top.rotate(top_rot) - side4 = side4.transpose(Image.FLIP_LEFT_RIGHT) - - return self.build_full_block(top, None, None, side3, side4, None) - + # normalize data so it can be used by a generic method + blockstate = {} + blockstate['facing'] = {0:'south', 1:'west', 2:'north', 3:'east'}[data] + return self.build_block_from_model('loom', blockstate) @material(blockid=11368, data=list(range(4)), transparent=True, solid=True, nospawn=True) def stonecutter(self, blockid, data): @@ -2869,17 +3100,7 @@ def create_tile(img_src, coord_crop, coord_paste, scale): # crops with 8 data values (like wheat) @material(blockid=59, data=list(range(8)), transparent=True, nospawn=True) def crops8(self, blockid, data): - raw_crop = self.load_image_texture("assets/minecraft/textures/block/wheat_stage%d.png" % data) - crop1 = self.transform_image_top(raw_crop) - crop2 = self.transform_image_side(raw_crop) - crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), self.bgcolor) - alpha_over(img, crop1, (0,12), crop1) - alpha_over(img, crop2, (6,3), crop2) - alpha_over(img, crop3, (6,3), crop3) - return img - + return self.build_block_from_model("wheat_stage%d" % data) # farmland and grass path (15/16 blocks) @material(blockid=[60, 208], data=list(range(2)), solid=True, transparent=True, nospawn=True) @@ -2971,7 +3192,7 @@ def signpost(self, blockid, data): # wooden and iron door # uses pseudo-ancildata found in iterate.c -@material(blockid=[64,71,193,194,195,196,197, 499, 500], data=list(range(32)), transparent=True) +@material(blockid=[64,71,193,194,195,196,197,457, 499, 500], data=list(range(32)), transparent=True) def door(self, blockid, data): #Masked to not clobber block top/bottom & swung info if self.rotation == 1: @@ -3005,6 +3226,8 @@ def door(self, blockid, data): raw_door = self.load_image_texture("assets/minecraft/textures/block/acacia_door_top.png") elif blockid == 197: # dark_oak door raw_door = self.load_image_texture("assets/minecraft/textures/block/dark_oak_door_top.png") + elif blockid == 457: # mangrove door + raw_door = self.load_image_texture("assets/minecraft/textures/block/mangrove_door_top.png") elif blockid == 499: # crimson door raw_door = self.load_image_texture("assets/minecraft/textures/block/crimson_door_top.png") elif blockid == 500: # warped door @@ -3024,6 +3247,8 @@ def door(self, blockid, data): raw_door = self.load_image_texture("assets/minecraft/textures/block/acacia_door_bottom.png") elif blockid == 197: # dark_oak door raw_door = self.load_image_texture("assets/minecraft/textures/block/dark_oak_door_bottom.png") + elif blockid == 457: # mangrove door + raw_door = self.load_image_texture("assets/minecraft/textures/block/mangrove_door_bottom.png") elif blockid == 499: # crimson door raw_door = self.load_image_texture("assets/minecraft/textures/block/crimson_door_bottom.png") elif blockid == 500: # warped door @@ -3171,7 +3396,7 @@ def ladder(self, blockid, data): # wall signs -@material(blockid=[68,11407,11408,11409,11410,11411,11412,12507,12508], data=[2, 3, 4, 5], transparent=True) +@material(blockid=[68,454,11407,11408,11409,11410,11411,11412,12507,12508], data=[2, 3, 4, 5], transparent=True) def wall_sign(self, blockid, data): # wall sign # first rotations @@ -3193,6 +3418,7 @@ def wall_sign(self, blockid, data): # wall sign sign_texture = { 68: "oak_planks.png", + 454: "mangrove_planks.png", 11407: "oak_planks.png", 11408: "spruce_planks.png", 11409: "birch_planks.png", @@ -3383,7 +3609,7 @@ def levers(self, blockid, data): return img # wooden and stone pressure plates, and weighted pressure plates -@material(blockid=[70, 72,147,148,11301,11302,11303,11304,11305, 1033,11517,11518], data=[0,1], transparent=True) +@material(blockid=[70, 72,147,148,1127,11301,11302,11303,11304,11305, 1033,11517,11518], data=[0,1], transparent=True) def pressure_plate(self, blockid, data): texture_name = {70:"assets/minecraft/textures/block/stone.png", # stone 72:"assets/minecraft/textures/block/oak_planks.png", # oak @@ -3396,7 +3622,8 @@ def pressure_plate(self, blockid, data): 11518:"assets/minecraft/textures/block/warped_planks.png", # warped 147:"assets/minecraft/textures/block/gold_block.png", # light golden 148:"assets/minecraft/textures/block/iron_block.png", # heavy iron - 1033:"assets/minecraft/textures/block/polished_blackstone.png" + 1033:"assets/minecraft/textures/block/polished_blackstone.png", + 1127:"assets/minecraft/textures/block/mangrove_planks.png" }[blockid] t = self.load_image_texture(texture_name).copy() @@ -3423,11 +3650,12 @@ def pressure_plate(self, blockid, data): return img +# mineral overlay # normal and glowing redstone ore -block(blockid=[73, 74], top_image="assets/minecraft/textures/block/redstone_ore.png") +solidmodelblock(blockid=[73], name="redstone_ore") # stone and wood buttons -@material(blockid=(77,143,11326,11327,11328,11329,11330,1034,11515,11516), data=list(range(16)), transparent=True) +@material(blockid=(77,143,1128,11326,11327,11328,11329,11330,1034,11515,11516), data=list(range(16)), transparent=True) def buttons(self, blockid, data): # 0x8 is set if the button is pressed mask this info and render @@ -3456,6 +3684,7 @@ def buttons(self, blockid, data): texturepath = {77:"assets/minecraft/textures/block/stone.png", 143:"assets/minecraft/textures/block/oak_planks.png", + 1128:"assets/minecraft/textures/block/mangrove_planks.png", 11326:"assets/minecraft/textures/block/spruce_planks.png", 11327:"assets/minecraft/textures/block/birch_planks.png", 11328:"assets/minecraft/textures/block/jungle_planks.png", @@ -3635,9 +3864,6 @@ def snow(self, blockid, data): return img -# snow block -block(blockid=80, top_image="assets/minecraft/textures/block/snow.png") - # cactus @material(blockid=81, data=list(range(15)), transparent=True, solid=True, nospawn=True) def cactus(self, blockid, data): @@ -3663,22 +3889,14 @@ def cactus(self, blockid, data): return img -# clay block -block(blockid=82, top_image="assets/minecraft/textures/block/clay.png") - # sugar cane @material(blockid=83, data=list(range(16)), transparent=True) def sugar_cane(self, blockid, data): tex = self.load_image_texture("assets/minecraft/textures/block/sugar_cane.png") return self.build_sprite(tex) -# jukebox -@material(blockid=84, data=list(range(16)), solid=True) -def jukebox(self, blockid, data): - return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jukebox_top.png"), self.load_image_texture("assets/minecraft/textures/block/note_block.png")) - # nether and normal fences -@material(blockid=[85, 188, 189, 190, 191, 192, 113, 511, 512], data=list(range(16)), transparent=True, nospawn=True) +@material(blockid=[85, 188, 189, 190, 191, 192, 113, 456, 511, 512], data=list(range(16)), transparent=True, nospawn=True) def fence(self, blockid, data): # create needed images for Big stick fence if blockid == 85: # normal fence @@ -3705,6 +3923,10 @@ def fence(self, blockid, data): fence_top = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy() fence_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy() fence_small_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy() + elif blockid == 456: # mangrove_fence + fence_top = self.load_image_texture("assets/minecraft/textures/block/mangrove_planks.png").copy() + fence_side = self.load_image_texture("assets/minecraft/textures/block/mangrove_planks.png").copy() + fence_small_side = self.load_image_texture("assets/minecraft/textures/block/mangrove_planks.png").copy() elif blockid == 511: # crimson_fence fence_top = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy() fence_side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy() @@ -3810,56 +4032,25 @@ def draw_west(): return img -# pumpkin -@material(blockid=[86, 91,11300], data=list(range(4)), solid=True) -def pumpkin(self, blockid, data): # pumpkins, jack-o-lantern - # rotation - if self.rotation == 1: - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif self.rotation == 2: - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif self.rotation == 3: - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - - # texture generation - top = self.load_image_texture("assets/minecraft/textures/block/pumpkin_top.png") - frontName = {86: "assets/minecraft/textures/block/pumpkin_side.png", - 91: "assets/minecraft/textures/block/jack_o_lantern.png", - 11300: "assets/minecraft/textures/block/carved_pumpkin.png" - }[blockid] - front = self.load_image_texture(frontName) - side = self.load_image_texture("assets/minecraft/textures/block/pumpkin_side.png") - - if data == 0: # pointing west - img = self.build_full_block(top, None, None, side, front) - - elif data == 1: # pointing north - img = self.build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = self.build_full_block(top, None, None, side, side) - - return img - +@material(blockid=91, data=list(range(4)), solid=True) +def jack_o_lantern(self, blockid, data): + # normalize data so it can be used by a generic method + blockstate = {} + blockstate['facing'] = {0:'south', 1:'west', 2:'north', 3:'east'}[data] + return self.build_block_from_model('jack_o_lantern', blockstate) + +@material(blockid=11300, data=list(range(4)), solid=True) +def carved_pumpkin(self, blockid, data): + # normalize data so it can be used by a generic method + blockstate = {} + blockstate['facing'] = {0:'south', 1:'west', 2:'north', 3:'east'}[data] + return self.build_block_from_model('carved_pumpkin', blockstate) + +# nether roof # netherrack -block(blockid=87, top_image="assets/minecraft/textures/block/netherrack.png") - +solidmodelblock(blockid=87, name="netherrack") # soul sand -block(blockid=88, top_image="assets/minecraft/textures/block/soul_sand.png") - -# glowstone -block(blockid=89, top_image="assets/minecraft/textures/block/glowstone.png") -# shroomlight -block(blockid=1011, top_image="assets/minecraft/textures/block/shroomlight.png") +solidmodelblock(blockid=88, name="soul_sand") # portal @material(blockid=90, data=[1, 2, 4, 5, 8, 10], transparent=True) @@ -4221,7 +4412,7 @@ def build_torch(active): # trapdoor # the trapdoor is looks like a sprite when opened, that's not good -@material(blockid=[96,167,11332,11333,11334,11335,11336,12501,12502], data=list(range(16)), transparent=True, nospawn=True) +@material(blockid=[96,167,451,11332,11333,11334,11335,11336,12501,12502], data=list(range(16)), transparent=True, nospawn=True) def trapdoor(self, blockid, data): # rotation @@ -4245,6 +4436,7 @@ def trapdoor(self, blockid, data): # texture generation texturepath = {96:"assets/minecraft/textures/block/oak_trapdoor.png", 167:"assets/minecraft/textures/block/iron_trapdoor.png", + 451:"assets/minecraft/textures/block/mangrove_trapdoor.png", 11332:"assets/minecraft/textures/block/spruce_trapdoor.png", 11333:"assets/minecraft/textures/block/birch_trapdoor.png", 11334:"assets/minecraft/textures/block/jungle_trapdoor.png", @@ -4279,37 +4471,27 @@ def trapdoor(self, blockid, data): return img # block with hidden silverfish (stone, cobblestone and stone brick) -@material(blockid=97, data=list(range(3)), solid=True) +@material(blockid=[4, 97, 98], data=list(range(3)), solid=True) def hidden_silverfish(self, blockid, data): - if data == 0: # stone - t = self.load_image_texture("assets/minecraft/textures/block/stone.png") - elif data == 1: # cobblestone - t = self.load_image_texture("assets/minecraft/textures/block/cobblestone.png") - elif data == 2: # stone brick - t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png") - - img = self.build_block(t, t) - - return img - -# stone brick -@material(blockid=98, data=list(range(4)), solid=True) -def stone_brick(self, blockid, data): - if data == 0: # normal - t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png") - elif data == 1: # mossy - t = self.load_image_texture("assets/minecraft/textures/block/mossy_stone_bricks.png") - elif data == 2: # cracked - t = self.load_image_texture("assets/minecraft/textures/block/cracked_stone_bricks.png") - elif data == 3: # "circle" stone brick - t = self.load_image_texture("assets/minecraft/textures/block/chiseled_stone_bricks.png") - - img = self.build_full_block(t, None, None, t, t) - - return img - + if blockid == 4: + return self.build_block_from_model("cobblestone") + if blockid == 97: + if data == 0: + return self.build_block_from_model("stone") + if data == 1: + return self.build_block_from_model("cobblestone") + else: + return self.build_block_from_model("stone_bricks") + if blockid == 98: + if data == 0: # normal + return self.build_block_from_model("stone_bricks") + elif data == 1: # mossy + return self.build_block_from_model("mossy_stone_bricks") + elif data == 2: # cracked + return self.build_block_from_model("cracked_stone_bricks") + elif data == 3: # "circle" stone brick + return self.build_block_from_model("chiseled_stone_bricks") -# huge brown/red mushrooms, and mushroom stems @material(blockid=[99, 100, 139], data=list(range(64)), solid=True) def huge_mushroom(self, blockid, data): # Re-arrange the bits in data based on self.rotation @@ -4324,12 +4506,6 @@ def huge_mushroom(self, blockid, data): 3: [6, 5, 1, 4, 3, 2]} new_data = 0 - # Add the ith bit to new_data then shift left one at a time, - # re-ordering data's bits in the order specified in bit_map - for i in bit_map[self.rotation]: - new_data = new_data << 1 - new_data |= (data >> (i - 1)) & 1 - data = new_data # texture generation texture_map = {99: "brown_mushroom_block", @@ -4346,7 +4522,6 @@ def huge_mushroom(self, blockid, data): return self.build_full_block(side_up, None, None, side_west, side_south) - # iron bars and glass pane # TODO glass pane is not a sprite, it has a texture for the side, # at the moment is not used @@ -4416,9 +4591,6 @@ def draw_bottom_left(): return img -# melon -block(blockid=103, top_image="assets/minecraft/textures/block/melon_top.png", side_image="assets/minecraft/textures/block/melon_side.png", solid=True) - # pumpkin and melon stem # TODO To render it as in game needs from pseudo data and ancil data: # once fully grown the stem bends to the melon/pumpkin block, @@ -4483,7 +4655,7 @@ def vines(self, blockid, data): # fence gates -@material(blockid=[107, 183, 184, 185, 186, 187, 513, 514], data=list(range(8)), transparent=True, nospawn=True) +@material(blockid=[107, 183, 184, 185, 186, 187, 455, 513, 514], data=list(range(8)), transparent=True, nospawn=True) def fence_gate(self, blockid, data): # rotation @@ -4522,6 +4694,8 @@ def fence_gate(self, blockid, data): gate_side = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png").copy() elif blockid == 187: # Acacia gate_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy() + elif blockid == 455: # Mangrove + gate_side = self.load_image_texture("assets/minecraft/textures/block/mangrove_planks.png").copy() elif blockid == 513: # Crimson gate_side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy() elif blockid == 514: # Warped @@ -4584,13 +4758,6 @@ def fence_gate(self, blockid, data): return img -# mycelium -block(blockid=110, top_image="assets/minecraft/textures/block/mycelium_top.png", side_image="assets/minecraft/textures/block/mycelium_side.png") -# warped_nylium & crimson_nylium -block(blockid=1006, top_image="assets/minecraft/textures/block/warped_nylium.png", side_image="assets/minecraft/textures/block/warped_nylium_side.png") -block(blockid=1007, top_image="assets/minecraft/textures/block/crimson_nylium.png", side_image="assets/minecraft/textures/block/crimson_nylium_side.png") - - # lilypad # At the moment of writing this lilypads has no ancil data and their # orientation depends on their position on the map. So it uses pseudo @@ -4609,20 +4776,6 @@ def lilypad(self, blockid, data): return self.build_full_block(None, None, None, None, None, t) -# nether bricks -@material(blockid=112, data=list(range(3)), solid=True) -def nether_bricks(self, blockid, data): - if data == 0: # normal - t = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png") - elif data == 1: # cracked - t = self.load_image_texture("assets/minecraft/textures/block/cracked_nether_bricks.png") - elif data == 2: # chiseled - t = self.load_image_texture("assets/minecraft/textures/block/chiseled_nether_bricks.png") - - img = self.build_full_block(t, None, None, t, t) - - return img - # nether wart @material(blockid=115, data=list(range(4)), transparent=True) def nether_wart(self, blockid, data): @@ -4639,15 +4792,10 @@ def nether_wart(self, blockid, data): return img # enchantment table -# TODO there's no book at the moment -@material(blockid=116, transparent=True, nodata=True) +# there's no book at the moment because it is not a part of the model +@material(blockid=116, transparent=True) def enchantment_table(self, blockid, data): - # no book at the moment - top = self.load_image_texture("assets/minecraft/textures/block/enchanting_table_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/enchanting_table_side.png") - img = self.build_full_block((top, 4), None, None, side, side) - - return img + return self.build_block_from_model('enchanting_table') # brewing stand # TODO this is a place holder, is a 2d image pasted @@ -4689,7 +4837,7 @@ def cauldron(self, blockid, data): # end portal and end_gateway -@material(blockid=[119,209], transparent=True, nodata=True) +@material(blockid=[119,209], transparent=True) def end_portal(self, blockid, data): img = Image.new("RGBA", (24,24), self.bgcolor) # generate a black texure with white, blue and grey dots resembling stars @@ -4703,7 +4851,7 @@ def end_portal(self, blockid, data): return self.build_block(t, t) t = self.transform_image_top(t) - alpha_over(img, t, (0,0), t) + alpha_over(img, t, (0,6), t) return img @@ -4711,76 +4859,24 @@ def end_portal(self, blockid, data): # end portal frame (data range 8 to get all orientations of filled) @material(blockid=120, data=list(range(8)), transparent=True, solid=True, nospawn=True) def end_portal_frame(self, blockid, data): - # Do rotation, only seems to affect ender eye & top of frame - data = data & 0b100 | ((self.rotation + (data & 0b11)) % 4) - - top = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_top.png").copy() - top = top.rotate((data % 4) * 90) - side = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_side.png") - img = self.build_full_block((top, 4), None, None, side, side) - if data & 0x4 == 0x4: # ender eye on it - # generate the eye - eye_t = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_eye.png").copy() - eye_t_s = eye_t.copy() - # cut out from the texture the side and the top of the eye - ImageDraw.Draw(eye_t).rectangle((0, 0, 15, 4), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - ImageDraw.Draw(eye_t_s).rectangle((0, 4, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - # transform images and paste - eye = self.transform_image_top(eye_t.rotate((data % 4) * 90)) - eye_s = self.transform_image_side(eye_t_s) - eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) - alpha_over(img, eye_s, (5, 5), eye_s) - alpha_over(img, eye_os, (9, 5), eye_os) - alpha_over(img, eye, (0, 0), eye) - - return img - - -# end stone -block(blockid=121, top_image="assets/minecraft/textures/block/end_stone.png") - -# dragon egg -# NOTE: this isn't a block, but I think it's better than nothing -block(blockid=122, top_image="assets/minecraft/textures/block/dragon_egg.png") - -# inactive redstone lamp -block(blockid=123, top_image="assets/minecraft/textures/block/redstone_lamp.png") - -# active redstone lamp -block(blockid=124, top_image="assets/minecraft/textures/block/redstone_lamp_on.png") - + facing = {2: 'north', 0: 'south', 1: 'west', 3: 'east'}[data % 4] + if data & 0x4 == 0x4: + return self.build_block_from_model('end_portal_frame_filled', {'facing': facing}) + return self.build_block_from_model('end_portal_frame', {'facing': facing}) + +@material(blockid=[123], data=list(range(2)), solid=True) +def redstone_lamp(self, blockid, data): + if data == 0: # off + return self.build_block_from_model('redstone_lamp') + return self.build_block_from_model('redstone_lamp_on') + # daylight sensor. @material(blockid=[151,178], transparent=True) def daylight_sensor(self, blockid, data): if blockid == 151: # daylight sensor - top = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_top.png") + return self.build_block_from_model('daylight_detector') else: # inverted daylight sensor - top = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_inverted_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_side.png") - - # cut the side texture in half - mask = side.crop((0,8,16,16)) - side = Image.new(side.mode, side.size, self.bgcolor) - alpha_over(side, mask,(0,0,16,8), mask) - - # plain slab - top = self.transform_image_top(top) - side = self.transform_image_side(side) - otherside = side.transpose(Image.FLIP_LEFT_RIGHT) - - sidealpha = side.split()[3] - side = ImageEnhance.Brightness(side).enhance(0.9) - side.putalpha(sidealpha) - othersidealpha = otherside.split()[3] - otherside = ImageEnhance.Brightness(otherside).enhance(0.8) - otherside.putalpha(othersidealpha) - - img = Image.new("RGBA", (24,24), self.bgcolor) - alpha_over(img, side, (0,12), side) - alpha_over(img, otherside, (12,12), otherside) - alpha_over(img, top, (0,6), top) - - return img + return self.build_block_from_model('daylight_detector_inverted') # wooden double and normal slabs @@ -4813,11 +4909,9 @@ def wooden_slabs(self, blockid, data): return self.build_slab_block(top, side, data & 8 == 8); +# mineral overlay # emerald ore -block(blockid=129, top_image="assets/minecraft/textures/block/emerald_ore.png") - -# emerald block -block(blockid=133, top_image="assets/minecraft/textures/block/emerald_block.png") +solidmodelblock(blockid=129, name="emerald_ore") # cocoa plant @material(blockid=127, data=list(range(12)), transparent=True) @@ -4900,49 +4994,9 @@ def cocoa_plant(self, blockid, data): return img -# command block -@material(blockid=[137,210,211], solid=True, nodata=True) -def command_block(self, blockid, data): - if blockid == 210: - front = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_front.png") - side = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_side.png") - back = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_back.png") - elif blockid == 211: - front = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_front.png") - side = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_side.png") - back = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_back.png") - else: - front = self.load_image_texture("assets/minecraft/textures/block/command_block_front.png") - side = self.load_image_texture("assets/minecraft/textures/block/command_block_side.png") - back = self.load_image_texture("assets/minecraft/textures/block/command_block_back.png") - return self.build_full_block(side, side, back, front, side) - -# beacon block -# at the moment of writing this, it seems the beacon block doens't use -# the data values -@material(blockid=138, transparent=True, nodata = True) -def beacon(self, blockid, data): - # generate the three pieces of the block - t = self.load_image_texture("assets/minecraft/textures/block/glass.png") - glass = self.build_block(t,t) - t = self.load_image_texture("assets/minecraft/textures/block/obsidian.png") - obsidian = self.build_full_block((t,12),None, None, t, t) - obsidian = obsidian.resize((20,20), Image.ANTIALIAS) - t = self.load_image_texture("assets/minecraft/textures/block/beacon.png") - crystal = self.build_block(t,t) - crystal = crystal.resize((16,16),Image.ANTIALIAS) - - # compose the block - img = Image.new("RGBA", (24,24), self.bgcolor) - alpha_over(img, obsidian, (2, 4), obsidian) - alpha_over(img, crystal, (4,3), crystal) - alpha_over(img, glass, (0,0), glass) - - return img - # cobblestone and mossy cobblestone walls, chorus plants, mossy stone brick walls # one additional bit of data value added for mossy and cobblestone -@material(blockid=[199]+list(range(1792, 1812 + 1)), data=list(range(32)), transparent=True, nospawn=True) +@material(blockid=[199]+list(range(1792, 1813 + 1)), data=list(range(32)), transparent=True, nospawn=True) def cobblestone_wall(self, blockid, data): walls_id_to_tex = { 199: "assets/minecraft/textures/block/chorus_plant.png", # chorus plants @@ -4967,6 +5021,7 @@ def cobblestone_wall(self, blockid, data): 1810: "assets/minecraft/textures/block/polished_deepslate.png", 1811: "assets/minecraft/textures/block/deepslate_bricks.png", 1812: "assets/minecraft/textures/block/deepslate_tiles.png", + 1813: "assets/minecraft/textures/block/mud_bricks.png", } t = self.load_image_texture(walls_id_to_tex[blockid]).copy() @@ -5114,128 +5169,27 @@ def crops4(self, blockid, data): return img -# anvils @material(blockid=145, data=list(range(12)), transparent=True, nospawn=True) def anvil(self, blockid, data): - # anvils only have two orientations, invert it for rotations 1 and 3 - orientation = data & 0x1 - if self.rotation in (1, 3): - if orientation == 1: - orientation = 0 - else: - orientation = 1 - - # get the correct textures - # the bits 0x4 and 0x8 determine how damaged is the anvil + facing = {2: 'north', 0: 'south', 1: 'west', 3: 'east'}[data % 4] if (data & 0xc) == 0: # non damaged anvil - top = self.load_image_texture("assets/minecraft/textures/block/anvil_top.png") + return self.build_block_from_model('anvil', {'facing': facing}) elif (data & 0xc) == 0x4: # slightly damaged - top = self.load_image_texture("assets/minecraft/textures/block/chipped_anvil_top.png") + return self.build_block_from_model('chipped_anvil', {'facing': facing}) elif (data & 0xc) == 0x8: # very damaged - top = self.load_image_texture("assets/minecraft/textures/block/damaged_anvil_top.png") - # everything else use this texture - big_side = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy() - small_side = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy() - base = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy() - small_base = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy() - - # cut needed patterns - ImageDraw.Draw(big_side).rectangle((0, 8, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - ImageDraw.Draw(small_side).rectangle((0, 0, 2, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - ImageDraw.Draw(small_side).rectangle((13, 0, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - ImageDraw.Draw(small_side).rectangle((0, 8, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0)) - ImageDraw.Draw(base).rectangle((0, 0, 15, 15), outline=(0, 0, 0, 0)) - ImageDraw.Draw(base).rectangle((1, 1, 14, 14), outline=(0, 0, 0, 0)) - ImageDraw.Draw(small_base).rectangle((0, 0, 15, 15), outline=(0, 0, 0, 0)) - ImageDraw.Draw(small_base).rectangle((1, 1, 14, 14), outline=(0, 0, 0, 0)) - ImageDraw.Draw(small_base).rectangle((2, 2, 13, 13), outline=(0, 0, 0, 0)) - ImageDraw.Draw(small_base).rectangle((3, 3, 12, 12), outline=(0, 0, 0, 0)) - - # check orientation and compose the anvil - if orientation == 1: # bottom-left top-right - top = top.rotate(90) - left_side = small_side - left_pos = (1, 6) - right_side = big_side - right_pos = (10, 5) - else: # top-left bottom-right - right_side = small_side - right_pos = (12, 6) - left_side = big_side - left_pos = (3, 5) - - img = Image.new("RGBA", (24, 24), self.bgcolor) - - # darken sides - alpha = big_side.split()[3] - big_side = ImageEnhance.Brightness(big_side).enhance(0.8) - big_side.putalpha(alpha) - alpha = small_side.split()[3] - small_side = ImageEnhance.Brightness(small_side).enhance(0.9) - small_side.putalpha(alpha) - alpha = base.split()[3] - base_d = ImageEnhance.Brightness(base).enhance(0.8) - base_d.putalpha(alpha) - - # compose - base = self.transform_image_top(base) - base_d = self.transform_image_top(base_d) - small_base = self.transform_image_top(small_base) - top = self.transform_image_top(top) - - alpha_over(img, base_d, (0, 12), base_d) - alpha_over(img, base_d, (0, 11), base_d) - alpha_over(img, base_d, (0, 10), base_d) - alpha_over(img, small_base, (0, 10), small_base) - - alpha_over(img, top, (0, 1), top) # Fix gap between block edges - alpha_over(img, top, (0, 0), top) - - left_side = self.transform_image_side(left_side) - right_side = self.transform_image_side(right_side).transpose(Image.FLIP_LEFT_RIGHT) - - alpha_over(img, left_side, left_pos, left_side) - alpha_over(img, right_side, right_pos, right_side) - - return img - - -# block of redstone -block(blockid=152, top_image="assets/minecraft/textures/block/redstone_block.png") + return self.build_block_from_model('damaged_anvil', {'facing': facing}) +# mineral overlay # nether quartz ore -block(blockid=153, top_image="assets/minecraft/textures/block/nether_quartz_ore.png") +solidmodelblock(blockid=153, name="nether_quartz_ore") # block of quartz -@material(blockid=155, data=list(range(5)), solid=True) -def quartz_block(self, blockid, data): - - if data in (0,1): # normal and chiseled quartz block - if data == 0: - top = self.load_image_texture("assets/minecraft/textures/block/quartz_block_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/quartz_block_side.png") - else: - top = self.load_image_texture("assets/minecraft/textures/block/chiseled_quartz_block_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/chiseled_quartz_block.png") - return self.build_block(top, side) - - # pillar quartz block with orientation - top = self.load_image_texture("assets/minecraft/textures/block/quartz_pillar_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/quartz_pillar.png").copy() - if data == 2: # vertical - return self.build_block(top, side) - elif data == 3: # north-south oriented - if self.rotation in (0,2): - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) - return self.build_full_block(side, None, None, side.rotate(90), top) - - elif data == 4: # east-west oriented - if self.rotation in (0,2): - return self.build_full_block(side, None, None, side.rotate(90), top) - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) +@material(blockid=155, data=list(range(3)), solid=True) +def quartz_pillar(self, blockid, data): + return self.build_block_from_model('quartz_pillar', blockstate={'axis': ({0: 'y', 1: 'x', 2: 'z'}[data])}) # hopper -@material(blockid=154, data=list(range(4)), transparent=True) +@material(blockid=154, data=list(range(6)), transparent=True) def hopper(self, blockid, data): #build the top side = self.load_image_texture("assets/minecraft/textures/block/hopper_outside.png") @@ -5258,96 +5212,10 @@ def hopper(self, blockid, data): return img -# slime block -block(blockid=165, top_image="assets/minecraft/textures/block/slime_block.png") - -# prismarine block -@material(blockid=168, data=list(range(3)), solid=True) -def prismarine_block(self, blockid, data): - - if data == 0: # prismarine - t = self.load_image_texture("assets/minecraft/textures/block/prismarine.png") - elif data == 1: # prismarine bricks - t = self.load_image_texture("assets/minecraft/textures/block/prismarine_bricks.png") - elif data == 2: # dark prismarine - t = self.load_image_texture("assets/minecraft/textures/block/dark_prismarine.png") - - img = self.build_block(t, t) - - return img - -# sea lantern -block(blockid=169, top_image="assets/minecraft/textures/block/sea_lantern.png") - # hay block -@material(blockid=170, data=list(range(9)), solid=True) +@material(blockid=170, data=list(range(3)), solid=True) def hayblock(self, blockid, data): - top = self.load_image_texture("assets/minecraft/textures/block/hay_block_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/hay_block_side.png") - - if self.rotation == 1: - if data == 4: data = 8 - elif data == 8: data = 4 - elif self.rotation == 3: - if data == 4: data = 8 - elif data == 8: data = 4 - - # choose orientation and paste textures - if data == 4: # east-west orientation - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) - elif data == 8: # north-south orientation - return self.build_full_block(side, None, None, side.rotate(90), top) - else: - return self.build_block(top, side) - - -# carpet - wool block that's small? -@material(blockid=171, data=list(range(17)), transparent=True) -def carpet(self, blockid, data): - if data < 16: - texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data]) - elif data == 16: - texture = self.load_image_texture("assets/minecraft/textures/block/moss_block.png") - - return self.build_full_block((texture,15),texture,texture,texture,texture) - -#clay block -block(blockid=172, top_image="assets/minecraft/textures/block/terracotta.png") - -#stained hardened clay -@material(blockid=159, data=list(range(16)), solid=True) -def stained_clay(self, blockid, data): - texture = self.load_image_texture("assets/minecraft/textures/block/%s_terracotta.png" % color_map[data]) - - return self.build_block(texture,texture) - -#coal block -block(blockid=173, top_image="assets/minecraft/textures/block/coal_block.png") - -# packed ice block -block(blockid=174, top_image="assets/minecraft/textures/block/packed_ice.png") - -#blue ice -block(blockid=11312, top_image="assets/minecraft/textures/block/blue_ice.png") - -#smooth stones -block(blockid=11313, top_image="assets/minecraft/textures/block/smooth_stone.png") # stone -block(blockid=11314, top_image="assets/minecraft/textures/block/sandstone_top.png") # sandstone -block(blockid=11315, top_image="assets/minecraft/textures/block/red_sandstone_top.png") # red sandstone - -#coral blocks -block(blockid=11316, top_image="assets/minecraft/textures/block/brain_coral_block.png") -block(blockid=11317, top_image="assets/minecraft/textures/block/bubble_coral_block.png") -block(blockid=11318, top_image="assets/minecraft/textures/block/fire_coral_block.png") -block(blockid=11319, top_image="assets/minecraft/textures/block/horn_coral_block.png") -block(blockid=11320, top_image="assets/minecraft/textures/block/tube_coral_block.png") - -#dead coral blocks -block(blockid=11321, top_image="assets/minecraft/textures/block/dead_brain_coral_block.png") -block(blockid=11322, top_image="assets/minecraft/textures/block/dead_bubble_coral_block.png") -block(blockid=11323, top_image="assets/minecraft/textures/block/dead_fire_coral_block.png") -block(blockid=11324, top_image="assets/minecraft/textures/block/dead_horn_coral_block.png") -block(blockid=11325, top_image="assets/minecraft/textures/block/dead_tube_coral_block.png") + return self.build_block_from_model('hay_block', blockstate={'axis': ({0: 'y', 1: 'x', 2: 'z'}[data])}) @material(blockid=175, data=list(range(16)), transparent=True) def flower(self, blockid, data): @@ -5375,96 +5243,33 @@ def flower(self, blockid, data): def chorus_flower(self, blockid, data): # aged 5, dead if data == 5: - texture = self.load_image_texture("assets/minecraft/textures/block/chorus_flower_dead.png") + return self.build_block_from_model("chorus_flower_dead") else: - texture = self.load_image_texture("assets/minecraft/textures/block/chorus_flower.png") - - return self.build_block(texture,texture) - -# purpur block -block(blockid=201, top_image="assets/minecraft/textures/block/purpur_block.png") + return self.build_block_from_model("chorus_flower") # purpur pillar @material(blockid=202, data=list(range(3)), solid=True) def purpur_pillar(self, blockid, data): - top=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar_top.png") - side=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar.png") - return self.build_axis_block(top, side, data) - -# end brick -block(blockid=206, top_image="assets/minecraft/textures/block/end_stone_bricks.png") + return self.build_block_from_model('purpur_pillar', blockstate={'axis': ({0: 'y', 1: 'x', 2: 'z'}[data])}) # frosted ice @material(blockid=212, data=list(range(4)), solid=True) def frosted_ice(self, blockid, data): - img = self.load_image_texture("assets/minecraft/textures/block/frosted_ice_%d.png" % data) - return self.build_block(img, img) + return self.build_block_from_model("frosted_ice_%d" % data) -# magma block -block(blockid=213, top_image="assets/minecraft/textures/block/magma.png") - -# nether wart block -block(blockid=214, top_image="assets/minecraft/textures/block/nether_wart_block.png") -# warped wart block -block(blockid=1010, top_image="assets/minecraft/textures/block/warped_wart_block.png") - -# red nether brick -block(blockid=215, top_image="assets/minecraft/textures/block/red_nether_bricks.png") @material(blockid=216, data=list(range(12)), solid=True) def boneblock(self, blockid, data): - # extract orientation - boneblock_orientation = data & 12 - if self.rotation == 1: - if boneblock_orientation == 4: boneblock_orientation = 8 - elif boneblock_orientation == 8: boneblock_orientation = 4 - elif self.rotation == 3: - if boneblock_orientation == 4: boneblock_orientation = 8 - elif boneblock_orientation == 8: boneblock_orientation = 4 - - top = self.load_image_texture("assets/minecraft/textures/block/bone_block_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/bone_block_side.png") - - # choose orientation and paste textures - if boneblock_orientation == 0: - return self.build_block(top, side) - elif boneblock_orientation == 4: # east-west orientation - return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90)) - elif boneblock_orientation == 8: # north-south orientation - return self.build_full_block(side, None, None, side.rotate(270), top) - + return self.build_block_from_model('bone_block', blockstate={'axis': ({0: 'y', 4: 'x', 8: 'z'}[data & 12])}) # observer @material(blockid=218, data=[0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13], solid=True, nospawn=True) def observer(self, blockid, data): - # Do rotation - if self.rotation in [1, 2, 3] and (data & 0b111) in [2, 3, 4, 5]: - rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3}, - 2: {2: 3, 3: 2, 4: 5, 5: 4}, - 3: {2: 4, 3: 5, 4: 3, 5: 2}} - data = (data & 0b1000) | rotation_map[self.rotation][data & 0b111] - - front = self.load_image_texture("assets/minecraft/textures/block/observer_front.png") - side = self.load_image_texture("assets/minecraft/textures/block/observer_side.png") - top = self.load_image_texture("assets/minecraft/textures/block/observer_top.png") - file_name_back = "observer_back_on" if data & 0b1000 else "observer_back" - back = self.load_image_texture("assets/minecraft/textures/block/%s.png" % file_name_back) - - if data & 0b0111 == 0: # Down - img = self.build_full_block(back, None, None, side.rotate(90), top) - elif data & 0b0111 == 1: # Up - img = self.build_full_block(front.rotate(180), None, None, side.rotate(90), top.rotate(180)) - elif data & 0b0111 == 2: # East - img = self.build_full_block(top.rotate(180), None, None, side, back) - elif data & 0b0111 == 3: # West - img = self.build_full_block(top, None, None, side, front) - elif data & 0b0111 == 4: # North - img = self.build_full_block(top.rotate(270), None, None, front, side) - elif data & 0b0111 == 5: # South - img = self.build_full_block(top.rotate(90), None, None, back, side) - - return img - + facing = {0: 'down', 1: 'up', 2: 'north', 3: 'south', 4: 'west', 5: 'east'}[data & 0b0111] + if data & 0b1000: + return self.build_block_from_model('observer', {'facing': facing}) + else: + return self.build_block_from_model('observer_on', {'facing': facing}) # shulker box @material(blockid=list(range(219, 235)) + [257], data=list(range(6)), solid=True, nospawn=True) @@ -5515,205 +5320,75 @@ def shulker_box(self, blockid, data): @material(blockid=255, data=list(range(4)), solid=True) def structure_block(self, blockid, data): if data == 0: - img = self.load_image_texture("assets/minecraft/textures/block/structure_block_save.png") + return self.build_block_from_model("structure_block_save") elif data == 1: - img = self.load_image_texture("assets/minecraft/textures/block/structure_block_load.png") + return self.build_block_from_model("structure_block_load") elif data == 2: - img = self.load_image_texture("assets/minecraft/textures/block/structure_block_corner.png") + return self.build_block_from_model("structure_block_corner") elif data == 3: - img = self.load_image_texture("assets/minecraft/textures/block/structure_block_data.png") - return self.build_block(img, img) - + return self.build_block_from_model("structure_block_data") + else: + raise Exception('unexpected structure block: ' + str(data)) # Jigsaw block @material(blockid=256, data=list(range(6)), solid=True) def jigsaw_block(self, blockid, data): - # Do rotation - if self.rotation in [1, 2, 3] and data in [2, 3, 4, 5]: - rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3}, - 2: {2: 3, 3: 2, 4: 5, 5: 4}, - 3: {2: 4, 3: 5, 4: 3, 5: 2}} - data = rotation_map[self.rotation][data] - - top = self.load_image_texture("assets/minecraft/textures/block/jigsaw_top.png") - bottom = self.load_image_texture("assets/minecraft/textures/block/jigsaw_bottom.png") - side = self.load_image_texture("assets/minecraft/textures/block/jigsaw_side.png") - - if data == 0: # Down - img = self.build_full_block(bottom.rotate(self.rotation * 90), None, None, - side.rotate(180), side.rotate(180)) - elif data == 1: # Up - img = self.build_full_block(top.rotate(self.rotation * 90), None, None, side, side) - elif data == 2: # North - img = self.build_full_block(side, None, None, side.rotate(90), bottom.rotate(180)) - elif data == 3: # South - img = self.build_full_block(side.rotate(180), None, None, side.rotate(270), top.rotate(270)) - elif data == 4: # West - img = self.build_full_block(side.rotate(90), None, None, top.rotate(180), side.rotate(270)) - elif data == 5: # East - img = self.build_full_block(side.rotate(270), None, None, bottom.rotate(180), - side.rotate(90)) - - return img + facing = {0: 'down', 1: 'up', 2: 'north', 3: 'south', 4: 'west', 5: 'east'}[data] + return self.build_block_from_model('jigsaw', {'facing':facing}) # beetroots(207), berry bushes (11505) @material(blockid=[207, 11505], data=list(range(4)), transparent=True, nospawn=True) def crops(self, blockid, data): + if blockid == 207: + return self.build_block_from_model("beetroots_stage%d" % data) + else: + raw_crop = self.load_image_texture("assets/minecraft/textures/block/sweet_berry_bush_stage%d.png" % data) + crop1 = self.transform_image_top(raw_crop) + crop2 = self.transform_image_side(raw_crop) + crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - crops_id_to_tex = { - 207: "assets/minecraft/textures/block/beetroots_stage%d.png", - 11505: "assets/minecraft/textures/block/sweet_berry_bush_stage%d.png", - } - - raw_crop = self.load_image_texture(crops_id_to_tex[blockid] % data) - crop1 = self.transform_image_top(raw_crop) - crop2 = self.transform_image_side(raw_crop) - crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), self.bgcolor) - alpha_over(img, crop1, (0,12), crop1) - alpha_over(img, crop2, (6,3), crop2) - alpha_over(img, crop3, (6,3), crop3) - return img - -# Concrete -@material(blockid=251, data=list(range(16)), solid=True) -def concrete(self, blockid, data): - texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete.png" % color_map[data]) - return self.build_block(texture, texture) - -# Concrete Powder -@material(blockid=252, data=list(range(16)), solid=True) -def concrete(self, blockid, data): - texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete_powder.png" % color_map[data]) - return self.build_block(texture, texture) - + img = Image.new("RGBA", (24,24), self.bgcolor) + alpha_over(img, crop1, (0,12), crop1) + alpha_over(img, crop2, (6,3), crop2) + alpha_over(img, crop3, (6,3), crop3) + return img # Glazed Terracotta @material(blockid=list(range(235, 251)), data=list(range(4)), solid=True) def glazed_terracotta(self, blockid, data): - # Do rotation - data = (self.rotation + data) % 4 - - texture = self.load_image_texture("assets/minecraft/textures/block/%s_glazed_terracotta.png" % - color_map[blockid - 235]).copy() - texture_side4 = texture.transpose(Image.FLIP_LEFT_RIGHT) - - if data == 0: # South - return self.build_full_block(texture, None, None, texture, texture_side4.rotate(270)) - elif data == 1: # West - return self.build_full_block(texture.rotate(270), None, None, texture.rotate(90), - texture_side4.rotate(180)) - elif data == 2: # North - return self.build_full_block(texture.rotate(180), None, None, texture.rotate(180), - texture_side4.rotate(90)) - elif data == 3: # East - return self.build_full_block(texture.rotate(90), None, None, texture.rotate(270), - texture_side4) - - -# dried kelp block -@material(blockid=11331, data=[0], solid=True) -def sandstone(self, blockid, data): - top = self.load_image_texture("assets/minecraft/textures/block/dried_kelp_top.png") - return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/dried_kelp_side.png")) + facing = {0: 'south', 1: 'west', 2: 'north', 3: 'east'}[data] + return self.build_block_from_model("%s_glazed_terracotta" % color_map[blockid - 235], {'facing': facing}) # scaffolding -block(blockid=11414, top_image="assets/minecraft/textures/block/scaffolding_top.png", side_image="assets/minecraft/textures/block/scaffolding_side.png", solid=False, transparent=True) +@material(blockid=[11414], data=list(range(2)), solid=False, transparent=True) +def scaffolding(self, blockid, data): + top = self.load_image_texture("assets/minecraft/textures/block/scaffolding_top.png") + side = self.load_image_texture("assets/minecraft/textures/block/scaffolding_side.png") + img = self.build_block(top, side) + return img # beehive and bee_nest @material(blockid=[11501, 11502], data=list(range(8)), solid=True) def beehivenest(self, blockid, data): - if blockid == 11501: #beehive - t_top = self.load_image("assets/minecraft/textures/block/beehive_end.png") - t_side = self.load_image("assets/minecraft/textures/block/beehive_side.png") - t_front = self.load_image("assets/minecraft/textures/block/beehive_front.png") - t_front_honey = self.load_image("assets/minecraft/textures/block/beehive_front_honey.png") - elif blockid == 11502: #bee_nest - t_top = self.load_image("assets/minecraft/textures/block/bee_nest_top.png") - t_side = self.load_image("assets/minecraft/textures/block/bee_nest_side.png") - t_front = self.load_image("assets/minecraft/textures/block/bee_nest_front.png") - t_front_honey = self.load_image("assets/minecraft/textures/block/bee_nest_front_honey.png") - - if data >= 4: - front = t_front_honey - else: - front = t_front - - if self.rotation == 0: # rendering north upper-left - if data == 0 or data == 4: # south - return self.build_full_block(t_top, t_side, t_side, t_side, front) - elif data == 1 or data == 5: # west - return self.build_full_block(t_top, t_side, t_side, front, t_side) - elif data == 2 or data == 6: # north - return self.build_full_block(t_top, t_side, front, t_side, t_side) - elif data == 3 or data == 7: # east - return self.build_full_block(t_top, front, t_side, t_side, t_side) - - elif self.rotation == 1: # north upper-right - if data == 0 or data == 4: # south - return self.build_full_block(t_top, t_side, t_side, front, t_side) - elif data == 1 or data == 5: # west - return self.build_full_block(t_top, t_side, front, t_side, t_side) - elif data == 2 or data == 6: # north - return self.build_full_block(t_top, front, t_side, t_side, t_side) - elif data == 3 or data == 7: # east - return self.build_full_block(t_top, t_side, t_side, t_side, front) - - elif self.rotation == 2: # north lower-right - if data == 0 or data == 4: # south - return self.build_full_block(t_top, t_side, front, t_side, t_side) - elif data == 1 or data == 5: # west - return self.build_full_block(t_top, front, t_side, t_side, t_side) - elif data == 2 or data == 6: # north - return self.build_full_block(t_top, t_side, t_side, t_side, front) - elif data == 3 or data == 7: # east - return self.build_full_block(t_top, t_side, t_side, front, t_side) - - elif self.rotation == 3: # north lower-left - if data == 0 or data == 4: # south - return self.build_full_block(t_top, front, t_side, t_side, t_side) - elif data == 1 or data == 5: # west - return self.build_full_block(t_top, t_side, t_side, t_side, front) - elif data == 2 or data == 6: # north - return self.build_full_block(t_top, t_side, t_side, front, t_side) - elif data == 3 or data == 7: # east - return self.build_full_block(t_top, t_side, front, t_side, t_side) - -# honeycomb_block -block(blockid=11503, top_image="assets/minecraft/textures/block/honeycomb_block.png") - -# honey_block -block(blockid=11504, top_image="assets/minecraft/textures/block/honey_block_top.png", side_image="assets/minecraft/textures/block/honey_block_side.png") - + facing = {0: 'south', 1: 'west', 2: 'north', 3: 'east'}[data % 4] + if blockid == 11501: + if data >= 4: + return self.build_block_from_model('beehive_honey', {'facing': facing}) + return self.build_block_from_model('beehive', {'facing': facing}) + else: # blockid == 11502: + if data >= 4: + return self.build_block_from_model('bee_nest_honey', {'facing': facing}) + return self.build_block_from_model('bee_nest', {'facing': facing}) # Barrel @material(blockid=11418, data=list(range(12)), solid=True) def barrel(self, blockid, data): - t_bottom = self.load_image("assets/minecraft/textures/block/barrel_bottom.png") - t_side = self.load_image("assets/minecraft/textures/block/barrel_side.png") + facing = {0: 'up', 1: 'down', 2: 'south', 3: 'east', 4: 'north', 5: 'west'}[data >> 1] if data & 0x01: - t_top = self.load_image("assets/minecraft/textures/block/barrel_top_open.png") - else: - t_top = self.load_image("assets/minecraft/textures/block/barrel_top.png") - data = data >> 1 - - if data == 0: # up - return self.build_full_block(t_top, None, None, t_side, t_side) - elif data == 1: # down - t_side = t_side.rotate(180) - return self.build_full_block(t_bottom, None, None, t_side, t_side) - elif data == 2: # south - return self.build_full_block(t_side.rotate(180), None, None, t_side.rotate(270), t_top) - elif data == 3: # east - return self.build_full_block(t_side.rotate(270), None, None, t_bottom, t_side.rotate(90)) - elif data == 4: # north - return self.build_full_block(t_side, None, None, t_side.rotate(90), t_bottom) - else: # west - return self.build_full_block(t_side.rotate(90), None, None, t_top, t_side.rotate(270)) - + return self.build_block_from_model('barrel_open', {'facing': facing}) + return self.build_block_from_model('barrel', {'facing': facing}) # Campfire (11506) and soul campfire (1003) @material(blockid=[11506, 1003], data=list(range(8)), solid=True, transparent=True, nospawn=True) @@ -5944,24 +5619,23 @@ def create_tile(img_src, coord_crop, coord_paste, rot): return img +# nether roof # Ancient Debris -block(blockid=[1000], top_image="assets/minecraft/textures/block/ancient_debris_top.png", - side_image="assets/minecraft/textures/block/ancient_debris_side.png") - +solidmodelblock(blockid=[1000], name="ancient_debris") # Basalt @material(blockid=[1001, 1002], data=list(range(3)), solid=True) def basalt(self, blockid, data): - block_name = "polished_basalt" if blockid == 1002 else "basalt" - top = self.load_image_texture("assets/minecraft/textures/block/" + block_name + "_top.png") - side = self.load_image_texture("assets/minecraft/textures/block/" + block_name + "_side.png") - return self.build_axis_block(top, side, data) - + axis = {0: 'y', 1: 'x', 2: 'z'}[data] + if blockid == 1001: # basalt + return self.build_block_from_model('basalt', {'axis': axis}) + if blockid == 1002: # polished_basalt + return self.build_block_from_model('polished_basalt', {'axis': axis}) + +# nether roof # Blackstone block -block(blockid=[1004], top_image="assets/minecraft/textures/block/blackstone_top.png", - side_image="assets/minecraft/textures/block/blackstone.png") - +solidmodelblock(blockid=[1004], name="blackstone") # Chain @material(blockid=11419, data=list(range(3)), solid=True, transparent=True, nospawn=True) @@ -5999,88 +5673,46 @@ def draw_z(): return img - # Respawn anchor @material(blockid=1037, data=list(range(5)), solid=True) def respawn_anchor(self, blockid, data): - top = self.load_image_texture("assets/minecraft/textures/block/respawn_anchor_top_off.png" if data == - 0 else "assets/minecraft/textures/block/respawn_anchor_top.png") - side = self.load_image_texture( - "assets/minecraft/textures/block/respawn_anchor_side%s.png" % (data)) - - return self.build_block(top, side) + return self.build_block_from_model("respawn_anchor_%s" % data) - -# Netherite -block(blockid=[1005], top_image="assets/minecraft/textures/block/netherite_block.png") +# nether roof # soul soil -block(blockid=1020, top_image="assets/minecraft/textures/block/soul_soil.png") +solidmodelblock(blockid=1020, name="soul_soil") # nether gold ore -block(blockid=1021, top_image="assets/minecraft/textures/block/nether_gold_ore.png") -# Solid Nether stone blocks -block(blockid=1022, top_image="assets/minecraft/textures/block/polished_blackstone.png") -block(blockid=1023, top_image="assets/minecraft/textures/block/chiseled_polished_blackstone.png") -block(blockid=1024, top_image="assets/minecraft/textures/block/gilded_blackstone.png") -block(blockid=1025, top_image="assets/minecraft/textures/block/cracked_polished_blackstone_bricks.png") -block(blockid=1026, top_image="assets/minecraft/textures/block/polished_blackstone_bricks.png") - -block(blockid=1035, top_image="assets/minecraft/textures/block/crying_obsidian.png") -block(blockid=1036, top_image="assets/minecraft/textures/block/lodestone_top.png", side_image="assets/minecraft/textures/block/lodestone_side.png") -block(blockid=1041, top_image="assets/minecraft/textures/block/quartz_bricks.png") - -block(blockid=1042, top_image="assets/minecraft/textures/block/amethyst_block.png") -block(blockid=1043, top_image="assets/minecraft/textures/block/raw_iron_block.png") -block(blockid=1044, top_image="assets/minecraft/textures/block/raw_gold_block.png") -block(blockid=1045, top_image="assets/minecraft/textures/block/budding_amethyst.png") - -# You have entered the COPPER ZONE -block(blockid=[1046, 1050], top_image="assets/minecraft/textures/block/copper_block.png") -block(blockid=[1047, 1051], top_image="assets/minecraft/textures/block/exposed_copper.png") -block(blockid=[1048, 1052], top_image="assets/minecraft/textures/block/weathered_copper.png") -block(blockid=[1049, 1053], top_image="assets/minecraft/textures/block/oxidized_copper.png") -# Cut variant -block(blockid=[1054, 1058], top_image="assets/minecraft/textures/block/cut_copper.png") -block(blockid=[1055, 1059], top_image="assets/minecraft/textures/block/exposed_cut_copper.png") -block(blockid=[1056, 1060], top_image="assets/minecraft/textures/block/weathered_cut_copper.png") -block(blockid=[1057, 1061], top_image="assets/minecraft/textures/block/oxidized_cut_copper.png") +solidmodelblock(blockid=1021, name="nether_gold_ore") -block(blockid=1062, top_image="assets/minecraft/textures/block/raw_copper_block.png") -block(blockid=1063, top_image="assets/minecraft/textures/block/copper_ore.png") -# You are now leaving the COPPER ZONE - -block(blockid=1080, top_image="assets/minecraft/textures/block/moss_block.png") -block(blockid=1081, top_image="assets/minecraft/textures/block/calcite.png") -block(blockid=1082, top_image="assets/minecraft/textures/block/rooted_dirt.png") +# waxed copper +solidmodelblock(blockid=[1050], name="copper_block") +solidmodelblock(blockid=[1051], name="exposed_copper") +solidmodelblock(blockid=[1052], name="weathered_copper") +solidmodelblock(blockid=[1053], name="oxidized_copper") +# Cut variant +solidmodelblock(blockid=[1058], name="cut_copper") +solidmodelblock(blockid=[1059], name="exposed_cut_copper") +solidmodelblock(blockid=[1060], name="weathered_cut_copper") +solidmodelblock(blockid=[1061], name="oxidized_cut_copper") +# mineral overlay +solidmodelblock(blockid=1063, name="copper_ore") # deepslate @material(blockid=1083, data=list(range(3)), solid=True) def deepslate(self, blockid, data): - top=self.load_image_texture("assets/minecraft/textures/block/deepslate_top.png") - side=self.load_image_texture("assets/minecraft/textures/block/deepslate.png") - return self.build_axis_block(top, side, data) - - -block(blockid=1084, top_image="assets/minecraft/textures/block/cobbled_deepslate.png") -block(blockid=1085, top_image="assets/minecraft/textures/block/polished_deepslate.png") -block(blockid=1086, top_image="assets/minecraft/textures/block/deepslate_coal_ore.png") -block(blockid=1087, top_image="assets/minecraft/textures/block/deepslate_iron_ore.png") -block(blockid=1088, top_image="assets/minecraft/textures/block/deepslate_copper_ore.png") -block(blockid=1089, top_image="assets/minecraft/textures/block/deepslate_gold_ore.png") -block(blockid=1090, top_image="assets/minecraft/textures/block/deepslate_emerald_ore.png") -block(blockid=1091, top_image="assets/minecraft/textures/block/deepslate_lapis_ore.png") -block(blockid=1092, top_image="assets/minecraft/textures/block/deepslate_diamond_ore.png") -block(blockid=1093, top_image="assets/minecraft/textures/block/deepslate_redstone_ore.png") -block(blockid=1094, top_image="assets/minecraft/textures/block/deepslate_bricks.png") -block(blockid=1095, top_image="assets/minecraft/textures/block/cracked_deepslate_bricks.png") -block(blockid=1096, top_image="assets/minecraft/textures/block/deepslate_tiles.png") -block(blockid=1097, top_image="assets/minecraft/textures/block/cracked_deepslate_tiles.png") -block(blockid=1098, top_image="assets/minecraft/textures/block/chiseled_deepslate.png") - -block(blockid=1107, top_image="assets/minecraft/textures/block/dripstone_block.png") -block(blockid=1108, top_image="assets/minecraft/textures/block/smooth_basalt.png") -block(blockid=1109, top_image="assets/minecraft/textures/block/tuff.png") + return self.build_block_from_model('deepslate', {'axis': {0: 'y', 1: 'x', 2: 'z'}[data]}) + +# mineral overlay +solidmodelblock(blockid=1086, name="deepslate_coal_ore") +solidmodelblock(blockid=1087, name="deepslate_iron_ore") +solidmodelblock(blockid=1088, name="deepslate_copper_ore") +solidmodelblock(blockid=1089, name="deepslate_gold_ore") +solidmodelblock(blockid=1090, name="deepslate_emerald_ore") +solidmodelblock(blockid=1091, name="deepslate_lapis_ore") +solidmodelblock(blockid=1092, name="deepslate_diamond_ore") +solidmodelblock(blockid=1093, name="deepslate_redstone_ore") @material(blockid=1110, data=list(range(16)), transparent=True) def pointed_dripstone(self, blockid, data): @@ -6097,10 +5729,6 @@ def pointed_dripstone(self, blockid, data): tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_tip.png" % (up_down)) return self.build_sprite(tex) - -block(blockid=1111, top_image="assets/minecraft/textures/block/powder_snow.png") - - @material(blockid=1112, data=0, transparent=True) def hangings_roots(self, blockid, data): tex = self.load_image_texture("assets/minecraft/textures/block/hanging_roots.png") @@ -6193,9 +5821,9 @@ def cave_vines(self, blockid, data): tex = self.load_image_texture("assets/minecraft/textures/block/cave_vines.png") return self.build_sprite(tex) - @material(blockid=1118, data=list(range(6)), transparent=True, solid=True) def lightning_rod(self, blockid, data): + tex = self.load_image_texture("assets/minecraft/textures/block/lightning_rod.png") img = Image.new("RGBA", (24, 24), self.bgcolor) @@ -6382,11 +6010,6 @@ def spore_blossom(self, blockid, data): alpha_over(img, base_top, (0, 0), base_top) return img - -block(blockid=1121, top_image="assets/minecraft/textures/block/mud.png") -block(blockid=1122, top_image="assets/minecraft/textures/block/packed_mud.png") -block(blockid=1123, top_image="assets/minecraft/textures/block/mud_bricks.png") -block(blockid=1125, top_image="assets/minecraft/textures/block/mangrove_roots_top.png", - side_image="assets/minecraft/textures/block/mangrove_roots_side.png") -block(blockid=1127, top_image="assets/minecraft/textures/block/muddy_mangrove_roots_top.png", - side_image="assets/minecraft/textures/block/muddy_mangrove_roots_side.png") +# Render all blocks not explicitly declared before +# Must run last to prevent being hidden by blocks with fixed IDs +unbound_models() diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 0fd3f796e..60181fcfe 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -18,12 +18,9 @@ import os.path import logging import time -import random import re -import locale import numpy -import math from . import nbt from . import cache @@ -37,10 +34,13 @@ class ChunkDoesntExist(Exception): pass - class UnsupportedVersion(Exception): pass +class UnknownBlockException(Exception): + pass + +modelblocks = {} def log_other_exceptions(func): """A decorator that prints out any errors that are not @@ -302,34 +302,12 @@ def __init__(self, regiondir, rel): 'minecraft:air': (0, 0), 'minecraft:cave_air': (0, 0), 'minecraft:void_air': (0, 0), - 'minecraft:stone': (1, 0), - 'minecraft:granite': (1, 1), - 'minecraft:polished_granite': (1, 2), - 'minecraft:diorite': (1, 3), - 'minecraft:polished_diorite': (1, 4), - 'minecraft:andesite': (1, 5), - 'minecraft:polished_andesite': (1, 6), 'minecraft:grass_block': (2, 0), - 'minecraft:dirt': (3, 0), - 'minecraft:coarse_dirt': (3, 1), - 'minecraft:podzol': (3, 2), - 'minecraft:cobblestone': (4, 0), 'minecraft:infested_cobblestone': (4, 0), - 'minecraft:oak_planks': (5, 0), - 'minecraft:spruce_planks': (5, 1), - 'minecraft:birch_planks': (5, 2), - 'minecraft:jungle_planks': (5, 3), - 'minecraft:acacia_planks': (5, 4), - 'minecraft:dark_oak_planks': (5, 5), - 'minecraft:crimson_planks': (5, 6), - 'minecraft:warped_planks': (5, 7), 'minecraft:sapling': (6, 0), 'minecraft:bedrock': (7, 0), 'minecraft:water': (8, 0), 'minecraft:lava': (11, 0), - 'minecraft:sand': (12, 0), - 'minecraft:red_sand': (12, 1), - 'minecraft:gravel': (13, 0), 'minecraft:gold_ore': (14, 0), 'minecraft:iron_ore': (15, 0), 'minecraft:coal_ore': (16, 0), @@ -352,10 +330,6 @@ def __init__(self, regiondir, rel): 'minecraft:lapis_ore': (21, 0), 'minecraft:lapis_block': (22, 0), 'minecraft:dispenser': (23, 0), - 'minecraft:sandstone': (24, 0), - 'minecraft:chiseled_sandstone': (24, 1), - 'minecraft:cut_sandstone': (24, 2), - 'minecraft:note_block': (25, 0), 'minecraft:powered_rail': (27, 0), 'minecraft:detector_rail': (28, 0), 'minecraft:sticky_piston': (29, 0), @@ -365,22 +339,6 @@ def __init__(self, regiondir, rel): 'minecraft:fern': (31, 2), 'minecraft:piston': (33, 0), 'minecraft:piston_head': (34, 0), - 'minecraft:white_wool': (35, 0), - 'minecraft:orange_wool': (35, 1), - 'minecraft:magenta_wool': (35, 2), - 'minecraft:light_blue_wool': (35, 3), - 'minecraft:yellow_wool': (35, 4), - 'minecraft:lime_wool': (35, 5), - 'minecraft:pink_wool': (35, 6), - 'minecraft:gray_wool': (35, 7), - 'minecraft:light_gray_wool': (35, 8), - 'minecraft:cyan_wool': (35, 9), - 'minecraft:purple_wool': (35, 10), - 'minecraft:blue_wool': (35, 11), - 'minecraft:brown_wool': (35, 12), - 'minecraft:green_wool': (35, 13), - 'minecraft:red_wool': (35, 14), - 'minecraft:black_wool': (35, 15), # Flowers 'minecraft:poppy': (38, 0), 'minecraft:blue_orchid': (38, 1), @@ -408,11 +366,8 @@ def __init__(self, regiondir, rel): 'minecraft:stone_brick_slab': (44, 5), 'minecraft:nether_brick_slab': (44, 6), 'minecraft:quartz_slab': (44, 7), - 'minecraft:bricks': (45, 0), 'minecraft:tnt': (46, 0), - 'minecraft:bookshelf': (47, 0), 'minecraft:mossy_cobblestone': (48, 0), - 'minecraft:obsidian': (49, 0), 'minecraft:wall_torch': (50, 0), 'minecraft:torch': (50, 5), 'minecraft:fire': (51, 0), @@ -424,7 +379,6 @@ def __init__(self, regiondir, rel): 'minecraft:redstone_wire': (55, 0), 'minecraft:diamond_ore': (56, 0), 'minecraft:diamond_block': (57, 0), - 'minecraft:crafting_table': (58, 0), 'minecraft:wheat': (59, 0), 'minecraft:farmland': (60, 0), 'minecraft:furnace': (61, 0), @@ -443,6 +397,7 @@ def __init__(self, regiondir, rel): 'minecraft:cobblestone_stairs': (67, 0), 'minecraft:wall_sign': (68, 0), 'minecraft:oak_wall_sign': (11407, 0), + 'minecraft:mangrove_wall_sign': (454, 0), 'minecraft:spruce_wall_sign': (11408, 0), 'minecraft:birch_wall_sign': (11409, 0), 'minecraft:jungle_wall_sign': (11410, 0), @@ -460,35 +415,25 @@ def __init__(self, regiondir, rel): 'minecraft:stone_button': (77, 0), 'minecraft:snow': (78, 0), 'minecraft:ice': (79, 0), - 'minecraft:snow_block': (80, 0), 'minecraft:cactus': (81, 0), - 'minecraft:clay': (82, 0), 'minecraft:sugar_cane': (83, 0), - 'minecraft:jukebox': (84, 0), 'minecraft:oak_fence': (85, 0), - 'minecraft:pumpkin': (86, 0), 'minecraft:netherrack': (87, 0), 'minecraft:soul_sand': (88, 0), - 'minecraft:glowstone': (89, 0), 'minecraft:nether_portal': (90, 0), 'minecraft:jack_o_lantern': (91, 0), 'minecraft:cake': (92, 0), 'minecraft:repeater': (93,0), 'minecraft:oak_trapdoor': (96, 0), 'minecraft:infested_stone': (97, 0), - 'minecraft:stone_bricks': (98, 0), 'minecraft:infested_stone_bricks': (98, 0), - 'minecraft:mossy_stone_bricks': (98, 1), 'minecraft:infested_mossy_stone_bricks': (98, 1), - 'minecraft:cracked_stone_bricks': (98, 2), 'minecraft:infested_cracked_stone_bricks': (98, 2), - 'minecraft:chiseled_stone_bricks': (98, 3), 'minecraft:infested_chiseled_stone_bricks': (98, 3), 'minecraft:brown_mushroom_block': (99, 0), 'minecraft:red_mushroom_block': (100, 0), 'minecraft:iron_bars': (101, 0), 'minecraft:glass_pane': (102, 0), - 'minecraft:melon': (103,0), 'minecraft:attached_pumpkin_stem': (104, 0), 'minecraft:attached_melon_stem': (104, 0), 'minecraft:pumpkin_stem': (105, 0), @@ -497,11 +442,7 @@ def __init__(self, regiondir, rel): 'minecraft:oak_fence_gate': (107, 0), 'minecraft:brick_stairs': (108, 0), 'minecraft:stone_brick_stairs': (109, 0), - 'minecraft:mycelium': (110, 0), 'minecraft:lily_pad': (111, 0), - 'minecraft:nether_bricks': (112, 0), - 'minecraft:cracked_nether_bricks': (112, 1), - 'minecraft:chiseled_nether_bricks': (112, 2), 'minecraft:nether_brick_fence': (113, 0), 'minecraft:nether_brick_stairs': (114, 0), 'minecraft:nether_wart': (115, 0), @@ -513,8 +454,6 @@ def __init__(self, regiondir, rel): 'minecraft:powder_snow_cauldron': (118, 3 << 2), 'minecraft:end_portal': (119, 0), 'minecraft:end_portal_frame': (120, 0), - 'minecraft:end_stone': (121, 0), - 'minecraft:dragon_egg': (122, 0), 'minecraft:redstone_lamp': (123, 0), 'minecraft:oak_slab': (126, 0), 'minecraft:spruce_slab': (126, 1), @@ -524,18 +463,16 @@ def __init__(self, regiondir, rel): 'minecraft:dark_oak_slab': (126, 5), 'minecraft:crimson_slab': (126, 6), 'minecraft:warped_slab': (126, 7), + 'minecraft:mangrove_slab': (1789, 0), 'minecraft:cocoa': (127, 0), 'minecraft:sandstone_stairs': (128, 0), 'minecraft:emerald_ore': (129, 0), 'minecraft:ender_chest': (130, 0), 'minecraft:tripwire': (131, 0), 'minecraft:tripwire_hook': (132, 0), - 'minecraft:emerald_block': (133, 0), 'minecraft:spruce_stairs': (134, 0), 'minecraft:birch_stairs': (135, 0), 'minecraft:jungle_stairs': (136, 0), - 'minecraft:command_block': (137, 0), - 'minecraft:beacon': (138, 0), 'minecraft:mushroom_stem': (139, 0), 'minecraft:flower_pot': (140, 0), 'minecraft:potted_poppy': (140, 0), # Pots not rendering @@ -576,63 +513,19 @@ def __init__(self, regiondir, rel): 'minecraft:heavy_weighted_pressure_plate': (148, 0), 'minecraft:comparator': (149, 0), 'minecraft:daylight_detector': (151, 0), - 'minecraft:redstone_block': (152, 0), 'minecraft:nether_quartz_ore': (153, 0), 'minecraft:hopper': (154, 0), - 'minecraft:quartz_block': (155, 0), 'minecraft:smooth_quartz': (155, 0), # Only bottom texture is different 'minecraft:quartz_pillar': (155, 2), - 'minecraft:chiseled_quartz_block': (155, 1), 'minecraft:quartz_stairs': (156, 0), 'minecraft:activator_rail': (157, 0), 'minecraft:dropper': (158, 0), - 'minecraft:white_terracotta': (159, 0), - 'minecraft:orange_terracotta': (159, 1), - 'minecraft:magenta_terracotta': (159, 2), - 'minecraft:light_blue_terracotta': (159, 3), - 'minecraft:yellow_terracotta': (159, 4), - 'minecraft:lime_terracotta': (159, 5), - 'minecraft:pink_terracotta': (159, 6), - 'minecraft:gray_terracotta': (159, 7), - 'minecraft:light_gray_terracotta': (159, 8), - 'minecraft:cyan_terracotta': (159, 9), - 'minecraft:purple_terracotta': (159, 10), - 'minecraft:blue_terracotta': (159, 11), - 'minecraft:brown_terracotta': (159, 12), - 'minecraft:green_terracotta': (159, 13), - 'minecraft:red_terracotta': (159, 14), - 'minecraft:black_terracotta': (159, 15), 'minecraft:acacia_log': (162, 0), 'minecraft:dark_oak_log': (162, 1), 'minecraft:acacia_stairs': (163, 0), 'minecraft:dark_oak_stairs': (164, 0), - 'minecraft:slime_block': (165,0), 'minecraft:iron_trapdoor': (167, 0), - 'minecraft:prismarine': (168, 0), - 'minecraft:dark_prismarine': (168, 2), - 'minecraft:prismarine_bricks': (168, 1), - 'minecraft:sea_lantern': (169, 0), 'minecraft:hay_block': (170, 0), - 'minecraft:white_carpet': (171, 0), - 'minecraft:orange_carpet': (171, 1), - 'minecraft:magenta_carpet': (171, 2), - 'minecraft:light_blue_carpet': (171, 3), - 'minecraft:yellow_carpet': (171, 4), - 'minecraft:lime_carpet': (171, 5), - 'minecraft:pink_carpet': (171, 6), - 'minecraft:gray_carpet': (171, 7), - 'minecraft:light_gray_carpet': (171, 8), - 'minecraft:cyan_carpet': (171, 9), - 'minecraft:purple_carpet': (171, 10), - 'minecraft:blue_carpet': (171, 11), - 'minecraft:brown_carpet': (171, 12), - 'minecraft:green_carpet': (171, 13), - 'minecraft:red_carpet': (171, 14), - 'minecraft:black_carpet': (171, 15), - 'minecraft:moss_carpet': (171, 16), - 'minecraft:terracotta': (172, 0), - 'minecraft:coal_block': (173, 0), - 'minecraft:packed_ice': (174, 0), 'minecraft:sunflower': (175, 0), 'minecraft:lilac': (175, 1), 'minecraft:tall_grass': (175, 2), @@ -641,9 +534,6 @@ def __init__(self, regiondir, rel): 'minecraft:peony': (175, 5), 'minecraft:standing_banner': (176, 0), 'minecraft:wall_banner': (177, 0), - 'minecraft:red_sandstone': (179, 0), - 'minecraft:chiseled_red_sandstone': (179, 1), - 'minecraft:cut_red_sandstone': (179, 2), 'minecraft:red_sandstone_stairs': (180, 0), 'minecraft:red_sandstone_slab': (182,0), 'minecraft:spruce_fence_gate': (183, 0), @@ -653,6 +543,8 @@ def __init__(self, regiondir, rel): 'minecraft:acacia_fence_gate': (187, 0), 'minecraft:crimson_fence_gate': (513, 0), 'minecraft:warped_fence_gate': (514, 0), + 'minecraft:mangrove_fence_gate': (455, 0), + 'minecraft:mangrove_fence': (456, 0), 'minecraft:spruce_fence': (188, 0), 'minecraft:birch_fence': (189, 0), 'minecraft:jungle_fence': (190, 0), @@ -665,25 +557,21 @@ def __init__(self, regiondir, rel): 'minecraft:jungle_door': (195, 0), 'minecraft:acacia_door': (196, 0), 'minecraft:dark_oak_door': (197, 0), + 'minecraft:mangrove_door': (457, 0), 'minecraft:crimson_door': (499, 0), 'minecraft:warped_door': (500, 0), + 'minecraft:end_gateway': (209, 0), + 'minecraft:end_rod': (198, 0), 'minecraft:chorus_plant': (199, 0), 'minecraft:chorus_flower': (200, 0), - 'minecraft:purpur_block': (201, 0), 'minecraft:purpur_pillar': (202, 0), 'minecraft:purpur_stairs': (203, 0), 'minecraft:purpur_slab': (205, 0), - 'minecraft:end_stone_bricks': (206, 0), 'minecraft:beetroots': (207, 0), 'minecraft:dirt_path': (208, 0), 'minecraft:grass_path': (208, 0), # compatibility - 'minecraft:repeating_command_block': (210, 0), - 'minecraft:chain_command_block': (211, 0), 'minecraft:frosted_ice': (212, 0), - 'minecraft:magma_block': (213, 0), - 'minecraft:nether_wart_block': (214, 0), - 'minecraft:red_nether_bricks': (215, 0), 'minecraft:bone_block': (216, 0), 'minecraft:observer': (218, 0), @@ -723,9 +611,6 @@ def __init__(self, regiondir, rel): 'minecraft:polished_basalt': (1002, 0), 'minecraft:soul_campfire': (1003, 0), 'minecraft:blackstone': (1004, 0), - 'minecraft:netherite_block': (1005, 0), - 'minecraft:warped_nylium': (1006, 0), - 'minecraft:crimson_nylium': (1007, 0), # Nether logs aka stem 'minecraft:warped_stem': (1008, 0), 'minecraft:stripped_warped_stem': (1008, 1), @@ -737,8 +622,6 @@ def __init__(self, regiondir, rel): 'minecraft:crimson_hyphae': (1009, 2), 'minecraft:stripped_crimson_hyphae': (1009, 3), # nether biomes - 'minecraft:warped_wart_block': (1010, 0), - 'minecraft:shroomlight': (1011, 0), 'minecraft:twisting_vines': (1012, 0), 'minecraft:twisting_vines_plant': (1013, 0), 'minecraft:weeping_vines': (1014, 0), @@ -749,12 +632,6 @@ def __init__(self, regiondir, rel): 'minecraft:crimson_roots': (1019, 0), 'minecraft:soul_soil': (1020, 0), 'minecraft:nether_gold_ore': (1021, 0), - # Solid Nether stone blocks - 'minecraft:polished_blackstone': (1022, 0), - 'minecraft:chiseled_polished_blackstone': (1023, 0), - 'minecraft:gilded_blackstone': (1024, 0), - 'minecraft:cracked_polished_blackstone_bricks': (1025, 0), - 'minecraft:polished_blackstone_bricks': (1026, 0), # Nether slabs 'minecraft:blackstone_slab': (1027, 0), 'minecraft:polished_blackstone_slab': (1028, 0), @@ -767,39 +644,22 @@ def __init__(self, regiondir, rel): 'minecraft:polished_blackstone_pressure_plate': (1033, 0), 'minecraft:polished_blackstone_button': (1034, 0), # advanced nether blocks - 'minecraft:crying_obsidian': (1035, 0), - 'minecraft:lodestone': (1036, 0), 'minecraft:respawn_anchor': (1037, 0), # soul lightning 'minecraft:soul_lantern': (1038, 0), 'minecraft:soul_wall_torch': (1039, 0), 'minecraft:soul_torch': (1039, 5), 'minecraft:soul_fire': (1040, 0), - # quartz bricks - 'minecraft:quartz_bricks': (1041, 0), # 1.17 blocks go here - 'minecraft:amethyst_block': (1042, 0), - 'minecraft:raw_iron_block': (1043, 0), - 'minecraft:raw_gold_block': (1044, 0), - 'minecraft:budding_amethyst': (1045, 0), - 'minecraft:copper_block': (1046, 0), - 'minecraft:exposed_copper': (1047, 0), - 'minecraft:weathered_copper': (1048, 0), - 'minecraft:oxidized_copper': (1049, 0), 'minecraft:waxed_copper_block': (1050, 0), 'minecraft:waxed_exposed_copper': (1051, 0), 'minecraft:waxed_weathered_copper': (1052, 0), 'minecraft:waxed_oxidized_copper': (1053, 0), - 'minecraft:cut_copper': (1054, 0), - 'minecraft:exposed_cut_copper': (1055, 0), - 'minecraft:weathered_cut_copper': (1056, 0), - 'minecraft:oxidized_cut_copper': (1057, 0), 'minecraft:waxed_cut_copper': (1058, 0), 'minecraft:waxed_exposed_cut_copper': (1059, 0), 'minecraft:waxed_weathered_cut_copper': (1060, 0), 'minecraft:waxed_oxidized_cut_copper': (1061, 0), - 'minecraft:raw_copper_block': (1062, 0), 'minecraft:copper_ore': (1063, 0), 'minecraft:cut_copper_stairs': (1064, 0), 'minecraft:exposed_cut_copper_stairs': (1065, 0), @@ -817,13 +677,8 @@ def __init__(self, regiondir, rel): 'minecraft:waxed_exposed_cut_copper_slab': (1077, 0), 'minecraft:waxed_weathered_cut_copper_slab': (1078, 0), 'minecraft:waxed_oxidized_cut_copper_slab': (1079, 0), - 'minecraft:moss_block': (1080, 0), - 'minecraft:calcite': (1081, 0), - 'minecraft:rooted_dirt': (1082, 0), 'minecraft:deepslate': (1083, 0), 'minecraft:infested_deepslate': (1083, 0), - 'minecraft:cobbled_deepslate': (1084, 0), - 'minecraft:polished_deepslate': (1085, 0), 'minecraft:deepslate_coal_ore': (1086, 0), 'minecraft:deepslate_iron_ore': (1087, 0), 'minecraft:deepslate_copper_ore': (1088, 0), @@ -832,28 +687,20 @@ def __init__(self, regiondir, rel): 'minecraft:deepslate_lapis_ore': (1091, 0), 'minecraft:deepslate_diamond_ore': (1092, 0), 'minecraft:deepslate_redstone_ore': (1093, 0), - 'minecraft:deepslate_bricks': (1094, 0), - 'minecraft:cracked_deepslate_bricks': (1095, 0), - 'minecraft:deepslate_tiles': (1096, 0), - 'minecraft:cracked_deepslate_tiles': (1097, 0), - 'minecraft:chiseled_deepslate': (1098, 0), 'minecraft:cobbled_deepslate_stairs': (1099, 0), 'minecraft:polished_deepslate_stairs': (1100, 0), 'minecraft:deepslate_brick_stairs': (1101, 0), 'minecraft:deepslate_tile_stairs': (1102, 0), + 'minecraft:mangrove_stairs': (1108, 0), 'minecraft:cobbled_deepslate_slab': (1103, 0), 'minecraft:polished_deepslate_slab': (1104, 0), 'minecraft:deepslate_brick_slab': (1105, 0), 'minecraft:deepslate_tile_slab': (1106, 0), - 'minecraft:dripstone_block': (1107, 0), - 'minecraft:smooth_basalt': (1108, 0), - 'minecraft:tuff': (1109, 0), 'minecraft:pointed_dripstone': (1110, 0), - 'minecraft:powder_snow': (1111, 0), 'minecraft:hanging_roots': (1112, 0), 'minecraft:small_amethyst_bud': (1113, 0), 'minecraft:medium_amethyst_bud': (1114, 0), @@ -865,14 +712,10 @@ def __init__(self, regiondir, rel): 'minecraft:spore_blossom': (1120, 0), # 1.19 blocks - 'minecraft:mud': (1121, 0), - 'minecraft:packed_mud': (1122, 0), - 'minecraft:mud_bricks': (1123, 0), 'minecraft:mud_brick_slab': (1124, 0), - 'minecraft:mangrove_roots': (1125, 0), 'minecraft:mangrove_log': (1126, 0), 'minecraft:stripped_mangrove_log': (1126, 1), - 'minecraft:muddy_mangrove_roots': (1127, 0), + 'minecraft:mud_brick_stairs': (1129, 0), # New blocks 'minecraft:carved_pumpkin': (11300, 0), @@ -883,6 +726,7 @@ def __init__(self, regiondir, rel): 'minecraft:dark_oak_pressure_plate': (11305, 0), 'minecraft:crimson_pressure_plate': (11517, 0), 'minecraft:warped_pressure_plate': (11518, 0), + 'minecraft:mangrove_pressure_plate': (1127, 0), 'minecraft:stripped_oak_log': (11306, 0), 'minecraft:stripped_spruce_log': (11306, 1), 'minecraft:stripped_birch_log': (11306, 2), @@ -901,20 +745,6 @@ def __init__(self, regiondir, rel): 'minecraft:stripped_jungle_wood': (11310, 3), 'minecraft:stripped_acacia_wood': (11311, 0), 'minecraft:stripped_dark_oak_wood': (11311, 1), - 'minecraft:blue_ice': (11312, 0), - 'minecraft:smooth_stone': (11313, 0), - 'minecraft:smooth_sandstone': (11314, 0), - 'minecraft:smooth_red_sandstone': (11315, 0), - 'minecraft:brain_coral_block': (11316, 0), - 'minecraft:bubble_coral_block': (11317, 0), - 'minecraft:fire_coral_block': (11318, 0), - 'minecraft:horn_coral_block': (11319, 0), - 'minecraft:tube_coral_block': (11320, 0), - 'minecraft:dead_brain_coral_block': (11321, 0), - 'minecraft:dead_bubble_coral_block': (11322, 0), - 'minecraft:dead_fire_coral_block': (11323, 0), - 'minecraft:dead_horn_coral_block': (11324, 0), - 'minecraft:dead_tube_coral_block': (11325, 0), 'minecraft:spruce_button': (11326,0), 'minecraft:birch_button': (11327,0), 'minecraft:jungle_button': (11328,0), @@ -922,7 +752,8 @@ def __init__(self, regiondir, rel): 'minecraft:dark_oak_button': (11330,0), 'minecraft:crimson_button': (11515,0), 'minecraft:warped_button': (11516,0), - 'minecraft:dried_kelp_block': (11331,0), + 'minecraft:mangrove_button': (1128,0), + 'minecraft:mangrove_trapdoor': (451, 0), 'minecraft:spruce_trapdoor': (11332, 0), 'minecraft:birch_trapdoor': (11333, 0), 'minecraft:jungle_trapdoor': (11334, 0), @@ -953,9 +784,6 @@ def __init__(self, regiondir, rel): "minecraft:mossy_stone_brick_slab": (11356, 0), "minecraft:smooth_quartz_slab": (11357, 0), "minecraft:smooth_stone_slab": (11358, 0), - "minecraft:fletching_table": (11359, 0), - "minecraft:cartography_table": (11360, 0), - "minecraft:smithing_table": (11361, 0), "minecraft:blast_furnace": (11362, 0), "minecraft:smoker": (11364, 0), "minecraft:lectern": (11366, 0), @@ -992,8 +820,6 @@ def __init__(self, regiondir, rel): # 1.15 blocks below 'minecraft:beehive': (11501, 0), 'minecraft:bee_nest': (11502, 0), - 'minecraft:honeycomb_block': (11503, 0), - 'minecraft:honey_block': (11504, 0), 'minecraft:sweet_berry_bush': (11505, 0), 'minecraft:campfire': (11506, 0), 'minecraft:bell': (11507, 0), @@ -1020,6 +846,7 @@ def __init__(self, regiondir, rel): 'minecraft:polished_deepslate_wall': (1810, 0), 'minecraft:deepslate_brick_wall': (1811, 0), 'minecraft:deepslate_tile_wall': (1812, 0), + 'minecraft:mud_brick_wall': (1813, 0), } colors = [ 'white', 'orange', 'magenta', 'light_blue', @@ -1035,8 +862,6 @@ def __init__(self, regiondir, rel): self._blockmap['minecraft:%s_wall_banner' % colors[i]] = (177, i) # not rendering self._blockmap['minecraft:%s_shulker_box' % colors[i]] = (219 + i, 0) self._blockmap['minecraft:%s_glazed_terracotta' % colors[i]] = (235 + i, 0) - self._blockmap['minecraft:%s_concrete' % colors[i]] = (251, i) - self._blockmap['minecraft:%s_concrete_powder' % colors[i]] = (252, i) # Re-initialize upon unpickling def __getstate__(self): @@ -1064,8 +889,8 @@ def _get_block(self, palette_entry): 'minecraft:blackstone_slab','minecraft:polished_blackstone_slab', 'minecraft:polished_blackstone_brick_slab', 'minecraft:cobbled_deepslate_slab', 'minecraft:polished_deepslate_slab', 'minecraft:deepslate_brick_slab', - 'minecraft:deepslate_tile_slab', 'minecraft:mud_brick_slab' - ) + 'minecraft:deepslate_tile_slab', 'minecraft:mud_brick_slab', + 'minecraft:mangrove_slab' ) prismarine_slabs = ('minecraft:prismarine_slab','minecraft:dark_prismarine_slab','minecraft:prismarine_brick_slab') copper_slabs = ( @@ -1078,9 +903,11 @@ def _get_block(self, palette_entry): 'minecraft:waxed_weathered_cut_copper_slab', 'minecraft:waxed_oxidized_cut_copper_slab' ) + # Map from slab to double slab block slab_to_double = { 'minecraft:stone_slab': 'minecraft:stone', + 'minecraft:stone_brick_slab': 'minecraft:stone_bricks', 'minecraft:cobblestone_slab': 'minecraft:cobblestone', 'minecraft:sandstone_slab': 'minecraft:sandstone', 'minecraft:red_sandstone_slab': 'minecraft:red_sandstone', @@ -1123,16 +950,25 @@ def _get_block(self, palette_entry): 'minecraft:prismarine_slab': 'minecraft:prismarine', 'minecraft:dark_prismarine_slab': 'minecraft:dark_prismarine', 'minecraft:prismarine_brick_slab': 'minecraft:prismarine_bricks', - } + 'minecraft:mangrove_slab': 'minecraft:mangrove_planks', + } colors = ['white', 'orange', 'magenta', 'light_blue', 'yellow', 'lime', 'pink', 'gray', 'light_gray', 'cyan', 'purple', 'blue', 'brown', 'green', 'red', 'black'] + global modelblocks + key = palette_entry['Name'] - (block, data) = self._blockmap[key] + if key in self._blockmap: + (block, data) = self._blockmap[key] + elif key in modelblocks: + (block, data) = modelblocks[key] + else: + raise UnknownBlockException(key) + if key in ['minecraft:redstone_ore', 'minecraft:redstone_lamp']: if palette_entry['Properties']['lit'] == 'true': - block += 1 + data = 1 elif key.endswith('gate'): facing = palette_entry['Properties']['facing'] data = {'south': 0, 'west': 1, 'north': 2, 'east': 3}[facing] @@ -1153,6 +989,11 @@ def _get_block(self, palette_entry): elif key == 'minecraft:daylight_detector': if palette_entry['Properties']['inverted'] == 'true': block = 178 + elif key == 'minecraft:scaffolding': + if palette_entry['Properties']['bottom'] == 'true': + data = 1 + else: + data = 0 elif key == 'minecraft:redstone_wire': data = palette_entry['Properties']['power'] elif key == 'minecraft:grass_block': @@ -1163,6 +1004,10 @@ def _get_block(self, palette_entry): elif key in ('minecraft:sunflower', 'minecraft:lilac', 'minecraft:tall_grass', 'minecraft:large_fern', 'minecraft:rose_bush', 'minecraft:peony'): if palette_entry['Properties']['half'] == 'upper': data |= 0x08 + # handle unknown slabs. The lighting on these will suck. + elif key.endswith('_slab') and palette_entry['Properties']['type'] == 'top' and key not in wood_slabs + stone_slabs + prismarine_slabs + copper_slabs: + if key+'_top' in modelblocks: + (block, data) = modelblocks[key+'_top'] elif key in wood_slabs + stone_slabs + prismarine_slabs + copper_slabs: # handle double slabs if palette_entry['Properties']['type'] == 'top': @@ -1170,8 +1015,15 @@ def _get_block(self, palette_entry): elif palette_entry['Properties']['type'] == 'double': if key in wood_slabs: block = 125 # block_double_wooden_slab + elif key in slab_to_double : + if slab_to_double[key] in self._blockmap: + (block, data) = self._blockmap[slab_to_double[key]] + elif slab_to_double[key] in modelblocks: + (block, data) = modelblocks[slab_to_double[key]] else: - (block, data) = self._blockmap[slab_to_double[key]] + if key+'_top' in modelblocks: + # unknown slabs render as top if full is unknown + (block, data) = modelblocks[key+'_top'] elif key in ['minecraft:ladder', 'minecraft:chest', 'minecraft:ender_chest', 'minecraft:trapped_chest', 'minecraft:furnace', @@ -1200,11 +1052,15 @@ def _get_block(self, palette_entry): data = {'east': 1, 'west': 2, 'south': 3, 'north': 4}[facing] elif face == 'floor': data = {'east': 6, 'west': 6, 'south': 5, 'north': 5}[facing] - elif key == 'minecraft:nether_wart': + elif key in ['minecraft:nether_wart', 'minecraft:frosted_ice']: data = int(palette_entry['Properties']['age']) + elif key in ['minecraft:jigsaw']: + data = {'down_east': 0, 'down_east': 0, 'down_north': 0, 'down_south': 0, 'down_west': 0, + 'up_east': 1, 'up_north': 1, 'up_south': 1, 'up_west': 1, 'north_up': 2, 'south_up': 3, + 'west_up': 4, 'east_up': 5}[palette_entry['Properties']['orientation']] elif (key.endswith('shulker_box') or key.endswith('piston') or key in ['minecraft:observer', 'minecraft:dropper', 'minecraft:dispenser', - 'minecraft:piston_head', 'minecraft:jigsaw', 'minecraft:end_rod']): + 'minecraft:piston_head', 'minecraft:end_rod']): p = palette_entry['Properties'] data = {'down': 0, 'up': 1, 'north': 2, 'south': 3, 'west': 4, 'east': 5}[p['facing']] if ((key.endswith('piston') and p.get('extended', 'false') == 'true') or @@ -1220,15 +1076,12 @@ def _get_block(self, palette_entry): data |= 4 elif axis == 'z': data |= 8 - elif key == 'minecraft:quartz_pillar': - axis = palette_entry['Properties']['axis'] - if axis == 'x': - data = 3 - if axis == 'z': - data = 4 elif key in ['minecraft:basalt', 'minecraft:polished_basalt', 'minecraft:chain', 'minecraft:purpur_pillar', - 'minecraft:deepslate']: - axis = palette_entry['Properties']['axis'] + 'minecraft:deepslate', 'minecraft:hay_block', 'minecraft:quartz_pillar']: + if 'axis' in palette_entry['Properties']: + axis = palette_entry['Properties']['axis'] + else: + axis = 'y' data = {'y': 0, 'x': 1, 'z': 2}[axis] elif key in ['minecraft:redstone_torch','minecraft:redstone_wall_torch','minecraft:wall_torch', 'minecraft:soul_torch', 'minecraft:soul_wall_torch']: @@ -1380,7 +1233,7 @@ def _get_block(self, palette_entry): data = {'tip': 0, 'tip_merge': 1, 'middle': 2, 'frustum': 3, 'base': 4}[p['thickness']] data |= {'up': 0, 'down': 0b1000}[p['vertical_direction']] elif key in ['minecraft:small_amethyst_bud', 'minecraft:medium_amethyst_bud', 'minecraft:large_amethyst_bud', - 'minecraft:lightning_rod']: + 'minecraft:lightning_rod', 'minecraft:hopper']: p = palette_entry['Properties'] data = {'down': 0, 'up': 1, 'east': 2, 'south': 3, 'west': 4, 'north': 5}[p['facing']] elif key in ['minecraft:cave_vines_plant', 'minecraft:cave_vines']: @@ -1505,7 +1358,7 @@ def _packed_longarray_to_shorts_v116(self, long_array, n, num_palette): return result - def _get_blockdata_v118(self, section, unrecognized_block_types, longarray_unpacker): + def _get_blockdata_v118(self, section, longarray_unpacker): block_states = section['block_states'] palette = block_states.get('palette') block_states_data = block_states.get('data') @@ -1522,8 +1375,10 @@ def _get_blockdata_v118(self, section, unrecognized_block_types, longarray_unpac key = palette[i] try: translated_blocks[i], translated_data[i] = self._get_block(key) - except KeyError: - pass # We already have initialised arrays with 0 (= air) + except UnknownBlockException as e: + # unrecognized blocks are usually found in 'blockstate' and need to be manually implemented for now + # inclused heads, skulls and candles + pass # Turn the BlockStates array into a 16x16x16 numpy matrix of shorts. blocks = numpy.empty((4096,), dtype=numpy.uint16) @@ -1538,7 +1393,7 @@ def _get_blockdata_v118(self, section, unrecognized_block_types, longarray_unpac return (blocks, data) - def _get_blockdata_v113(self, section, unrecognized_block_types, longarray_unpacker): + def _get_blockdata_v113(self, section, longarray_unpacker): # Translate each entry in the palette to a 1.2-era (block, data) int pair. num_palette_entries = len(section['Palette']) translated_blocks = numpy.zeros((num_palette_entries,), dtype=numpy.uint16) # block IDs @@ -1547,7 +1402,7 @@ def _get_blockdata_v113(self, section, unrecognized_block_types, longarray_unpac key = section['Palette'][i] try: translated_blocks[i], translated_data[i] = self._get_block(key) - except KeyError: + except UnknownBlockException as e: pass # We already have initialised arrays with 0 (= air) # Turn the BlockStates array into a 16x16x16 numpy matrix of shorts. @@ -1593,6 +1448,10 @@ def _get_blockdata_v112(self, section): return (blocks, data_expanded) + def add_to_blockmap(self, blockmap): + global modelblocks + modelblocks = blockmap + #@log_other_exceptions def get_chunk(self, x, z): """Returns a dictionary object representing the "Level" NBT Compound @@ -1710,7 +1569,6 @@ def get_chunk(self, x, z): chunk_data['Biomes'] = biomes chunk_data['NewBiomes'] = (len(biomes.shape) == 3) - unrecognized_block_types = {} for section in chunk_data['Sections']: # Turn the skylight array into a 16x16x16 matrix. The array comes @@ -1746,9 +1604,9 @@ def get_chunk(self, x, z): section['BlockLight'] = blocklight_expanded if 'block_states' in section: - (blocks, data) = self._get_blockdata_v118(section, unrecognized_block_types, longarray_unpacker) + (blocks, data) = self._get_blockdata_v118(section, longarray_unpacker) elif 'Palette' in section: - (blocks, data) = self._get_blockdata_v113(section, unrecognized_block_types, longarray_unpacker) + (blocks, data) = self._get_blockdata_v113(section, longarray_unpacker) elif 'Data' in section: (blocks, data) = self._get_blockdata_v112(section) else: # Special case introduced with 1.14 @@ -1764,9 +1622,6 @@ def get_chunk(self, x, z): logging.debug("Full traceback:", exc_info=1) raise nbt.CorruptChunkError() - for k in unrecognized_block_types: - logging.debug("Found %d blocks of unknown type %s" % (unrecognized_block_types[k], k)) - return chunk_data @@ -1897,6 +1752,8 @@ def iterate_newer_chunks(self,filemtime): return self._r.iterate_newer_chunks(filemtime) def get_chunk_mtime(self, x, z): return self._r.get_chunk_mtime(x,z) + def add_to_blockmap(self, blockmap): + return self._r.add_to_blockmap(blockmap) # see RegionSet.rotate. These values are chosen so that they can be # passed directly to rot90; this means that they're the number of