diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index b8af17a..06a2048 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -125,25 +125,6 @@ def get_tileentity_data(level): data = level['TileEntities'] return data -# This set holds blocks ids that can be seen through, for occlusion calculations -transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, - 81, 83, 85, 90, 92, 93, 94, 96, 101, 102, 104, 105, - 106, 107, 108, 109]) - -# This set holds block ids that are solid blocks -solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 35, 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, - 61, 62, 67, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) - -# This set holds block ids that are fluid blocks -fluid_blocks = set([8,9,10,11]) - -# This set holds block ids that are not candidates for spawning mobs on -# (glass, slabs, stairs, fluids, ice, pistons, webs,TNT, wheat, cactus, iron bars, glass planes, fences, fence gate, cake, bed, repeaters, trapdoor) -nospawn_blocks = set([20,26, 29, 30, 33, 34, 44, 46, 53, 59, 67, 79, 81, 85, 92, 93, 94, 96, 107, 109, 101, 102]).union(fluid_blocks) - class ChunkCorrupt(Exception): pass @@ -412,7 +393,7 @@ def generate_facemasks(): left = Image.new("L", (24,24), 0) whole = Image.new("L", (24,24), 0) - toppart = textures.transform_image(white) + toppart = textures.transform_image_top(white) leftpart = textures.transform_image_side(white) # using the real PIL paste here (not alpha_over) because there is diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 44f30f8..0fc1109 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -20,12 +20,22 @@ static PyObject *textures = NULL; static PyObject *chunk_mod = NULL; static PyObject *blockmap = NULL; -static PyObject *special_blocks = NULL; -static PyObject *specialblockmap = NULL; + +unsigned int max_blockid = 0; +unsigned int max_data = 0; +unsigned char *block_properties = NULL; + +static PyObject *known_blocks = NULL; static PyObject *transparent_blocks = NULL; +static PyObject *solid_blocks = NULL; +static PyObject *fluid_blocks = NULL; +static PyObject *nospawn_blocks = NULL; PyObject *init_chunk_render(PyObject *self, PyObject *args) { + PyObject *tmp = NULL; + unsigned int i; + /* this function only needs to be called once, anything more should be * ignored */ if (blockmap) { @@ -47,29 +57,54 @@ PyObject *init_chunk_render(PyObject *self, PyObject *args) { blockmap = PyObject_GetAttrString(textures, "blockmap"); if (!blockmap) return NULL; - special_blocks = PyObject_GetAttrString(textures, "special_blocks"); - if (!special_blocks) + + tmp = PyObject_GetAttrString(textures, "max_blockid"); + if (!tmp) return NULL; - specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); - if (!specialblockmap) + max_blockid = PyInt_AsLong(tmp); + tmp = PyObject_GetAttrString(textures, "max_data"); + if (!tmp) return NULL; - transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks"); + max_data = PyInt_AsLong(tmp); + + /* assemble the property table */ + known_blocks = PyObject_GetAttrString(textures, "known_blocks"); + if (!known_blocks) + return NULL; + transparent_blocks = PyObject_GetAttrString(textures, "transparent_blocks"); if (!transparent_blocks) return NULL; + solid_blocks = PyObject_GetAttrString(textures, "solid_blocks"); + if (!solid_blocks) + return NULL; + fluid_blocks = PyObject_GetAttrString(textures, "fluid_blocks"); + if (!fluid_blocks) + return NULL; + nospawn_blocks = PyObject_GetAttrString(textures, "nospawn_blocks"); + if (!nospawn_blocks) + return NULL; + block_properties = calloc(max_blockid, sizeof(unsigned char)); + for (i = 0; i < max_blockid; i++) { + PyObject *block = PyInt_FromLong(i); + + if (PySequence_Contains(known_blocks, block)) + block_properties[i] |= 1 << KNOWN; + if (PySequence_Contains(transparent_blocks, block)) + block_properties[i] |= 1 << TRANSPARENT; + if (PySequence_Contains(solid_blocks, block)) + block_properties[i] |= 1 << SOLID; + if (PySequence_Contains(fluid_blocks, block)) + block_properties[i] |= 1 << FLUID; + if (PySequence_Contains(nospawn_blocks, block)) + block_properties[i] |= 1 << NOSPAWN; + + Py_DECREF(block); + } + Py_RETURN_NONE; } -int -is_transparent(unsigned char b) { - PyObject *block = PyInt_FromLong(b); - int ret = PySequence_Contains(transparent_blocks, block); - Py_DECREF(block); - return ret; - -} - - unsigned char check_adjacent_blocks(RenderState *state, int x,int y,int z, unsigned char blockid) { /* @@ -284,14 +319,15 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { return final_data; - /* portal, iron bars and glass panes - * Note: iron bars and glass panes "stick" to other blocks, but - * at the moment of writing this is not clear which ones stick and - * which others no, so for the moment stick only with himself. - * This is a TODO! - */ - } else if ((state->block == 90) || (state->block == 101) || - (state->block == 102)) { + } else if ((state->block == 101) || (state->block == 102)) { + /* iron bars and glass panes: + * they seem to stick to almost everything but air, but + * not sure yet! Still a TODO! */ + /* return check adjacent blocks with air, bit inverted */ + return check_adjacent_blocks(state, x, y, z, 0) ^ 0x0f; + + } else if ((state->block == 90) || (state->block == 113)) { + /* portal and nether brick fences */ return check_adjacent_blocks(state, x, y, z, state->block); } @@ -372,14 +408,15 @@ chunk_render(PyObject *self, PyObject *args) { for (state.x = 15; state.x > -1; state.x--) { for (state.y = 0; state.y < 16; state.y++) { - PyObject *blockid = NULL; - + /* set up the render coordinates */ state.imgx = xoff + state.x*12 + state.y*12; /* 128*12 -- offset for z direction, 15*6 -- offset for x */ state.imgy = yoff - state.x*6 + state.y*6 + 128*12 + 15*6; for (state.z = 0; state.z < 128; state.z++) { + unsigned char ancilData; + state.imgy -= 12; /* get blockid */ @@ -395,63 +432,46 @@ chunk_render(PyObject *self, PyObject *args) { if ((state.imgy >= imgsize1 + 24) || (state.imgy <= -24)) { continue; } - - /* decref'd on replacement *and* at the end of the z for block */ - if (blockid) { - Py_DECREF(blockid); - } - blockid = PyInt_FromLong(state.block); - - // check for occlusion + /* check for occlusion */ if (render_mode_occluded(rendermode, state.x, state.y, state.z)) { continue; } - // everything stored here will be a borrowed ref + /* everything stored here will be a borrowed ref */ - /* get the texture and mask from block type / ancil. data */ - if (!PySequence_Contains(special_blocks, blockid)) { - /* t = textures.blockmap[blockid] */ - t = PyList_GetItem(blockmap, state.block); + ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); + state.block_data = ancilData; + /* block that need pseudo ancildata: + * grass, water, glass, chest, restone wire, + * ice, fence, portal, iron bars, glass panes */ + if ((state.block == 2) || (state.block == 9) || + (state.block == 20) || (state.block == 54) || + (state.block == 55) || (state.block == 79) || + (state.block == 85) || (state.block == 90) || + (state.block == 101) || (state.block == 102) || + (state.block == 113)) { + ancilData = generate_pseudo_data(&state, ancilData); + state.block_pdata = ancilData; } else { - PyObject *tmp; - - unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); - state.block_data = ancilData; - /* block that need pseudo ancildata: - * grass, water, glass, chest, restone wire, - * ice, fence, portal, iron bars, glass panes */ - if ((state.block == 2) || (state.block == 9) || - (state.block == 20) || (state.block == 54) || - (state.block == 55) || (state.block == 79) || - (state.block == 85) || (state.block == 90) || - (state.block == 101) || (state.block == 102)) { - ancilData = generate_pseudo_data(&state, ancilData); - state.block_pdata = ancilData; - } else { - state.block_pdata = 0; - } - - tmp = PyTuple_New(2); - - Py_INCREF(blockid); /* because SetItem steals */ - PyTuple_SetItem(tmp, 0, blockid); - PyTuple_SetItem(tmp, 1, PyInt_FromLong(ancilData)); - - /* this is a borrowed reference. no need to decref */ - t = PyDict_GetItem(specialblockmap, tmp); - Py_DECREF(tmp); + state.block_pdata = 0; } + /* make sure our block info is in-bounds */ + if (state.block >= max_blockid || ancilData >= max_data) + continue; + + /* get the texture */ + t = PyList_GET_ITEM(blockmap, max_data * state.block + ancilData); + /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { PyObject *src, *mask, *mask_light; int randx = 0, randy = 0; src = PyTuple_GetItem(t, 0); - mask = PyTuple_GetItem(t, 1); - mask_light = PyTuple_GetItem(t, 2); + mask = PyTuple_GetItem(t, 0); + mask_light = PyTuple_GetItem(t, 1); if (mask == Py_None) mask = src; @@ -473,11 +493,6 @@ chunk_render(PyObject *self, PyObject *args) { } } } - - if (blockid) { - Py_DECREF(blockid); - blockid = NULL; - } } } diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 279c06b..0e45a2f 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 12 +#define OVERVIEWER_EXTENSION_VERSION 13 /* Python PIL, and numpy headers */ #include @@ -94,8 +94,32 @@ typedef struct { PyObject *right_blocks; } RenderState; PyObject *init_chunk_render(PyObject *self, PyObject *args); -int is_transparent(unsigned char b); PyObject *chunk_render(PyObject *self, PyObject *args); +typedef enum +{ + KNOWN, + TRANSPARENT, + SOLID, + FLUID, + NOSPAWN, +} BlockProperty; +/* globals set in init_chunk_render, here because they're used + in block_has_property */ +extern unsigned int max_blockid; +extern unsigned int max_data; +extern unsigned char *block_properties; +static inline int +block_has_property(unsigned char b, BlockProperty prop) { + if (b >= max_blockid || !(block_properties[b] & (1 << KNOWN))) { + /* block is unknown, return defaults */ + if (prop == TRANSPARENT) + return 1; + return 0; + } + + return block_properties[b] & (1 << prop); +} +#define is_transparent(b) block_has_property((b), TRANSPARENT) /* pull in the rendermode info */ #include "rendermodes.h" diff --git a/overviewer_core/src/rendermode-normal.c b/overviewer_core/src/rendermode-normal.c index e37a47a..24bf665 100644 --- a/overviewer_core/src/rendermode-normal.c +++ b/overviewer_core/src/rendermode-normal.c @@ -173,7 +173,9 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * * get constant brown color (see textures.py) */ (((state->block == 104) || (state->block == 105)) && (state->block_data != 7)) || /* vines */ - state->block == 106) + state->block == 106 || + /* lily pads */ + state->block == 111) { /* do the biome stuff! */ PyObject *facemask = mask; @@ -240,6 +242,10 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * /* vines */ color = PySequence_GetItem(self->grasscolor, index); break; + case 111: + /* lily padas */ + color = PySequence_GetItem(self->grasscolor, index); + break; default: break; }; @@ -270,7 +276,8 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * facemask = NULL; } - if (state->block == 18 || state->block == 106) /* leaves and vines */ + if (state->block == 18 || state->block == 106 || state->block == 111) + /* leaves, vines and lyli pads */ { r = 37; g = 118; diff --git a/overviewer_core/src/rendermode-overlay.c b/overviewer_core/src/rendermode-overlay.c index 17b760b..47c44fc 100644 --- a/overviewer_core/src/rendermode-overlay.c +++ b/overviewer_core/src/rendermode-overlay.c @@ -37,10 +37,6 @@ rendermode_overlay_start(void *data, RenderState *state, PyObject *options) { Py_DECREF(facemasks_py); self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - - self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); - self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); - self->get_color = get_color; return 0; @@ -52,8 +48,6 @@ rendermode_overlay_finish(void *data, RenderState *state) { Py_DECREF(self->facemask_top); Py_DECREF(self->white_color); - Py_DECREF(self->solid_blocks); - Py_DECREF(self->fluid_blocks); } static int @@ -81,8 +75,7 @@ static void rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeOverlay *self = (RenderModeOverlay *)data; unsigned char r, g, b, a; - PyObject *top_block_py, *block_py; - + // exactly analogous to edge-line code for these special blocks int increment=0; if (state->block == 44) // half-step @@ -101,27 +94,19 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject } /* check to be sure this block is solid/fluid */ - top_block_py = PyInt_FromLong(top_block); - if (PySequence_Contains(self->solid_blocks, top_block_py) || - PySequence_Contains(self->fluid_blocks, top_block_py)) { + if (block_has_property(top_block, SOLID) || block_has_property(top_block, FLUID)) { /* top block is fluid or solid, skip drawing */ - Py_DECREF(top_block_py); return; } - Py_DECREF(top_block_py); } /* check to be sure this block is solid/fluid */ - block_py = PyInt_FromLong(state->block); - if (!PySequence_Contains(self->solid_blocks, block_py) && - !PySequence_Contains(self->fluid_blocks, block_py)) { + if (!block_has_property(state->block, SOLID) && !block_has_property(state->block, FLUID)) { /* not fluid or solid, skip drawing the overlay */ - Py_DECREF(block_py); return; } - Py_DECREF(block_py); /* get our color info */ self->get_color(data, state, &r, &g, &b, &a); diff --git a/overviewer_core/src/rendermode-spawn.c b/overviewer_core/src/rendermode-spawn.c index a5c0a27..275f33c 100644 --- a/overviewer_core/src/rendermode-spawn.c +++ b/overviewer_core/src/rendermode-spawn.c @@ -35,13 +35,10 @@ static void get_color(void *data, RenderState *state, /* default to no overlay, until told otherwise */ *a = 0; - block_py = PyInt_FromLong(state->block); - if (PySequence_Contains(self->nospawn_blocks, block_py)) { + if (block_has_property(state->block, NOSPAWN)) { /* nothing can spawn on this */ - Py_DECREF(block_py); return; } - Py_DECREF(block_py); blocklight = getArrayByte3D(self->blocklight, x, y, MIN(127, z_light)); @@ -72,7 +69,6 @@ rendermode_spawn_start(void *data, RenderState *state, PyObject *options) { /* now do custom initializations */ self = (RenderModeSpawn *)data; - self->nospawn_blocks = PyObject_GetAttrString(state->chunk, "nospawn_blocks"); self->blocklight = PyObject_GetAttrString(state->self, "blocklight"); self->skylight = PyObject_GetAttrString(state->self, "skylight"); @@ -87,7 +83,6 @@ rendermode_spawn_finish(void *data, RenderState *state) { /* first free all *our* stuff */ RenderModeSpawn* self = (RenderModeSpawn *)data; - Py_DECREF(self->nospawn_blocks); Py_DECREF(self->blocklight); Py_DECREF(self->skylight); diff --git a/overviewer_core/src/rendermodes.h b/overviewer_core/src/rendermodes.h index 5084cd8..e253885 100644 --- a/overviewer_core/src/rendermodes.h +++ b/overviewer_core/src/rendermodes.h @@ -143,8 +143,6 @@ extern RenderModeInterface rendermode_normal; typedef struct { /* top facemask and white color image, for drawing overlays */ PyObject *facemask_top, *white_color; - /* only show overlay on top of solid or fluid blocks */ - PyObject *solid_blocks, *fluid_blocks; /* can be overridden in derived classes to control overlay alpha and color last four vars are r, g, b, a out */ @@ -206,8 +204,6 @@ typedef struct { /* inherits from overlay */ RenderModeOverlay parent; - /* used to figure out which blocks are spawnable */ - PyObject *nospawn_blocks; PyObject *skylight, *blocklight; } RenderModeSpawn; extern RenderModeInterface rendermode_spawn; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 3bd5f0c..931d4ec 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -24,11 +24,26 @@ from random import randint import numpy from PIL import Image, ImageEnhance, ImageOps, ImageDraw import logging +import functools import util import composite +## +## useful global variables +## + +# user-provided path given by --texture-path _find_file_local_path = None +# image background color to use +bgcolor = None +# an array of the textures in terrain.png, split up +terrain_images = None + +## +## Helpers for opening textures +## + def _find_file(filename, mode="rb", verbose=False): """Searches for the given file and returns an open handle to it. This searches the following locations in this order: @@ -140,9 +155,6 @@ def _load_image(filename): buffer = StringIO(fileobj.read()) return Image.open(buffer).convert("RGBA") -def _get_terrain_image(): - return _load_image("terrain.png") - def _split_terrain(terrain): """Builds and returns a length 256 array of each 16x16 chunk of texture""" textures = [] @@ -163,7 +175,11 @@ def _split_terrain(terrain): return textures -def transform_image(img, blockID=None): +## +## Image Transformation Functions +## + +def transform_image_top(img): """Takes a PIL image and rotates it left 45 degrees and shrinks the y axis by a factor of 2. Returns the resulting image, which will be 24x12 pixels @@ -191,25 +207,10 @@ def transform_image(img, blockID=None): newimg = img.transform((24,12), Image.AFFINE, transform) return newimg -def transform_image_side(img, blockID=None): +def transform_image_side(img): """Takes an image and shears it for the left side of the cube (reflect for the right side)""" - if blockID in (44,): # step block - # make the top half transparent - # (don't just crop img, since we want the size of - # img to be unchanged - mask = img.crop((0,8,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,0,16,8), mask) - img = n - if blockID in (78,): # snow - # make the top three quarters transparent - mask = img.crop((0,12,16,16)) - n = Image.new(img.mode, img.size, bgcolor) - composite.alpha_over(n, mask,(0,12,16,16), mask) - img = n - # Size of the cube side before shear img = img.resize((12,12), Image.ANTIALIAS) @@ -222,7 +223,7 @@ def transform_image_side(img, blockID=None): newimg = img.transform((12,18), Image.AFFINE, transform) return newimg -def transform_image_slope(img, blockID=None): +def transform_image_slope(img): """Takes an image and shears it in the shape of a slope going up in the -y direction (reflect for +x direction). Used for minetracks""" @@ -239,7 +240,7 @@ def transform_image_slope(img, blockID=None): return newimg -def transform_image_angle(img, angle, blockID=None): +def transform_image_angle(img, angle): """Takes an image an shears it in arbitrary angle with the axis of rotation being vertical. @@ -279,7 +280,7 @@ def transform_image_angle(img, angle, blockID=None): return newimg -def _build_block(top, side, blockID=None): +def build_block(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 @@ -287,13 +288,13 @@ def _build_block(top, side, blockID=None): img = Image.new("RGBA", (24,24), bgcolor) original_texture = top.copy() - top = transform_image(top, blockID) + top = transform_image_top(top) if not side: composite.alpha_over(img, top, (0,0), top) return img - side = transform_image_side(side, blockID) + side = transform_image_side(side) otherside = side.transpose(Image.FLIP_LEFT_RIGHT) # Darken the sides slightly. These methods also affect the alpha layer, @@ -306,39 +307,9 @@ def _build_block(top, side, blockID=None): otherside = ImageEnhance.Brightness(otherside).enhance(0.8) otherside.putalpha(othersidealpha) - ## special case for tall-grass, fern, dead shrub, and pumpkin/melon stem - if blockID in (31,32,104,105): - front = original_texture.resize((14,11), Image.ANTIALIAS) - composite.alpha_over(img, front, (5,9)) - return img - - ## special case for non-block things - if blockID in (37,38,6,39,40,83,30): ## flowers, sapling, mushrooms, reeds, web - # - # instead of pasting these blocks at the cube edges, place them in the middle: - # and omit the top - composite.alpha_over(img, side, (6,3), side) - composite.alpha_over(img, otherside, (6,3), otherside) - return img - - if blockID in (81,): # cacti! - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,6), otherside) - composite.alpha_over(img, top, (0,0), top) - elif blockID in (44,): # half step - # shift each texture down 6 pixels - composite.alpha_over(img, side, (0,12), side) - composite.alpha_over(img, otherside, (12,12), otherside) - composite.alpha_over(img, top, (0,6), top) - elif blockID in (78,): # snow - # shift each texture down 9 pixels - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,9), top) - else: - composite.alpha_over(img, top, (0,0), top) - composite.alpha_over(img, side, (0,6), side) - composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,0), top) + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) # Manually touch up 6 pixels that leave a gap because of how the # shearing works out. This makes the blocks perfectly tessellate-able @@ -351,8 +322,7 @@ def _build_block(top, side, blockID=None): return img - -def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None): +def build_full_block(top, side1, side2, side3, side4, bottom=None): """From a top texture, a bottom texture and 4 different side textures, build a full block with four differnts faces. All images should be 16x16 image objects. Returns a 24x24 image. Can be used to render any block. @@ -397,7 +367,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None # first back sides if side1 != None : - side1 = transform_image_side(side1, blockID) + side1 = transform_image_side(side1) side1 = side1.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side. @@ -409,7 +379,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None if side2 != None : - side2 = transform_image_side(side2, blockID) + side2 = transform_image_side(side2) # Darken this side. sidealpha2 = side2.split()[3] @@ -419,12 +389,12 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side2, (12,0), side2) if bottom != None : - bottom = transform_image(bottom, blockID) + bottom = transform_image_top(bottom) composite.alpha_over(img, bottom, (0,12), bottom) # front sides if side3 != None : - side3 = transform_image_side(side3, blockID) + side3 = transform_image_side(side3) # Darken this side sidealpha = side3.split()[3] @@ -434,7 +404,7 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side3, (0,6), side3) if side4 != None : - side4 = transform_image_side(side4, blockID) + side4 = transform_image_side(side4) side4 = side4.transpose(Image.FLIP_LEFT_RIGHT) # Darken this side @@ -445,110 +415,31 @@ def _build_full_block(top, side1, side2, side3, side4, bottom=None, blockID=None composite.alpha_over(img, side4, (12,6), side4) if top != None : - top = transform_image(top, blockID) + top = transform_image_top(top) composite.alpha_over(img, top, (0, increment), top) return img - -def _build_blockimages(): - """Returns a mapping from blockid to an image of that block in perspective - The values of the mapping are actually (image in RGB mode, alpha channel). - This is not appropriate for all block types, only block types that are - proper cubes""" - - # Top textures of all block types. The number here is the index in the - # texture array (terrain_images), which comes from terrain.png's cells, left to right top to - # bottom. - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - topids = [ -1, 1, 0, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 69, 72, 73, 75, -1,102,103,104,105,-1, 102, -1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 137, - ] - - # NOTE: For non-block textures, the sideid is ignored, but can't be -1 - - # And side textures of all block types - # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - sideids = [ -1, 1, 3, 2, 16, 4, -1, 17,205,205,237,237, 18, 19, 32, 33, - # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - 34, -1, 52, 48, -1,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1, - # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, - # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 - 36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1, - # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 - -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 51, -1, -1, -1, 66, -1, - # 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - 66, 70, 72, 73, 74,-1 ,118,103,104,105, -1, 118,-1, -1, -1, -1, - # 96 97 98 99 100 101 102 103 - -1, -1, -1, -1, -1, -1, -1, 136, - ] - - # This maps block id to the texture that goes on the side of the block - if len(topids) != len(sideids): - raise Exception("mismatched lengths") - - allimages = [] - for toptextureid, sidetextureid,blockID in zip(topids, sideids,range(len(topids))): - if toptextureid == -1 or sidetextureid == -1: - allimages.append(None) - continue - - toptexture = terrain_images[toptextureid] - sidetexture = terrain_images[sidetextureid] - - ## _build_block needs to know about the block ID, not just the textures - ## of the block or the texture ID - img = _build_block(toptexture, sidetexture, blockID) - - allimages.append(generate_texture_tuple(img, blockID)) - - # Future block types: - while len(allimages) < 256: - allimages.append(None) - return allimages - -def load_water(): - """Evidentially, the water and lava textures are not loaded from any files - in the jar (that I can tell). They must be generated on the fly. While - terrain.png does have some water and lava cells, not all texture packs - include them. So I load them here from a couple pngs included. - - This mutates the blockmap global list with the new water and lava blocks. - Block 9, standing water, is given a block with only the top face showing. - Block 8, flowing water, is given a full 3 sided cube.""" - - try: - # try the MCPatcher water first, in case it's present - watertexture = _load_image("custom_water_still.png") - watertexture = watertexture.crop((0, 0, watertexture.size[0], watertexture.size[0])) - except IOError: - watertexture = _load_image("water.png") - w1 = _build_block(watertexture, None) - blockmap[9] = generate_texture_tuple(w1,9) - w2 = _build_block(watertexture, watertexture) - blockmap[8] = generate_texture_tuple(w2,8) +def build_sprite(side): + """From a side texture, create a sprite-like texture such as those used + for spiderwebs or flowers.""" + img = Image.new("RGBA", (24,24), bgcolor) - try: - # try the MCPatcher lava first, in case it's present - lavatexture = _load_image("custom_lava_still.png") - lavatexture = lavatexture.crop((0, 0, lavatexture.size[0], lavatexture.size[0])) - except IOError: - lavatexture = _load_image("lava.png") - lavablock = _build_block(lavatexture, lavatexture) - blockmap[10] = generate_texture_tuple(lavablock,10) - blockmap[11] = blockmap[10] + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + composite.alpha_over(img, side, (6,3), side) + composite.alpha_over(img, otherside, (6,3), otherside) + return img + +def build_billboard(tex): + """From a texture, create a billboard-like texture such as those used for + tall grass or melon stems.""" + img = Image.new("RGBA", (24,24), bgcolor) + + front = tex.resize((14, 11), Image.ANTIALIAS) + composite.alpha_over(img, front, (5,9)) + return img def generate_opaque_mask(img): """ Takes the alpha channel of the image and generates a mask @@ -558,1724 +449,29 @@ def generate_opaque_mask(img): alpha = img.split()[3] return alpha.point(lambda a: int(min(a, 25.5) * 10)) -def generate_texture_tuple(img, blockid): - """ This takes an image and returns the needed tuple for the - blockmap list and specialblockmap dictionary.""" - return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) - -def generate_special_texture(blockID, data): - """Generates a special texture, such as a correctly facing minecraft track""" - - data = convert_data(blockID, data) - - # blocks need to be handled here (and in chunk.py) - - if blockID == 2: # grass - # data & 0x10 means SNOW sides - side_img = terrain_images[3] - if data & 0x10: - side_img = terrain_images[68] - img = _build_block(terrain_images[0], side_img, 2) - if not data & 0x10: - global biome_grass_texture - composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) - return generate_texture_tuple(img, blockID) - - - if blockID == 6: # saplings - # The bottom two bits are used fo the sapling type, the top two - # bits are used as a grow-counter for the tree. - - if data & 0x3 == 0: # usual saplings - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - if data & 0x3 == 1: # spruce sapling - toptexture = terrain_images[63] - sidetexture = terrain_images[63] - - if data & 0x3 == 2: # birch sapling - toptexture = terrain_images[79] - sidetexture = terrain_images[79] - - if data & 0x3 == 3: # unused usual sapling - toptexture = terrain_images[15] - sidetexture = terrain_images[15] - - img = _build_block(toptexture, sidetexture, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID == 9 or blockID == 20 or blockID == 79: # spring water, flowing water and waterfall water, AND glass, AND ice - # water,glass and ice share the way to be rendered - if blockID == 9: - try: - texture = _load_image("custom_water_still.png") - texture = texture.crop((0, 0, texture.size[0], texture.size[0])) - except IOError: - texture = _load_image("water.png") - elif blockID == 20: - texture = terrain_images[49] - else: - texture = terrain_images[67] - - if (data & 0b10000) == 16: - 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: side3 = None - - if (data & 0b0100) == 4: - side4 = texture # bottom right - else: side4 = None - - # if nothing shown do not draw at all - if top == side3 == side4 == None: - return None - - img = _build_full_block(top,None,None,side3,side4) - return generate_texture_tuple(img, blockID) - - - if blockID == 17: # wood: normal, birch and pines - top = terrain_images[21] - if data == 0: - side = terrain_images[20] - img = _build_block(top, side, 17) - if data == 1: - side = terrain_images[116] - img = _build_block(top, side, 17) - if data == 2: - side = terrain_images[117] - img = _build_block(top, side, 17) - - return generate_texture_tuple(img, blockID) - - - if blockID == 18: # leaves - t = terrain_images[52] - if data == 1: - # pine! - t = terrain_images[132] - img = _build_block(t, t, 18) - return generate_texture_tuple(img, blockID) - - - if blockID == 26: # bed - increment = 8 - left_face = None - right_face = None - if data & 0x8 == 0x8: # head of the bed - top = terrain_images[135] - if data & 0x00 == 0x00: # head pointing to West - top = top.copy().rotate(270) - left_face = terrain_images[151] - right_face = terrain_images[152] - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = terrain_images[152] - right_face = terrain_images[151] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - right_face = None - if data & 0x03 == 0x03: # South - right_face = None - right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) - - else: # foot of the bed - top = terrain_images[134] - if data & 0x00 == 0x00: # head pointing to West - top = top.rotate(270) - left_face = terrain_images[150] - right_face = None - if data & 0x01 == 0x01: # ... North - top = top.rotate(270) - left_face = None - right_face = terrain_images[150] - if data & 0x02 == 0x02: # East - top = top.rotate(180) - left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) - if data & 0x03 == 0x03: # South - left_face = terrain_images[149] - right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) - - top = (top, increment) - img = _build_full_block(top, None, None, left_face, right_face) - - return generate_texture_tuple(img, blockID) - - - if blockID == 31: # tall grass - if data == 0: # dead shrub - texture = terrain_images[55] - elif data == 1: # tall grass - texture = terrain_images[39] - elif data == 2: # fern - texture = terrain_images[56] - - img = _build_block(texture, texture, blockID) - return generate_texture_tuple(img,31) - - - if blockID in (29,33): # sticky and normal body piston. - if blockID == 29: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[109].copy() - interior_t = terrain_images[110].copy() - - if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff - # remove piston texture from piston body - ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x1: # up - img = _build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) - - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t , None, None ,side_t.rotate(90), back_t) - - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) - temp = transform_image_side(interior_t, blockID) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (9,5), temp) - - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) - temp = transform_image_side(interior_t, blockID) - composite.alpha_over(img, temp, (3,5), temp) - - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) - - else: # pushed in, normal full blocks, easy stuff - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block(back_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x1: # up - img = _build_full_block(piston_t ,None ,None ,side_t, side_t) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) - elif data & 0x07 == 0x3: # west - img = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) - elif data & 0x07 == 0x5: # south - img = _build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) - - - return generate_texture_tuple(img, blockID) - - - if blockID == 34: # piston extension (sticky and normal) - if (data & 0x8) == 0x8: # sticky - piston_t = terrain_images[106].copy() - else: # normal - piston_t = terrain_images[107].copy() - - # other textures - side_t = terrain_images[108].copy() - back_t = terrain_images[107].copy() - # crop piston body - ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the horizontal piston extension stick - h_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t, blockID) - composite.alpha_over(h_stick, temp, (1,7), temp) - temp = transform_image(side_t.rotate(90)) - composite.alpha_over(h_stick, temp, (1,1), temp) - # Darken it - sidealpha = h_stick.split()[3] - h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) - h_stick.putalpha(sidealpha) - - # generate the vertical piston extension stick - v_stick = Image.new("RGBA", (24,24), bgcolor) - temp = transform_image_side(side_t.rotate(90), blockID) - composite.alpha_over(v_stick, temp, (12,6), temp) - temp = temp.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(v_stick, temp, (1,6), temp) - # Darken it - sidealpha = v_stick.split()[3] - v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) - v_stick.putalpha(sidealpha) - - # Piston orientation is stored in the 3 first bits - if data & 0x07 == 0x0: # down - side_t = side_t.rotate(180) - img = _build_full_block((back_t, 12) ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,-3), v_stick) - elif data & 0x07 == 0x1: # up - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(piston_t ,None ,None ,side_t, side_t) - composite.alpha_over(img, v_stick, (0,4), v_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x2: # east - img = _build_full_block(side_t ,None ,None ,side_t.rotate(90), None) - temp = transform_image_side(back_t, blockID).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, temp, (2,2), temp) - composite.alpha_over(img, h_stick, (6,3), h_stick) - elif data & 0x07 == 0x3: # west - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) - composite.alpha_over(img, h_stick, (0,0), h_stick) - composite.alpha_over(img, img2, (0,0), img2) - elif data & 0x07 == 0x4: # north - img = _build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - elif data & 0x07 == 0x5: # south - img = Image.new("RGBA", (24,24), bgcolor) - img2 = _build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) - temp = transform_image_side(back_t, blockID) - composite.alpha_over(img2, temp, (10,2), temp) - composite.alpha_over(img, img2, (0,0), img2) - composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 35: # wool - if data == 0: # white - top = side = terrain_images[64] - elif data == 1: # orange - top = side = terrain_images[210] - elif data == 2: # magenta - top = side = terrain_images[194] - elif data == 3: # light blue - top = side = terrain_images[178] - elif data == 4: # yellow - top = side = terrain_images[162] - elif data == 5: # light green - top = side = terrain_images[146] - elif data == 6: # pink - top = side = terrain_images[130] - elif data == 7: # grey - top = side = terrain_images[114] - elif data == 8: # light grey - top = side = terrain_images[225] - elif data == 9: # cyan - top = side = terrain_images[209] - elif data == 10: # purple - top = side = terrain_images[193] - elif data == 11: # blue - top = side = terrain_images[177] - elif data == 12: # brown - top = side = terrain_images[161] - elif data == 13: # dark green - top = side = terrain_images[145] - elif data == 14: # red - top = side = terrain_images[129] - elif data == 15: # black - top = side = terrain_images[113] - - img = _build_block(top, side, 35) - return generate_texture_tuple(img, blockID) - - - if blockID in (43,44): # slab and double-slab - - if data == 0: # stone slab - top = terrain_images[6] - side = terrain_images[5] - elif data == 1: # stone slab - top = terrain_images[176] - side = terrain_images[192] - elif data == 2: # wooden slab - top = side = terrain_images[4] - elif data == 3: # cobblestone slab - top = side = terrain_images[16] - elif data == 4: # brick? - top = side = terrain_images[7] - elif data == 5: # stone brick? - top = side = terrain_images[54] - - img = _build_block(top, side, blockID) - return generate_texture_tuple(img, blockID) - - - if blockID in (50,75,76): # torch, off redstone torch, on redstone torch - - # choose the proper texture - if blockID == 50: # torch - small = terrain_images[80] - elif blockID == 75: # off redstone torch - small = terrain_images[115] - else: # on redstone torch - small = terrain_images[99] - - # compose a torch bigger than the normal - # (better for doing transformations) - torch = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(torch,small,(-4,-3)) - composite.alpha_over(torch,small,(-5,-2)) - composite.alpha_over(torch,small,(-3,-2)) - - # angle of inclination of the texture - rotation = 15 - - if data == 1: # pointing south - torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. - img = _build_full_block(None, None, None, torch, None, None, blockID) - - elif data == 2: # pointing north - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, None, torch, None, None, None, blockID) - - elif data == 3: # pointing west - torch = torch.rotate(rotation, Image.NEAREST) - img = _build_full_block(None, torch, None, None, None, None, blockID) - - elif data == 4: # pointing east - torch = torch.rotate(-rotation, Image.NEAREST) - img = _build_full_block(None, None, None, None, torch, None, blockID) - - elif data == 5: # standing on the floor - # compose a "3d torch". - img = Image.new("RGBA", (24,24), bgcolor) - - small_crop = small.crop((2,2,14,14)) - slice = small_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(img, slice, (7,5)) - composite.alpha_over(img, small_crop, (6,6)) - composite.alpha_over(img, small_crop, (7,6)) - composite.alpha_over(img, slice, (7,7)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 51: # fire - firetexture = _load_image("fire.png") - side1 = transform_image_side(firetexture) - side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - - composite.alpha_over(img, side1, (12,0), side1) - composite.alpha_over(img, side2, (0,0), side2) - - composite.alpha_over(img, side1, (0,6), side1) - composite.alpha_over(img, side2, (12,6), side2) - - return generate_texture_tuple(img, blockID) - - - if blockID in (53,67, 108, 109): # wooden, stone brick, and cobblestone stairs. - - if blockID == 53: # wooden - texture = terrain_images[4] - elif blockID == 67: # cobblestone - texture = terrain_images[16] - elif blockID == 108: # red brick stairs - texture = terrain_images[7] - elif blockID == 109: # stone brick stairs - texture = terrain_images[54] - - side = texture.copy() - half_block_u = texture.copy() # up, down, left, right - half_block_d = texture.copy() - half_block_l = texture.copy() - half_block_r = texture.copy() - - # generate needed geometries - ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - if data == 0: # ascending south - img = _build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) - tmp1 = transform_image_side(half_block_u) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_l) - composite.alpha_over(img, tmp2, (0,6)) - - elif data == 1: # ascending north - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_r) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_l, None, None, texture, side) - composite.alpha_over(img, tmp2) - - elif data == 2: # ascending west - img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back - tmp1 = transform_image(half_block_u) - composite.alpha_over(img, tmp1, (0,6)) - tmp2 = _build_full_block(half_block_d, None, None, side, texture) - composite.alpha_over(img, tmp2) - - elif data == 3: # ascending east - img = _build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) - tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the vertical part of the second step - sidealpha = tmp1.split()[3] - # darken it a bit more than usual, looks better - tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) - tmp1.putalpha(sidealpha) - - composite.alpha_over(img, tmp1, (6,3)) - tmp2 = transform_image(half_block_d) - composite.alpha_over(img, tmp2, (0,6)) - - # touch up a (horrible) pixel - img.putpixel((18,3),(0,0,0,0)) - - return generate_texture_tuple(img, blockID) - - if blockID == 54: # chests - # First to bits of the pseudo data store if it's a single chest - # or it's a double chest, first half or second half. - # The to last bits store the orientation. - - top = terrain_images[25] - side = terrain_images[26] - - if data & 12 == 0: # single chest - front = terrain_images[27] - back = terrain_images[26] - - elif data & 12 == 4: # double, first half - front = terrain_images[41] - back = terrain_images[57] - - elif data & 12 == 8: # double, second half - front = terrain_images[42] - back = terrain_images[58] - - else: # just in case - front = terrain_images[25] - side = terrain_images[25] - back = terrain_images[25] - - if data & 3 == 0: # facing west - img = _build_full_block(top, None, None, side, front) - - elif data & 3 == 1: # north - img = _build_full_block(top, None, None, front, side) - - elif data & 3 == 2: # east - img = _build_full_block(top, None, None, side, back) - - elif data & 3 == 3: # south - img = _build_full_block(top, None, None, back, side) - - else: - img = _build_full_block(top, None, None, back, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 55: # redstone wire - - if data & 0b1000000 == 64: # powered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) - - - else: # unpowered redstone wire - redstone_wire_t = terrain_images[165] - redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) - - redstone_cross_t = terrain_images[164] - redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) - - # generate an image per redstone direction - branch_top_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_top_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_right = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - branch_bottom_left = redstone_cross_t.copy() - ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # generate the bottom texture - if data & 0b111111 == 0: - bottom = redstone_cross_t.copy() - - elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction - bottom = redstone_wire_t.copy() - - elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction - bottom = redstone_wire_t.copy().rotate(90) - - else: - bottom = Image.new("RGBA", (16,16), bgcolor) - if (data & 0b0001) == 1: - composite.alpha_over(bottom,branch_top_left) - - if (data & 0b1000) == 8: - composite.alpha_over(bottom,branch_top_right) - - if (data & 0b0010) == 2: - composite.alpha_over(bottom,branch_bottom_left) - - if (data & 0b0100) == 4: - composite.alpha_over(bottom,branch_bottom_right) - - # check for going up redstone wire - if data & 0b100000 == 32: - side1 = redstone_wire_t.rotate(90) - else: - side1 = None - - if data & 0b010000 == 16: - side2 = redstone_wire_t.rotate(90) - else: - side2 = None - - img = _build_full_block(None,side1,side2,None,None,bottom) - - return generate_texture_tuple(img, blockID) - - - if blockID == 58: # crafting table - top = terrain_images[43] - side3 = terrain_images[43+16] - side4 = terrain_images[43+16+1] - - img = _build_full_block(top, None, None, side3, side4, None, 58) - return generate_texture_tuple(img, blockID) - - - if blockID == 59: # crops - raw_crop = terrain_images[88+data] - crop1 = transform_image(raw_crop, blockID) - crop2 = transform_image_side(raw_crop, blockID) - crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) - - img = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(img, crop1, (0,12), crop1) - composite.alpha_over(img, crop2, (6,3), crop2) - composite.alpha_over(img, crop3, (6,3), crop3) - return generate_texture_tuple(img, blockID) - - - if blockID in (61, 62, 23): #furnace and burning furnace - top = terrain_images[62] - side = terrain_images[45] - - if blockID == 61: - front = terrain_images[44] - - elif blockID == 62: - front = terrain_images[45+16] - - elif blockID == 23: - front = terrain_images[46] - - if data == 3: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 4: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 63: # singposts - - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # If the signpost is looking directly to the image, draw some - # random dots, they will look as text. - if data in (0,1,2,3,4,5,15): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - - # Minecraft uses wood texture for the signpost stick - texture_stick = terrain_images[20] - texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) - ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - img = Image.new("RGBA", (24,24), bgcolor) - - # W N ~90 E S ~270 - angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) - angle = math.radians(angles[data]) - post = transform_image_angle(texture, angle) - - # choose the position of the "3D effect" - incrementx = 0 - if data in (1,6,7,8,9,14): - incrementx = -1 - elif data in (3,4,5,11,12,13): - incrementx = +1 - - composite.alpha_over(img, texture_stick,(11, 8),texture_stick) - # post2 is a brighter signpost pasted with a small sift, - # gives to the signpost some 3D effect. - post2 = ImageEnhance.Brightness(post).enhance(1.2) - composite.alpha_over(img, post2,(incrementx, -3),post2) - composite.alpha_over(img, post, (0,-2), post) - - return generate_texture_tuple(img, blockID) - - - if blockID in (64,71): #wooden door, or iron door - if data & 0x8 == 0x8: # top of the door - raw_door = terrain_images[81 if blockID == 64 else 82] - else: # bottom of the door - raw_door = terrain_images[97 if blockID == 64 else 98] - - # if you want to render all doors as closed, then force - # force swung to be False - if data & 0x4 == 0x4: - swung=True - else: - swung=False - - # mask out the high bits to figure out the orientation - img = Image.new("RGBA", (24,24), bgcolor) - if (data & 0x03) == 0: # northeast corner - if not swung: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (0,6), tex) - else: - # flip first to set the doornob on the correct side - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - tex = tex.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - - if (data & 0x03) == 1: # southeast corner - if not swung: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - else: - tex = transform_image_side(raw_door) - composite.alpha_over(img, tex, (12,0), tex) - - if (data & 0x03) == 2: # southwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (12,0), tex) - else: - tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - - if (data & 0x03) == 3: # northwest corner - if not swung: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - else: - tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) - composite.alpha_over(img, tex, (0,6), tex) - - return generate_texture_tuple(img, blockID) - - - if blockID == 65: # ladder - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[83] - #print "ladder is facing: %d" % data - if data == 5: - # normally this ladder would be obsured by the block it's attached to - # but since ladders can apparently be placed on transparent blocks, we - # have to render this thing anyway. same for data == 2 - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 2: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 3: - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 4: - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - - if blockID in (27, 28, 66): # minetrack: - img = Image.new("RGBA", (24,24), bgcolor) - - if blockID == 27: # powered rail - if data & 0x8 == 0: # unpowered - raw_straight = terrain_images[163] - raw_corner = terrain_images[112] # they don't exist but make the code - # much simplier - elif data & 0x8 == 0x8: # powered - raw_straight = terrain_images[179] - raw_corner = terrain_images[112] # leave corners for code simplicity - # filter the 'powered' bit - data = data & 0x7 - - elif blockID == 28: # detector rail - raw_straight = terrain_images[195] - raw_corner = terrain_images[112] # leave corners for code simplicity - - elif blockID == 66: # normal rail - raw_straight = terrain_images[128] - raw_corner = terrain_images[112] - - ## use transform_image to scale and shear - if data == 0: - track = transform_image(raw_straight, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 6: - track = transform_image(raw_corner, blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 7: - track = transform_image(raw_corner.rotate(270), blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 8: - # flip - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 9: - track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM), - blockID) - composite.alpha_over(img, track, (0,12), track) - elif data == 1: - track = transform_image(raw_straight.rotate(90), blockID) - composite.alpha_over(img, track, (0,12), track) - - #slopes - elif data == 2: # slope going up in +x direction - track = transform_image_slope(raw_straight,blockID) - track = track.transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, track, (2,0), track) - # the 2 pixels move is needed to fit with the adjacent tracks - - elif data == 3: # slope going up in -x direction - # tracks are sprites, in this case we are seeing the "side" of - # the sprite, so draw a line to make it beautiful. - ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) - # grey from track texture (exterior grey). - # the track doesn't start from image corners, be carefull drawing the line! - elif data == 4: # slope going up in -y direction - track = transform_image_slope(raw_straight,blockID) - composite.alpha_over(img, track, (0,0), track) - - elif data == 5: # slope going up in +y direction - # same as "data == 3" - ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) - - return generate_texture_tuple(img, blockID) - - - if blockID == 68: # wall sign - texture = terrain_images[4].copy() - # cut the planks to the size of a signpost - ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # draw some random black dots, they will look as text - """ don't draw text at the moment, they are used in blank for decoration - - if data in (3,4): - for i in range(15): - x = randint(4,11) - y = randint(3,7) - texture.putpixel((x,y),(0,0,0,255)) - """ - - img = Image.new("RGBA", (24,24), bgcolor) - - incrementx = 0 - if data == 2: # east - incrementx = +1 - sign = _build_full_block(None, None, None, None, texture) - elif data == 3: # west - incrementx = -1 - sign = _build_full_block(None, texture, None, None, None) - elif data == 4: # north - incrementx = +1 - sign = _build_full_block(None, None, texture, None, None) - elif data == 5: # south - incrementx = -1 - sign = _build_full_block(None, None, None, texture, None) - - sign2 = ImageEnhance.Brightness(sign).enhance(1.2) - composite.alpha_over(img, sign2,(incrementx, 2),sign2) - composite.alpha_over(img, sign, (0,3), sign) - - return generate_texture_tuple(img, blockID) - - if blockID == 70 or blockID == 72: # wooden and stone pressure plates - if blockID == 70: # stone - t = terrain_images[1].copy() - else: # wooden - t = terrain_images[4].copy() - - # cut out the outside border, pressure plates are smaller - # than a normal block - ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) - - # create the textures and a darker version to make a 3d by - # pasting them with an offstet of 1 pixel - img = Image.new("RGBA", (24,24), bgcolor) - - top = transform_image(t, blockID) - - alpha = top.split()[3] - topd = ImageEnhance.Brightness(top).enhance(0.8) - topd.putalpha(alpha) - - #show it 3d or 2d if unpressed or pressed - if data == 0: - composite.alpha_over(img,topd, (0,12),topd) - composite.alpha_over(img,top, (0,11),top) - elif data == 1: - composite.alpha_over(img,top, (0,12),top) - - return generate_texture_tuple(img, blockID) - - if blockID == 85: # fences - # create needed images for Big stick fence - - fence_top = terrain_images[4].copy() - fence_side = terrain_images[4].copy() - - # generate the textures of the fence - ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the big stick - fence_side = transform_image_side(fence_side,85) - fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) - fence_top = transform_image(fence_top,85) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_side.split()[3] - fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) - fence_side.putalpha(sidealpha) - othersidealpha = fence_other_side.split()[3] - fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) - fence_other_side.putalpha(othersidealpha) - - # Compose the fence big stick - fence_big = Image.new("RGBA", (24,24), bgcolor) - composite.alpha_over(fence_big,fence_side, (5,4),fence_side) - composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) - composite.alpha_over(fence_big,fence_top, (0,0),fence_top) - - # Now render the small sticks. - # Create needed images - fence_small_side = terrain_images[4].copy() - - # Generate mask - ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # Create the sides and the top of the small sticks - fence_small_side = transform_image_side(fence_small_side,85) - fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Darken the sides slightly. These methods also affect the alpha layer, - # so save them first (we don't want to "darken" the alpha layer making - # the block transparent) - sidealpha = fence_small_other_side.split()[3] - fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) - fence_small_other_side.putalpha(sidealpha) - sidealpha = fence_small_side.split()[3] - fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) - fence_small_side.putalpha(sidealpha) - - # Create img to compose the fence - img = Image.new("RGBA", (24,24), bgcolor) - - # Position of fence small sticks in img. - # These postitions are strange because the small sticks of the - # fence are at the very left and at the very right of the 16x16 images - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose small sticks in the back of the image, - # then big stick and thecn small sticks in the front. - - if (data & 0b0001) == 1: - composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left - if (data & 0b1000) == 8: - composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right - - composite.alpha_over(img,fence_big,(0,0),fence_big) - - if (data & 0b0010) == 2: - composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left - if (data & 0b0100) == 4: - composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right - - return generate_texture_tuple(img, blockID) - - - if blockID in (86,91): # pumpkins, jack-o-lantern - top = terrain_images[102] - frontID = 119 if blockID == 86 else 120 - front = terrain_images[frontID] - side = terrain_images[118] - - if data == 0: # pointing west - img = _build_full_block(top, None, None, side, front) - - elif data == 1: # pointing north - img = _build_full_block(top, None, None, front, side) - - else: # in any other direction the front can't be seen - img = _build_full_block(top, None, None, side, side) - - return generate_texture_tuple(img, blockID) - - - if blockID == 90: # portal - portaltexture = _load_image("portal.png") - img = Image.new("RGBA", (24,24), bgcolor) - - side = transform_image_side(portaltexture) - otherside = side.transpose(Image.FLIP_TOP_BOTTOM) - - if data in (1,4): - composite.alpha_over(img, side, (5,4), side) - - if data in (2,8): - composite.alpha_over(img, otherside, (5,4), otherside) - - return generate_texture_tuple(img, blockID) - - - if blockID == 92: # cake! (without bites, at the moment) - - top = terrain_images[121] - side = terrain_images[122] - top = transform_image(top, blockID) - side = transform_image_side(side, blockID) - 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), bgcolor) - - composite.alpha_over(img, side, (1,6), side) - composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole - composite.alpha_over(img, otherside, (12,6), otherside) - composite.alpha_over(img, top, (0,6), top) - - return generate_texture_tuple(img, blockID) - - - if blockID in (93, 94): # redstone repeaters (diodes), ON and OFF - # generate the diode - top = terrain_images[131] if blockID == 93 else terrain_images[147] - side = terrain_images[5] - increment = 13 - - if (data & 0x3) == 0: # pointing east - pass - - if (data & 0x3) == 1: # pointing south - top = top.rotate(270) - - if (data & 0x3) == 2: # pointing west - top = top.rotate(180) - - if (data & 0x3) == 3: # pointing north - top = top.rotate(90) - - img = _build_full_block( (top, increment), None, None, side, side) - - # compose a "3d" redstone torch - t = terrain_images[115].copy() if blockID == 93 else terrain_images[99].copy() - torch = Image.new("RGBA", (24,24), bgcolor) - - t_crop = t.crop((2,2,14,14)) - slice = t_crop.copy() - ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) - - composite.alpha_over(torch, slice, (6,4)) - composite.alpha_over(torch, t_crop, (5,5)) - composite.alpha_over(torch, t_crop, (6,5)) - composite.alpha_over(torch, slice, (6,6)) - - # paste redstone torches everywhere! - # the torch is too tall for the repeater, crop the bottom. - ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # touch up the 3d effect with big rectangles, just in case, for other texture packs - ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) - - # torch positions for every redstone torch orientation. - # - # This is a horrible list of torch orientations. I tried to - # obtain these orientations by rotating the positions for one - # orientation, but pixel rounding is horrible and messes the - # torches. - - if (data & 0x3) == 0: # pointing east - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,2) - static_torch = (-3,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,3) - static_torch = (-3,-1) - - elif (data & 0x3) == 1: # pointing south - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,-1) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,2) - static_torch = (5,-1) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,2) - static_torch = (5,-1) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,3) - static_torch = (5,-1) - - elif (data & 0x3) == 2: # pointing west - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (5,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (0,0) - static_torch = (5,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (-1,0) - static_torch = (5,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (-2,-1) - static_torch = (5,3) - - elif (data & 0x3) == 3: # pointing north - if (data & 0xC) == 0: # one tick delay - moving_torch = (1,1) - static_torch = (-3,3) - - elif (data & 0xC) == 4: # two ticks delay - moving_torch = (2,0) - static_torch = (-3,3) - - elif (data & 0xC) == 8: # three ticks delay - moving_torch = (3,0) - static_torch = (-3,3) - - elif (data & 0xC) == 12: # four ticks delay - moving_torch = (4,-1) - static_torch = (-3,3) - - # this paste order it's ok for east and south orientation - # but it's wrong for north and west orientations. But using the - # default texture pack the torches are small enough to no overlap. - composite.alpha_over(img, torch, static_torch, torch) - composite.alpha_over(img, torch, moving_torch, torch) - - return generate_texture_tuple(img, blockID) - - - if blockID == 96: # trapdoor - texture = terrain_images[84] - if data & 0x4 == 0x4: # opened trapdoor - if data & 0x3 == 0: # west - img = _build_full_block(None, None, None, None, texture) - if data & 0x3 == 1: # east - img = _build_full_block(None, texture, None, None, None) - if data & 0x3 == 2: # south - img = _build_full_block(None, None, texture, None, None) - if data & 0x3 == 3: # north - img = _build_full_block(None, None, None, texture, None) - - elif data & 0x4 == 0: # closed trapdoor - img = _build_full_block((texture, 12), None, None, texture, texture) - - return generate_texture_tuple(img, blockID) - - if blockID == 98: # normal, mossy and cracked stone brick - if data == 0: # normal - t = terrain_images[54] - elif data == 1: # mossy - t = terrain_images[100] - else: # cracked - t = terrain_images[101] - - img = _build_full_block(t, None, None, t, t) - - return generate_texture_tuple(img, blockID) - - if blockID == 99 or blockID == 100: # huge brown and red mushroom - if blockID == 99: # brown - cap = terrain_images[126] - else: # red - cap = terrain_images[125] - stem = terrain_images[141] - porous = terrain_images[142] - - if data == 0: # fleshy piece - img = _build_full_block(porous, None, None, porous, porous) - - if data == 1: # north-east corner - img = _build_full_block(cap, None, None, cap, porous) - - if data == 2: # east side - img = _build_full_block(cap, None, None, porous, porous) - - if data == 3: # south-east corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 4: # north side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 5: # top piece - img = _build_full_block(cap, None, None, porous, porous) - - if data == 6: # south side - img = _build_full_block(cap, None, None, cap, porous) - - if data == 7: # north-west corner - img = _build_full_block(cap, None, None, cap, cap) - - if data == 8: # west side - img = _build_full_block(cap, None, None, porous, cap) - - if data == 9: # south-west corner - img = _build_full_block(cap, None, None, porous, cap) - - if data == 10: # stem - img = _build_full_block(porous, None, None, stem, stem) - - return generate_texture_tuple(img, blockID) - - if blockID == 101 or blockID == 102: # iron bars and glass panes - if blockID == 101: - # iron bars - t = terrain_images[85] - else: - # glass panes - t = terrain_images[49] - left = t.copy() - right = t.copy() - - # generate the four small pieces of the glass pane - ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) - ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - up_left = transform_image_side(left) - up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) - dw_right = transform_image_side(right) - dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) - - # Create img to compose the texture - img = Image.new("RGBA", (24,24), bgcolor) - - # +x axis points top right direction - # +y axis points bottom right direction - # First compose things in the back of the image, - # then things in the front. - - if (data & 0b0001) == 1 or data == 0: - composite.alpha_over(img,up_left, (6,3),up_left) # top left - if (data & 0b1000) == 8 or data == 0: - composite.alpha_over(img,up_right, (6,3),up_right) # top right - if (data & 0b0010) == 2 or data == 0: - composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left - if (data & 0b0100) == 4 or data == 0: - composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right - - return generate_texture_tuple(img, blockID) - - if blockID == 104 or blockID == 105: # pumpkin and melon stems. - # the ancildata value indicates how much of the texture - # is shown. - if data & 48 == 0: - # not fully grown stem or no pumpkin/melon touching it, - # straight up stem - t = terrain_images[111].copy() - img = Image.new("RGBA", (16,16), bgcolor) - composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) - img = _build_block(img, img, blockID) - if data & 7 == 7: - # fully grown stem gets brown color! - # there is a conditional in rendermode-normal to not - # tint the data value 7 - img = tintTexture(img, (211,169,116)) - return generate_texture_tuple(img, blockID) - - else: # fully grown, and a pumpking/melon touching it, - # corner stem - pass - - - if blockID == 106: # vine - img = Image.new("RGBA", (24,24), bgcolor) - raw_texture = terrain_images[143] - # print "vine is facing: %d" % data - if data == 2: # south - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (0,6), tex) - return generate_texture_tuple(img, blockID) - if data == 1: # east - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (12,6), tex) - return generate_texture_tuple(img, blockID) - if data == 4: # west - tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - composite.alpha_over(img, tex, (0,0), tex) - return generate_texture_tuple(img, blockID) - if data == 8: # north - tex = transform_image_side(raw_texture) - composite.alpha_over(img, tex, (12,0), tex) - return generate_texture_tuple(img, blockID) - - if blockID == 107: - # create the closed gate side - gate_side = terrain_images[4].copy() - gate_side_draw = ImageDraw.Draw(gate_side) - gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) - gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - - # darken the sides slightly, as with the fences - sidealpha = gate_side.split()[3] - gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) - gate_side.putalpha(sidealpha) - - # create the other sides - mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT), blockID) - gate_side = transform_image_side(gate_side, blockID) - gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) - mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) - - # Create img to compose the fence gate - img = Image.new("RGBA", (24,24), bgcolor) - - if data & 0x4: - # opened - data = data & 0x3 - if data == 0: - composite.alpha_over(img, gate_side, (2,8), gate_side) - composite.alpha_over(img, gate_side, (13,3), gate_side) - elif data == 1: - composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) - composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) - elif data == 2: - composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) - composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) - elif data == 3: - composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) - else: - # closed - - # positions for pasting the fence sides, as with fences - pos_top_left = (2,3) - pos_top_right = (10,3) - pos_bottom_right = (10,7) - pos_bottom_left = (2,7) - - if data == 0 or data == 2: - composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) - composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) - elif data == 1 or data == 3: - composite.alpha_over(img, gate_side, pos_top_left, gate_side) - composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) - - return generate_texture_tuple(img, blockID) - - return None - -def convert_data(blockID, data): - if blockID == 26: # bed - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID in (29, 33, 34): # sticky piston, piston, piston extension - #Masked to not clobber block head/foot info - if _north == 'upper-left': - if (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID in (27, 28, 66): # minetrack: - #Masked to not clobber powered rail on/off info - #Ascending and flat straight - if _north == 'upper-left': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 5 - elif (data & 0b0111) == 3: data = data & 0b1000 | 4 - elif (data & 0b0111) == 4: data = data & 0b1000 | 2 - elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif _north == 'upper-right': - if (data & 0b0111) == 2: data = data & 0b1000 | 3 - elif (data & 0b0111) == 3: data = data & 0b1000 | 2 - elif (data & 0b0111) == 4: data = data & 0b1000 | 5 - elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif _north == 'lower-right': - if (data & 0b0111) == 0: data = data & 0b1000 | 1 - elif (data & 0b0111) == 1: data = data & 0b1000 | 0 - elif (data & 0b0111) == 2: data = data & 0b1000 | 4 - elif (data & 0b0111) == 3: data = data & 0b1000 | 5 - elif (data & 0b0111) == 4: data = data & 0b1000 | 3 - elif (data & 0b0111) == 5: data = data & 0b1000 | 2 - if blockID == 66: # normal minetrack only - #Corners - if _north == 'upper-left': - if data == 6: data = 7 - elif data == 7: data = 8 - elif data == 8: data = 6 - elif data == 9: data = 9 - elif _north == 'upper-right': - if data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 7 - elif _north == 'lower-right': - if data == 6: data = 9 - elif data == 7: data = 6 - elif data == 8: data = 8 - elif data == 9: data = 7 - if blockID in (50, 75, 76): # torch, off/on redstone torch - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 4 - elif data == 3: data = 2 - elif data == 4: data = 1 - elif _north == 'upper-right': - if data == 1: data = 2 - elif data == 2: data = 1 - elif data == 3: data = 4 - elif data == 4: data = 3 - elif _north == 'lower-right': - if data == 1: data = 4 - elif data == 2: data = 3 - elif data == 3: data = 1 - elif data == 4: data = 2 - if blockID in (53,67,108,109): # wooden and cobblestone stairs. - if _north == 'upper-left': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 1 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 1 - elif data == 1: data = 0 - elif data == 2: data = 3 - elif data == 3: data = 2 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 2 - elif data == 2: data = 0 - elif data == 3: data = 1 - if blockID in (61, 62, 23): # furnace and burning furnace - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 63: # signposts - if _north == 'upper-left': - data = (data + 4) % 16 - elif _north == 'upper-right': - data = (data + 8) % 16 - elif _north == 'lower-right': - data = (data + 12) % 16 - if blockID in (64,71): # wooden/iron door - #Masked to not clobber block top/bottom & swung info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 65: # ladder - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID == 68: # wall sign - if _north == 'upper-left': - if data == 2: data = 5 - elif data == 3: data = 4 - elif data == 4: data = 2 - elif data == 5: data = 3 - elif _north == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - elif _north == 'lower-right': - if data == 2: data = 4 - elif data == 3: data = 5 - elif data == 4: data = 3 - elif data == 5: data = 2 - if blockID in (86,91): # pumpkins, jack-o-lantern - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if blockID in (93, 94): # redstone repeaters, ON and OFF - #Masked to not clobber delay info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - if blockID == 96: # trapdoor - #Masked to not clobber opened/closed info - if _north == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 - elif (data & 0b0011) == 2: data = data & 0b1100 | 0 - elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif _north == 'upper-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 1 - elif (data & 0b0011) == 1: data = data & 0b1100 | 0 - elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - elif _north == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 - elif (data & 0b0011) == 2: data = data & 0b1100 | 1 - elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - if blockID == 99 or blockID == 100: # huge red and brown mushroom - if _north == 'upper-left': - if data == 1: data = 3 - elif data == 2: data = 6 - elif data == 3: data = 9 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 1 - elif data == 8: data = 4 - elif data == 9: data = 7 - elif _north == 'upper-right': - if data == 1: data = 9 - elif data == 2: data = 8 - elif data == 3: data = 7 - elif data == 4: data = 6 - elif data == 6: data = 4 - elif data == 7: data = 3 - elif data == 8: data = 2 - elif data == 9: data = 1 - elif _north == 'lower-right': - if data == 1: data = 7 - elif data == 2: data = 4 - elif data == 3: data = 1 - elif data == 4: data = 2 - elif data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 3 - if blockID == 106: # vine - if _north == 'upper-left': - if data == 1: data = 2 - elif data == 4: data = 8 - elif data == 8: data = 1 - elif data == 2: data = 4 - elif _north == 'upper-right': - if data == 1: data = 4 - elif data == 4: data = 1 - elif data == 8: data = 2 - elif data == 2: data = 8 - elif _north == 'lower-right': - if data == 1: data = 8 - elif data == 4: data = 2 - elif data == 8: data = 4 - elif data == 2: data = 1 - if blockID == 107: # fence gates - opened = False - if data & 0x4: - data = data & 0x3 - opened = True - if _north == 'upper-left': - if data == 0: data = 1 - elif data == 1: data = 2 - elif data == 2: data = 3 - elif data == 3: data = 0 - elif _north == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 - elif _north == 'lower-right': - if data == 0: data = 3 - elif data == 1: data = 0 - elif data == 2: data = 1 - elif data == 3: data = 2 - if opened: - data = data | 0x4 - - return data - def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA return i +def generate_texture_tuple(img): + """ This takes an image and returns the needed tuple for the + blockmap array.""" + if img is None: + return None + return (img, generate_opaque_mask(img)) + +## +## Biomes +## + currentBiomeFile = None currentBiomeData = None grasscolor = None foliagecolor = None watercolor = None +_north = None def prepareBiomeData(worlddir): global grasscolor, foliagecolor, watercolor @@ -2316,6 +512,7 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + global _north biomeX = chunkX // 32 biomeY = chunkY // 32 rots = 0 @@ -2355,6 +552,10 @@ def getBiomeData(worlddir, chunkX, chunkY): currentBiomeData = data return data +## +## Color Light +## + lightcolor = None lightcolor_checked = False def loadLightColor(): @@ -2369,143 +570,2351 @@ def loadLightColor(): lightcolor = None return lightcolor -# This set holds block ids that require special pre-computing. These are typically -# things that require ancillary data to render properly (i.e. ladder plus orientation) -# A good source of information is: -# http://www.minecraftwiki.net/wiki/Data_values -# (when adding new blocks here and in generate_special_textures, -# please, if possible, keep the ascending order of blockid value) - -special_blocks = set([ 2, 6, 9, 17, 18, 20, 26, 23, 27, 28, 29, 31, 33, - 34, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, - 63, 64, 65, 66, 67, 68, 70, 71, 72, 75, 76, 79, 85, - 86, 90, 91, 92, 93, 94, 96, 98, 99, 100, 101, 102, - 104, 105, 106, 107, 108, 109]) - -# this is a map of special blockIDs to a list of all -# possible values for ancillary data that it might have. - -special_map = {} - -# 0x10 means SNOW sides -special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is - # used in the mod WildGrass, and this - # small fix shows the map as expected, - # and is harmless for normal maps -special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings) -special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data -special_map[17] = range(3) # wood: normal, birch and pine -special_map[18] = range(16) # leaves, birch, normal or pine leaves -special_map[20] = range(32) # glass, used to only render the exterior surface, uses pseudo data -special_map[26] = range(12) # bed, orientation -special_map[23] = range(6) # dispensers, orientation -special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered -special_map[28] = range(6) # detector rail, orientation/slope -special_map[29] = (0,1,2,3,4,5,8,9,10,11,12,13) # sticky piston body, orientation, pushed in/out -special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself -special_map[33] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal piston body, orientation, pushed in/out -special_map[34] = (0,1,2,3,4,5,8,9,10,11,12,13) # normal and sticky piston extension, orientation, sticky/normal -special_map[35] = range(16) # wool, colored and white -special_map[43] = range(6) # stone, sandstone, wooden and cobblestone double-slab -special_map[44] = range(6) # stone, sandstone, wooden and cobblestone slab -special_map[50] = (1,2,3,4,5) # torch, position in the block -special_map[51] = range(16) # fire, position in the block (not implemented) -special_map[53] = range(4) # wooden stairs, orientation -special_map[54] = range(12) # chests, orientation and type (single or double), uses pseudo data -special_map[55] = range(128) # redstone wire, all the possible combinations, uses pseudo data -special_map[58] = (0,) # crafting table, it has 2 different sides -special_map[59] = range(8) # crops, grow from 0 to 7 -special_map[61] = range(6) # furnace, orientation -special_map[62] = range(6) # burning furnace, orientation -special_map[63] = range(16) # signpost, orientation -special_map[64] = range(16) # wooden door, open/close and orientation -special_map[65] = (2,3,4,5) # ladder, orientation -special_map[66] = range(10) # minecrart tracks, orientation, slope -special_map[67] = range(4) # cobblestone stairs, orientation -special_map[68] = (2,3,4,5) # wall sing, orientation -special_map[70] = (0,1) # stone pressure plate, non pressed and pressed -special_map[71] = range(16) # iron door, open/close and orientation -special_map[72] = (0,1) # wooden pressure plate, non pressed and pressed -special_map[75] = (1,2,3,4,5) # off redstone torch, orientation -special_map[76] = (1,2,3,4,5) # on redstone torch, orientation -special_map[79] = range(32) # ice, used to only render the exterior surface, uses pseudo data -special_map[85] = range(17) # fences, all the possible combination, uses pseudo data -special_map[86] = range(5) # pumpkin, orientation -special_map[90] = (1,2,4,8) # portal, in 2 orientations, 4 cases, uses pseudo data -special_map[91] = range(5) # jack-o-lantern, orientation -special_map[92] = range(6) # cake, eaten amount, (not implemented) -special_map[93] = range(16) # OFF redstone repeater, orientation and delay -special_map[94] = range(16) # ON redstone repeater, orientation and delay -special_map[96] = range(8) # trapdoor, open, closed, orientation -special_map[98] = range(3) # stone brick, normal, mossy and cracked -special_map[99] = range(11) # huge brown mushroom, side, corner, etc, piece -special_map[100] = range(11) # huge red mushroom, side, corner, etc, piece -special_map[101]= range(16) # iron bars, all the possible combination, uses pseudo data -special_map[102]= range(16) # glass panes, all the possible combination, uses pseudo data -special_map[104] = range(8) # pumpkin stem, size of the stem -special_map[105] = range(8) # melon stem, size of the stem -special_map[106] = (1,2,4,8) # vine, orientation -special_map[107] = range(8) # fence gates, orientation + open bit -special_map[108]= range(4) # red stairs, orientation -special_map[109]= range(4) # stonebrick stairs, orientation +## +## The big one: generate() and associated framework +## # placeholders that are generated in generate() -bgcolor = None -terrain_images = None -blockmap = None +texture_dimensions = None +blockmap_generators = {} +blockmap = [] biome_grass_texture = None -specialblockmap = None + +known_blocks = set() +used_datas = set() +max_blockid = 0 +max_data = 0 + +transparent_blocks = set() +solid_blocks = set() +fluid_blocks = set() +nospawn_blocks = set() + +# 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} + + # make sure blockid and data are iterable + try: + iter(blockid) + except: + blockid = [blockid,] + try: + iter(data) + except: + data = [data,] + + def inner_material(func): + global blockmap_generators + + # create a wrapper function with a known signature + @functools.wraps(func) + def func_wrapper(blockid, data, north): + try: + return func(blockid, data, north) + except TypeError: + return func(blockid, data) + + used_datas.update(data) + for block in blockid: + # set the property sets appropriately + known_blocks.update([block]) + for prop in properties: + try: + if block in kwargs.get(prop, []): + properties[prop].update([block]) + 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 + + return func_wrapper + return inner_material + +# shortcut function for pure blocks, default to solid +def block(blockid=[], top_index=None, side_index=None, **kwargs): + new_kwargs = {'solid' : True} + new_kwargs.update(kwargs) + + if top_index is None: + raise ValueError("top_index was not provided") + + if side_index is None: + side_index = top_index + + @material(blockid=blockid, **new_kwargs) + def inner_block(unused_id, unused_data): + return build_block(terrain_images[top_index], terrain_images[side_index]) + return inner_block + +# shortcut function for sprite blocks, defaults to transparent +def sprite(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_sprite(unused_id, unused_data): + return build_sprite(terrain_images[index]) + return inner_sprite + +# shortcut function for billboard blocks, defaults to transparent +def billboard(blockid=[], index=None, **kwargs): + new_kwargs = {'transparent' : True} + new_kwargs.update(kwargs) + + if index is None: + raise ValueError("index was not provided") + + @material(blockid=blockid, **new_kwargs) + def inner_billboard(unused_id, unused_data): + return build_billboard(terrain_images[index]) + return inner_billboard def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower-left'): - global _north - _north = north_direction global _find_file_local_path global bgcolor + global texture_dimensions + global _north bgcolor = bgc - global _find_file_local_path, texture_dimensions _find_file_local_path = path + _north = north_direction texture_dimensions = (texture_size, texture_size) # This maps terainids to 16x16 images global terrain_images - terrain_images = _split_terrain(_get_terrain_image()) - - # generate the normal blocks - global blockmap - blockmap = _build_blockimages() - load_water() + terrain_images = _split_terrain(_load_image("terrain.png")) # generate biome grass mask global biome_grass_texture - biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) - - # generate the special blocks - global specialblockmap, special_blocks - specialblockmap = {} - for blockID in special_blocks: - for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) + biome_grass_texture = build_block(terrain_images[0], terrain_images[38]) + # generate the blocks + global blockmap, blockmap_generators + global max_blockid, max_data + max_blockid = max(known_blocks) + 1 + max_data = max(used_datas) + 1 + blockmap = [None] * max_blockid * max_data + + for blockid, data in blockmap_generators: + texgen = blockmap_generators[(blockid, data)] + tex = texgen(blockid, data, north_direction) + blockmap[blockid * max_data + data] = generate_texture_tuple(tex) + if texture_size != 24: # rescale biome textures. biome_grass_texture = biome_grass_texture.resize(texture_dimensions, Image.ANTIALIAS) - # rescale the normal block images - for i in range(len(blockmap)): - if blockmap[i] != None: - block = blockmap[i] - alpha = block[1] - block = block[0] - block.putalpha(alpha) - scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - blockmap[i] = generate_texture_tuple(scaled_block, i) - # rescale the special block images - for blockid, data in iter(specialblockmap): - block = specialblockmap[(blockid,data)] - if block != None: - alpha = block[1] - block = block[0] - block.putalpha(alpha) + for i, tex in enumerate(blockmap): + if tex != None: + block = tex[0] scaled_block = block.resize(texture_dimensions, Image.ANTIALIAS) - specialblockmap[(blockid,data)] = generate_texture_tuple(scaled_block, blockid) + blockmap[i] = generate_texture_tuple(scaled_block) + +## +## and finally: actual texture definitions +## + +# stone +block(blockid=1, top_index=1) + +@material(blockid=2, data=range(11)+[0x10,], solid=True) +def grass(blockid, data): + # 0x10 bit means SNOW + side_img = terrain_images[3] + if data & 0x10: + side_img = terrain_images[68] + img = build_block(terrain_images[0], side_img) + if not data & 0x10: + global biome_grass_texture + composite.alpha_over(img, biome_grass_texture, (0, 0), biome_grass_texture) + return img + +# dirt +block(blockid=3, top_index=2) +# cobblestone +block(blockid=4, top_index=16) +# wooden plank +block(blockid=5, top_index=4) + +@material(blockid=6, data=range(16), transparent=True) +def saplings(blockid, data): + # usual saplings + tex = terrain_images[15] + + if data & 0x3 == 1: # spruce sapling + tex = terrain_images[63] + if data & 0x3 == 2: # birch sapling + tex = terrain_images[79] + + return build_sprite(tex) + +# bedrock +block(blockid=7, top_index=17) + +@material(blockid=8, data=range(16), fluid=True, transparent=True, nospawn=True) +def water(blockid, data): + watertex = _load_image("water.png") + return build_block(watertex, watertex) + +# other water, glass, and ice (no inner surfaces) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[9, 20, 79], data=range(32), fluid=(9,), transparent=True, nospawn=True, solid=(79, 20)) +def no_inner_surfaces(blockid, data): + if blockid == 9: + texture = _load_image("water.png") + elif blockid == 20: + texture = terrain_images[49] + else: + texture = terrain_images[67] + + if (data & 0b10000) == 16: + 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: + side3 = None + + if (data & 0b0100) == 4: + side4 = texture # bottom right + else: + side4 = None + + # if nothing shown do not draw at all + if top is None and side3 is None and side4 is None: + return None + + img = build_full_block(top,None,None,side3,side4) + return img + +@material(blockid=[10, 11], data=range(16), fluid=True, transparent=False, nospawn=True) +def lava(blockid, data): + lavatex = _load_image("lava.png") + return build_block(lavatex, lavatex) + +# sand +block(blockid=12, top_index=18) +# gravel +block(blockid=13, top_index=19) +# gold ore +block(blockid=14, top_index=32) +# iron ore +block(blockid=15, top_index=33) +# coal ore +block(blockid=16, top_index=34) + +@material(blockid=17, data=range(3), solid=True) +def wood(blockid, data): + top = terrain_images[21] + if data == 0: # normal + return build_block(top, terrain_images[20]) + if data == 1: # birch + return build_block(top, terrain_images[116]) + if data == 2: # pine + return build_block(top, terrain_images[117]) + +@material(blockid=18, data=range(16), transparent=True, solid=True) +def leaves(blockid, data): + t = terrain_images[52] + if data == 1: + # pine! + t = terrain_images[132] + return build_block(t, t) + +# sponge +block(blockid=19, top_index=48) +# lapis lazuli ore +block(blockid=21, top_index=160) +# lapis lazuli block +block(blockid=22, top_index=144) + +# dispensers, furnaces, and burning furnaces +@material(blockid=[23, 61, 62], data=range(6), solid=True) +def furnaces(blockid, data, north): + # first, do the north rotation if needed + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + top = terrain_images[62] + side = terrain_images[45] + + if blockid == 61: + front = terrain_images[44] + elif blockid == 62: + front = terrain_images[61] + elif blockid == 23: + front = terrain_images[46] + + if data == 3: # pointing west + return build_full_block(top, None, None, side, front) + elif data == 4: # pointing north + return build_full_block(top, None, None, front, side) + else: # in any other direction the front can't be seen + return build_full_block(top, None, None, side, side) + +# sandstone +block(blockid=24, top_index=176, side_index=192) +# note block +block(blockid=25, top_index=74) + +@material(blockid=26, data=range(12), transparent=True, nospawn=True) +def bed(blockid, data, north): + # first get north rotation done + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + increment = 8 + left_face = None + right_face = None + if data & 0x8 == 0x8: # head of the bed + top = terrain_images[135] + if data & 0x00 == 0x00: # head pointing to West + top = top.copy().rotate(270) + left_face = terrain_images[151] + right_face = terrain_images[152] + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = terrain_images[152] + right_face = terrain_images[151] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + right_face = None + if data & 0x03 == 0x03: # South + right_face = None + right_face = terrain_images[151].transpose(Image.FLIP_LEFT_RIGHT) + + else: # foot of the bed + top = terrain_images[134] + if data & 0x00 == 0x00: # head pointing to West + top = top.rotate(270) + left_face = terrain_images[150] + right_face = None + if data & 0x01 == 0x01: # ... North + top = top.rotate(270) + left_face = None + right_face = terrain_images[150] + if data & 0x02 == 0x02: # East + top = top.rotate(180) + left_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + right_face = terrain_images[149].transpose(Image.FLIP_LEFT_RIGHT) + if data & 0x03 == 0x03: # South + left_face = terrain_images[149] + right_face = terrain_images[150].transpose(Image.FLIP_LEFT_RIGHT) + + top = (top, increment) + return build_full_block(top, None, None, left_face, right_face) + +# powered, detector, and normal rails +@material(blockid=[27, 28, 66], data=range(14), transparent=True) +def rails(blockid, data, north): + # first, do north rotation + # Masked to not clobber powered rail on/off info + # Ascending and flat straight + if north == 'upper-left': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + if blockid == 66: # normal minetrack only + #Corners + if north == 'upper-left': + if data == 6: data = 7 + elif data == 7: data = 8 + elif data == 8: data = 6 + elif data == 9: data = 9 + elif north == 'upper-right': + if data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + elif north == 'lower-right': + if data == 6: data = 9 + elif data == 7: data = 6 + elif data == 8: data = 8 + elif data == 9: data = 7 + img = Image.new("RGBA", (24,24), bgcolor) + + if blockid == 27: # powered rail + if data & 0x8 == 0: # unpowered + raw_straight = terrain_images[163] + raw_corner = terrain_images[112] # they don't exist but make the code + # much simplier + elif data & 0x8 == 0x8: # powered + raw_straight = terrain_images[179] + raw_corner = terrain_images[112] # leave corners for code simplicity + # filter the 'powered' bit + data = data & 0x7 + + elif blockid == 28: # detector rail + raw_straight = terrain_images[195] + raw_corner = terrain_images[112] # leave corners for code simplicity + + elif blockid == 66: # normal rail + raw_straight = terrain_images[128] + raw_corner = terrain_images[112] + + ## use transform_image to scale and shear + if data == 0: + track = transform_image_top(raw_straight) + composite.alpha_over(img, track, (0,12), track) + elif data == 6: + track = transform_image_top(raw_corner) + composite.alpha_over(img, track, (0,12), track) + elif data == 7: + track = transform_image_top(raw_corner.rotate(270)) + composite.alpha_over(img, track, (0,12), track) + elif data == 8: + # flip + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90)) + composite.alpha_over(img, track, (0,12), track) + elif data == 9: + track = transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM)) + composite.alpha_over(img, track, (0,12), track) + elif data == 1: + track = transform_image_top(raw_straight.rotate(90)) + composite.alpha_over(img, track, (0,12), track) + + #slopes + elif data == 2: # slope going up in +x direction + track = transform_image_slope(raw_straight) + track = track.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, track, (2,0), track) + # the 2 pixels move is needed to fit with the adjacent tracks + + elif data == 3: # slope going up in -x direction + # tracks are sprites, in this case we are seeing the "side" of + # the sprite, so draw a line to make it beautiful. + ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) + # grey from track texture (exterior grey). + # the track doesn't start from image corners, be carefull drawing the line! + elif data == 4: # slope going up in -y direction + track = transform_image_slope(raw_straight) + composite.alpha_over(img, track, (0,0), track) + + elif data == 5: # slope going up in +y direction + # same as "data == 3" + ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) + + return img + +# sticky and normal piston body +@material(blockid=[29, 33], data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, solid=True, nospawn=True) +def piston(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if blockid == 29: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[109].copy() + interior_t = terrain_images[110].copy() + + if data & 0x08 == 0x08: # pushed out, non full blocks, tricky stuff + # remove piston texture from piston body + ImageDraw.Draw(side_t).rectangle((0, 0,16,3),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x1: # up + img = build_full_block((interior_t, 4) ,None ,None ,side_t, side_t) + + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t , None, None ,side_t.rotate(90), back_t) + + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), None) + temp = transform_image_side(interior_t) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (9,5), temp) + + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , None, side_t.rotate(270)) + temp = transform_image_side(interior_t) + composite.alpha_over(img, temp, (3,5), temp) + + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None , None ,back_t, side_t.rotate(90)) + + else: # pushed in, normal full blocks, easy stuff + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block(back_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x1: # up + img = build_full_block(piston_t ,None ,None ,side_t, side_t) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), back_t) + elif data & 0x07 == 0x3: # west + img = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None ,piston_t, side_t.rotate(270)) + elif data & 0x07 == 0x5: # south + img = build_full_block(side_t.rotate(270) ,None ,None ,back_t, side_t.rotate(90)) + + return img + +# sticky and normal piston shaft +@material(blockid=34, data=[0,1,2,3,4,5,8,9,10,11,12,13], transparent=True, nospawn=True) +def piston_extension(blockid, data, north): + # first, north rotation + # Masked to not clobber block head/foot info + if north == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + + if (data & 0x8) == 0x8: # sticky + piston_t = terrain_images[106].copy() + else: # normal + piston_t = terrain_images[107].copy() + + # other textures + side_t = terrain_images[108].copy() + back_t = terrain_images[107].copy() + # crop piston body + ImageDraw.Draw(side_t).rectangle((0, 4,16,16),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the horizontal piston extension stick + h_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t) + composite.alpha_over(h_stick, temp, (1,7), temp) + temp = transform_image_top(side_t.rotate(90)) + composite.alpha_over(h_stick, temp, (1,1), temp) + # Darken it + sidealpha = h_stick.split()[3] + h_stick = ImageEnhance.Brightness(h_stick).enhance(0.85) + h_stick.putalpha(sidealpha) + + # generate the vertical piston extension stick + v_stick = Image.new("RGBA", (24,24), bgcolor) + temp = transform_image_side(side_t.rotate(90)) + composite.alpha_over(v_stick, temp, (12,6), temp) + temp = temp.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(v_stick, temp, (1,6), temp) + # Darken it + sidealpha = v_stick.split()[3] + v_stick = ImageEnhance.Brightness(v_stick).enhance(0.85) + v_stick.putalpha(sidealpha) + + # Piston orientation is stored in the 3 first bits + if data & 0x07 == 0x0: # down + side_t = side_t.rotate(180) + img = build_full_block((back_t, 12) ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,-3), v_stick) + elif data & 0x07 == 0x1: # up + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(piston_t ,None ,None ,side_t, side_t) + composite.alpha_over(img, v_stick, (0,4), v_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x2: # east + img = build_full_block(side_t ,None ,None ,side_t.rotate(90), None) + temp = transform_image_side(back_t).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, temp, (2,2), temp) + composite.alpha_over(img, h_stick, (6,3), h_stick) + elif data & 0x07 == 0x3: # west + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(180) ,None ,None ,side_t.rotate(270), piston_t) + composite.alpha_over(img, h_stick, (0,0), h_stick) + composite.alpha_over(img, img2, (0,0), img2) + elif data & 0x07 == 0x4: # north + img = build_full_block(side_t.rotate(90) ,None ,None , piston_t, side_t.rotate(270)) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (0,0), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + elif data & 0x07 == 0x5: # south + img = Image.new("RGBA", (24,24), bgcolor) + img2 = build_full_block(side_t.rotate(270) ,None ,None ,None, side_t.rotate(90)) + temp = transform_image_side(back_t) + composite.alpha_over(img2, temp, (10,2), temp) + composite.alpha_over(img, img2, (0,0), img2) + composite.alpha_over(img, h_stick.transpose(Image.FLIP_LEFT_RIGHT), (-3,2), h_stick.transpose(Image.FLIP_LEFT_RIGHT)) + + return img + +# cobweb +sprite(blockid=30, index=11, nospawn=True) + +@material(blockid=31, data=range(3), transparent=True) +def tall_grass(blockid, data): + if data == 0: # dead shrub + texture = terrain_images[55] + elif data == 1: # tall grass + texture = terrain_images[39] + elif data == 2: # fern + texture = terrain_images[56] + + return build_billboard(texture) + +# dead bush +billboard(blockid=32, index=55) + +@material(blockid=35, data=range(16), solid=True) +def wool(blockid, data): + if data == 0: # white + texture = terrain_images[64] + elif data == 1: # orange + texture = terrain_images[210] + elif data == 2: # magenta + texture = terrain_images[194] + elif data == 3: # light blue + texture = terrain_images[178] + elif data == 4: # yellow + texture = terrain_images[162] + elif data == 5: # light green + texture = terrain_images[146] + elif data == 6: # pink + texture = terrain_images[130] + elif data == 7: # grey + texture = terrain_images[114] + elif data == 8: # light grey + texture = terrain_images[225] + elif data == 9: # cyan + texture = terrain_images[209] + elif data == 10: # purple + texture = terrain_images[193] + elif data == 11: # blue + texture = terrain_images[177] + elif data == 12: # brown + texture = terrain_images[161] + elif data == 13: # dark green + texture = terrain_images[145] + elif data == 14: # red + texture = terrain_images[129] + elif data == 15: # black + texture = terrain_images[113] + + return build_block(texture, texture) + +# dandelion +sprite(blockid=37, index=13) +# rose +sprite(blockid=38, index=12) +# brown mushroom +sprite(blockid=39, index=29) +# red mushroom +sprite(blockid=40, index=28) +# block of gold +block(blockid=41, top_index=23) +# block of iron +block(blockid=42, top_index=22) + +# double slabs and slabs +@material(blockid=[43, 44], data=range(6), transparent=(44,), solid=True) +def slabs(blockid, data): + if data == 0: # stone slab + top = terrain_images[6] + side = terrain_images[5] + elif data == 1: # stone slab + top = terrain_images[176] + side = terrain_images[192] + elif data == 2: # wooden slab + top = side = terrain_images[4] + elif data == 3: # cobblestone slab + top = side = terrain_images[16] + elif data == 4: # brick? + top = side = terrain_images[7] + elif data == 5: # stone brick? + top = side = terrain_images[54] + + if blockid == 43: # double slab + return build_block(top, side) + + # cut the side texture in half + mask = side.crop((0,8,16,16)) + side = Image.new(side.mode, side.size, bgcolor) + composite.alpha_over(side, mask,(0,0,16,8), mask) + + # plain slab + top = transform_image_top(top) + side = 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), bgcolor) + composite.alpha_over(img, side, (0,12), side) + composite.alpha_over(img, otherside, (12,12), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# brick block +block(blockid=45, top_index=7) +# TNT +block(blockid=46, top_index=9, side_index=8, nospawn=True) +# bookshelf +block(blockid=47, top_index=4, side_index=35) +# moss stone +block(blockid=48, top_index=36) +# obsidian +block(blockid=49, top_index=37) + +# torch, redstone torch (off), redstone torch(on) +@material(blockid=[50, 75, 76], data=[1, 2, 3, 4, 5], transparent=True) +def torches(blockid, data, north): + # first, north rotations + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + elif north == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 + + # choose the proper texture + if blockid == 50: # torch + small = terrain_images[80] + elif blockid == 75: # off redstone torch + small = terrain_images[115] + else: # on redstone torch + small = terrain_images[99] + + # compose a torch bigger than the normal + # (better for doing transformations) + torch = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(torch,small,(-4,-3)) + composite.alpha_over(torch,small,(-5,-2)) + composite.alpha_over(torch,small,(-3,-2)) + + # angle of inclination of the texture + rotation = 15 + + if data == 1: # pointing south + torch = torch.rotate(-rotation, Image.NEAREST) # nearest filter is more nitid. + img = build_full_block(None, None, None, torch, None, None) + + elif data == 2: # pointing north + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, None, torch, None, None, None) + + elif data == 3: # pointing west + torch = torch.rotate(rotation, Image.NEAREST) + img = build_full_block(None, torch, None, None, None, None) + + elif data == 4: # pointing east + torch = torch.rotate(-rotation, Image.NEAREST) + img = build_full_block(None, None, None, None, torch, None) + + elif data == 5: # standing on the floor + # compose a "3d torch". + img = Image.new("RGBA", (24,24), bgcolor) + + small_crop = small.crop((2,2,14,14)) + slice = small_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(img, slice, (7,5)) + composite.alpha_over(img, small_crop, (6,6)) + composite.alpha_over(img, small_crop, (7,6)) + composite.alpha_over(img, slice, (7,7)) + + return img + +# fire +@material(blockid=51, data=range(16), transparent=True) +def fire(blockid, data): + firetexture = _load_image("fire.png") + side1 = transform_image_side(firetexture) + side2 = transform_image_side(firetexture).transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + + composite.alpha_over(img, side1, (12,0), side1) + composite.alpha_over(img, side2, (0,0), side2) + + composite.alpha_over(img, side1, (0,6), side1) + composite.alpha_over(img, side2, (12,6), side2) + + return img + +# monster spawner +block(blockid=52, top_index=34, transparent=True) + +# wooden, cobblestone, red brick, stone brick and netherbrick stairs. +@material(blockid=[53,67,108,109,114], data=range(4), transparent=True, solid=True, nospawn=True) +def stairs(blockid, data, north): + + # first, north rotations + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + if blockid == 53: # wooden + texture = terrain_images[4] + elif blockid == 67: # cobblestone + texture = terrain_images[16] + elif blockid == 108: # red brick stairs + texture = terrain_images[7] + elif blockid == 109: # stone brick stairs + texture = terrain_images[54] + elif blockid == 114: # netherbrick stairs + texture = terrain_images[224] + + side = texture.copy() + half_block_u = texture.copy() # up, down, left, right + half_block_d = texture.copy() + half_block_l = texture.copy() + half_block_r = texture.copy() + + # generate needed geometries + ImageDraw.Draw(side).rectangle((0,0,7,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_u).rectangle((0,8,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_d).rectangle((0,0,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_l).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(half_block_r).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + if data == 0: # ascending south + img = build_full_block(half_block_r, None, None, half_block_d, side.transpose(Image.FLIP_LEFT_RIGHT)) + tmp1 = transform_image_side(half_block_u) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.8) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_l) + composite.alpha_over(img, tmp2, (0,6)) + + elif data == 1: # ascending north + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_r) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_l, None, None, texture, side) + composite.alpha_over(img, tmp2) + + elif data == 2: # ascending west + img = Image.new("RGBA", (24,24), bgcolor) # first paste the texture in the back + tmp1 = transform_image_top(half_block_u) + composite.alpha_over(img, tmp1, (0,6)) + tmp2 = build_full_block(half_block_d, None, None, side, texture) + composite.alpha_over(img, tmp2) + + elif data == 3: # ascending east + img = build_full_block(half_block_u, None, None, side.transpose(Image.FLIP_LEFT_RIGHT), half_block_d) + tmp1 = transform_image_side(half_block_u).transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the vertical part of the second step + sidealpha = tmp1.split()[3] + # darken it a bit more than usual, looks better + tmp1 = ImageEnhance.Brightness(tmp1).enhance(0.7) + tmp1.putalpha(sidealpha) + + composite.alpha_over(img, tmp1, (6,4)) #workaround, fixes a hole + composite.alpha_over(img, tmp1, (6,3)) + tmp2 = transform_image_top(half_block_d) + composite.alpha_over(img, tmp2, (0,6)) + + # touch up a (horrible) pixel + img.putpixel((18,3),(0,0,0,0)) + + return img + +# normal and locked chest (locked was the one used in april fools' day) +# uses pseudo-ancildata found in iterate.c +@material(blockid=[54,95], data=range(12), solid=True) +def chests(blockid, data): + # First two bits of the pseudo data store if it's a single chest + # or it's a double chest, first half or second half (left to right). + # The last two bits store the orientation. + + # No need for north stuff, uses pseudo data and rotates with the map + + top = terrain_images[25] + side = terrain_images[26] + + if data & 12 == 0: # single chest + front = terrain_images[27] + back = terrain_images[26] + + elif data & 12 == 4: # double, first half + front = terrain_images[41] + back = terrain_images[57] + + elif data & 12 == 8: # double, second half + front = terrain_images[42] + back = terrain_images[58] + + else: # just in case + front = terrain_images[25] + side = terrain_images[25] + back = terrain_images[25] + + if data & 3 == 0: # facing west + img = build_full_block(top, None, None, side, front) + + elif data & 3 == 1: # north + img = build_full_block(top, None, None, front, side) + + elif data & 3 == 2: # east + img = build_full_block(top, None, None, side, back) + + elif data & 3 == 3: # south + img = build_full_block(top, None, None, back, side) + + else: + img = build_full_block(top, None, None, back, side) + + return img + +# redstone wire +# uses pseudo-ancildata found in iterate.c +@material(blockid=55, data=range(128), transparent=True) +def wire(blockid, data): + + if data & 0b1000000 == 64: # powered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(255,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(255,0,0)) + + + else: # unpowered redstone wire + redstone_wire_t = terrain_images[165] + redstone_wire_t = tintTexture(redstone_wire_t,(48,0,0)) + + redstone_cross_t = terrain_images[164] + redstone_cross_t = tintTexture(redstone_cross_t,(48,0,0)) + + # generate an image per redstone direction + branch_top_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_left).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_top_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_top_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_top_right).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_right = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_right).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + branch_bottom_left = redstone_cross_t.copy() + ImageDraw.Draw(branch_bottom_left).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(branch_bottom_left).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # generate the bottom texture + if data & 0b111111 == 0: + bottom = redstone_cross_t.copy() + + elif data & 0b1111 == 10: #= 0b1010 redstone wire in the x direction + bottom = redstone_wire_t.copy() + + elif data & 0b1111 == 5: #= 0b0101 redstone wire in the y direction + bottom = redstone_wire_t.copy().rotate(90) + + else: + bottom = Image.new("RGBA", (16,16), bgcolor) + if (data & 0b0001) == 1: + composite.alpha_over(bottom,branch_top_left) + + if (data & 0b1000) == 8: + composite.alpha_over(bottom,branch_top_right) + + if (data & 0b0010) == 2: + composite.alpha_over(bottom,branch_bottom_left) + + if (data & 0b0100) == 4: + composite.alpha_over(bottom,branch_bottom_right) + + # check for going up redstone wire + if data & 0b100000 == 32: + side1 = redstone_wire_t.rotate(90) + else: + side1 = None + + if data & 0b010000 == 16: + side2 = redstone_wire_t.rotate(90) + else: + side2 = None + + img = build_full_block(None,side1,side2,None,None,bottom) + + return img + +# diamond ore +block(blockid=56, top_index=50) +# diamond block +block(blockid=57, top_index=24) + +# crafting table +# needs two different sides +@material(blockid=58, solid=True) +def crafting_table(blockid, data): + top = terrain_images[43] + side3 = terrain_images[43+16] + side4 = terrain_images[43+16+1] + + img = build_full_block(top, None, None, side3, side4, None) + return img + +# crops +@material(blockid=59, data=range(8), transparent=True, nospawn=True) +def crops(blockid, data): + raw_crop = terrain_images[88+data] + crop1 = transform_image_top(raw_crop) + crop2 = transform_image_side(raw_crop) + crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT) + + img = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(img, crop1, (0,12), crop1) + composite.alpha_over(img, crop2, (6,3), crop2) + composite.alpha_over(img, crop3, (6,3), crop3) + return img + +# farmland +@material(blockid=60, data=range(9), solid=True) +def farmland(blockid, data): + top = terrain_images[86] + if data == 0: + top = terrain_images[87] + return build_block(top, terrain_images[2]) + +# signposts +@material(blockid=63, data=range(16), transparent=True) +def signpost(blockid, data, north): + + # first north rotations + if north == 'upper-left': + data = (data + 4) % 16 + elif north == 'upper-right': + data = (data + 8) % 16 + elif north == 'lower-right': + data = (data + 12) % 16 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # If the signpost is looking directly to the image, draw some + # random dots, they will look as text. + if data in (0,1,2,3,4,5,15): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + + # Minecraft uses wood texture for the signpost stick + texture_stick = terrain_images[20] + texture_stick = texture_stick.resize((12,12), Image.ANTIALIAS) + ImageDraw.Draw(texture_stick).rectangle((2,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + img = Image.new("RGBA", (24,24), bgcolor) + + # W N ~90 E S ~270 + angles = (330.,345.,0.,15.,30.,55.,95.,120.,150.,165.,180.,195.,210.,230.,265.,310.) + angle = math.radians(angles[data]) + post = transform_image_angle(texture, angle) + + # choose the position of the "3D effect" + incrementx = 0 + if data in (1,6,7,8,9,14): + incrementx = -1 + elif data in (3,4,5,11,12,13): + incrementx = +1 + + composite.alpha_over(img, texture_stick,(11, 8),texture_stick) + # post2 is a brighter signpost pasted with a small shift, + # gives to the signpost some 3D effect. + post2 = ImageEnhance.Brightness(post).enhance(1.2) + composite.alpha_over(img, post2,(incrementx, -3),post2) + composite.alpha_over(img, post, (0,-2), post) + + return img + + +# wooden and iron door +@material(blockid=[64,71], data=range(16), transparent=True) +def door(blockid, data, north): + #Masked to not clobber block top/bottom & swung info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + if data & 0x8 == 0x8: # top of the door + raw_door = terrain_images[81 if blockid == 64 else 82] + else: # bottom of the door + raw_door = terrain_images[97 if blockid == 64 else 98] + + # if you want to render all doors as closed, then force + # force swung to be False + if data & 0x4 == 0x4: + swung=True + else: + swung=False + + # mask out the high bits to figure out the orientation + img = Image.new("RGBA", (24,24), bgcolor) + if (data & 0x03) == 0: # northeast corner + if not swung: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (0,6), tex) + else: + # flip first to set the doornob on the correct side + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + tex = tex.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if (data & 0x03) == 1: # southeast corner + if not swung: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + else: + tex = transform_image_side(raw_door) + composite.alpha_over(img, tex, (12,0), tex) + + if (data & 0x03) == 2: # southwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (12,0), tex) + else: + tex = transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if (data & 0x03) == 3: # northwest corner + if not swung: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + else: + tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) + composite.alpha_over(img, tex, (0,6), tex) + + return img + +# ladder +@material(blockd=65, data=[2, 3, 4, 5], transparent=True) +def ladder(blockid, data, north): + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[83] + + if data == 5: + # normally this ladder would be obsured by the block it's attached to + # but since ladders can apparently be placed on transparent blocks, we + # have to render this thing anyway. same for data == 2 + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + return generate_texture_tuple(img, blockID) + if data == 2: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + return generate_texture_tuple(img, blockID) + if data == 3: + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + return generate_texture_tuple(img, blockID) + if data == 4: + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + return generate_texture_tuple(img, blockID) + + +# wall signs +@material(blockid=68, data=[2, 3, 4, 5], transparent=True) +def wall_sign(blockid, data, north): # wall sign + + # first north rotations + if north == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + elif north == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 + + texture = terrain_images[4].copy() + # cut the planks to the size of a signpost + ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # draw some random black dots, they will look as text + """ don't draw text at the moment, they are used in blank for decoration + + if data in (3,4): + for i in range(15): + x = randint(4,11) + y = randint(3,7) + texture.putpixel((x,y),(0,0,0,255)) + """ + + img = Image.new("RGBA", (24,24), bgcolor) + + incrementx = 0 + if data == 2: # east + incrementx = +1 + sign = build_full_block(None, None, None, None, texture) + elif data == 3: # west + incrementx = -1 + sign = build_full_block(None, texture, None, None, None) + elif data == 4: # north + incrementx = +1 + sign = build_full_block(None, None, texture, None, None) + elif data == 5: # south + incrementx = -1 + sign = build_full_block(None, None, None, texture, None) + + sign2 = ImageEnhance.Brightness(sign).enhance(1.2) + composite.alpha_over(img, sign2,(incrementx, 2),sign2) + composite.alpha_over(img, sign, (0,3), sign) + + return img + +## +## not rendered: levers +## +@material(blockid=69, data=range(16), transparent=True) +def levers(blockid, data, north): + # place holder, used to mae the block transparent + return None + +# wooden and stone pressure plates +@material(blockid=[70, 72], data=[0,1], transparent=True) +def pressure_plate(blockid, data): + if blockid == 70: # stone + t = terrain_images[1].copy() + else: # wooden + t = terrain_images[4].copy() + + # cut out the outside border, pressure plates are smaller + # than a normal block + ImageDraw.Draw(t).rectangle((0,0,15,15),outline=(0,0,0,0)) + + # create the textures and a darker version to make a 3d by + # pasting them with an offstet of 1 pixel + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(t) + + alpha = top.split()[3] + topd = ImageEnhance.Brightness(top).enhance(0.8) + topd.putalpha(alpha) + + #show it 3d or 2d if unpressed or pressed + if data == 0: + composite.alpha_over(img,topd, (0,12),topd) + composite.alpha_over(img,top, (0,11),top) + elif data == 1: + composite.alpha_over(img,top, (0,12),top) + + return img + +# normal and glowing redstone ore +block(blockid=[73, 74], top_index=51) + +@material(blockid=77, data=range(16), transparent=True) +def buttons(blockid, data, north): + + # 0x8 is set if the button is pressed mask this info and render + # it as unpressed + data = data & 0x7 + + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + elif north == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 + + t = terrain_images[1].copy() + + # generate the texture for the button + ImageDraw.Draw(t).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(t).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + img = Image.new("RGBA", (24,24), bgcolor) + + button = transform_image_side(t) + + if data == 1: # facing SOUTH + # buttons can't be placed in transparent blocks, so this + # direction can't be seen + return None + + elif data == 2: # facing NORTH + # paste it twice with different brightness to make a 3D effect + composite.alpha_over(img, button, (12,-1), button) + + alpha = button.split()[3] + button = ImageEnhance.Brightness(button).enhance(0.9) + button.putalpha(alpha) + + composite.alpha_over(img, button, (11,0), button) + + elif data == 3: # facing WEST + # paste it twice with different brightness to make a 3D effect + button = button.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, button, (0,-1), button) + + alpha = button.split()[3] + button = ImageEnhance.Brightness(button).enhance(0.9) + button.putalpha(alpha) + + composite.alpha_over(img, button, (1,0), button) + + elif data == 4: # facing EAST + # buttons can't be placed in transparent blocks, so this + # direction can't be seen + return None + + return img + +# snow +@material(blockid=78, data=range(8), transparent=True, solid=True) +def snow(blockid, data): + # still not rendered correctly: data other than 0 + + tex = terrain_images[66] + + # make the side image, top 3/4 transparent + mask = tex.crop((0,12,16,16)) + sidetex = Image.new(tex.mode, tex.size, bgcolor) + composite.alpha_over(sidetex, mask, (0,12,16,16), mask) + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(tex) + side = transform_image_side(sidetex) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + composite.alpha_over(img, side, (0,6), side) + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,9), top) + + return img + +# snow block +block(blockid=80, top_index=66) + +# cactus +@material(blockid=81, data=range(15), transparent=True, solid=True, nospawn=True) +def cactus(blockid, data): + top = terrain_images[69] + side = terrain_images[70] + + img = Image.new("RGBA", (24,24), bgcolor) + + top = transform_image_top(top) + side = 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) + + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,6), otherside) + composite.alpha_over(img, top, (0,0), top) + + return img + +# clay block +block(blockid=82, top_index=72) + +# sugar cane +@material(blockid=83, data=range(16), transparent=True) +def sugar_cane(blockid, data): + tex = terrain_images[73] + return build_sprite(tex) + +# jukebox +@material(blockid=84, data=range(16), solid=True) +def jukebox(blockid, data): + return build_block(terrain_images[75], terrain_images[74]) + +# nether and normal fences +# uses pseudo-ancildata found in iterate.c +@material(blockid=[85, 113], data=range(16), transparent=True, nospawn=True) +def fence(blockid, data): + # no need for north rotations, it uses pseudo data. + # create needed images for Big stick fence + if blockid == 85: # normal fence + fence_top = terrain_images[4].copy() + fence_side = terrain_images[4].copy() + fence_small_side = terrain_images[4].copy() + else: # netherbrick fence + fence_top = terrain_images[224].copy() + fence_side = terrain_images[224].copy() + fence_small_side = terrain_images[224].copy() + + # generate the textures of the fence + ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the big stick + fence_side = transform_image_side(fence_side) + fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) + fence_top = transform_image_top(fence_top) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_side.split()[3] + fence_side = ImageEnhance.Brightness(fence_side).enhance(0.9) + fence_side.putalpha(sidealpha) + othersidealpha = fence_other_side.split()[3] + fence_other_side = ImageEnhance.Brightness(fence_other_side).enhance(0.8) + fence_other_side.putalpha(othersidealpha) + + # Compose the fence big stick + fence_big = Image.new("RGBA", (24,24), bgcolor) + composite.alpha_over(fence_big,fence_side, (5,4),fence_side) + composite.alpha_over(fence_big,fence_other_side, (7,4),fence_other_side) + composite.alpha_over(fence_big,fence_top, (0,0),fence_top) + + # Now render the small sticks. + # Create needed images + ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # Create the sides and the top of the small sticks + fence_small_side = transform_image_side(fence_small_side) + fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Darken the sides slightly. These methods also affect the alpha layer, + # so save them first (we don't want to "darken" the alpha layer making + # the block transparent) + sidealpha = fence_small_other_side.split()[3] + fence_small_other_side = ImageEnhance.Brightness(fence_small_other_side).enhance(0.9) + fence_small_other_side.putalpha(sidealpha) + sidealpha = fence_small_side.split()[3] + fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) + fence_small_side.putalpha(sidealpha) + + # Create img to compose the fence + img = Image.new("RGBA", (24,24), bgcolor) + + # Position of fence small sticks in img. + # These postitions are strange because the small sticks of the + # fence are at the very left and at the very right of the 16x16 images + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose small sticks in the back of the image, + # then big stick and thecn small sticks in the front. + + if (data & 0b0001) == 1: + composite.alpha_over(img,fence_small_side, pos_top_left,fence_small_side) # top left + if (data & 0b1000) == 8: + composite.alpha_over(img,fence_small_other_side, pos_top_right,fence_small_other_side) # top right + + composite.alpha_over(img,fence_big,(0,0),fence_big) + + if (data & 0b0010) == 2: + composite.alpha_over(img,fence_small_other_side, pos_bottom_left,fence_small_other_side) # bottom left + if (data & 0b0100) == 4: + composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right + + return img + +# pumpkin +@material(blockid=[86, 91], data=range(4), solid=True) +def pumpkin(blockid, data, north): # pumpkins, jack-o-lantern + # north rotation + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + + # texture generation + top = terrain_images[102] + frontID = 119 if blockid == 86 else 120 + front = terrain_images[frontID] + side = terrain_images[118] + + if data == 0: # pointing west + img = build_full_block(top, None, None, side, front) + + elif data == 1: # pointing north + img = build_full_block(top, None, None, front, side) + + else: # in any other direction the front can't be seen + img = build_full_block(top, None, None, side, side) + + return img + +# netherrack +block(blockid=87, top_index=103) + +# soul sand +block(blockid=88, top_index=104) + +# glowstone +block(blockid=89, top_index=105) + +# portal +@material(blockid=90, data=[1, 2, 4, 8], transparent=True) +def portal(blockid, data): + # no north orientation uses pseudo data + portaltexture = _load_image("portal.png") + img = Image.new("RGBA", (24,24), bgcolor) + + side = transform_image_side(portaltexture) + otherside = side.transpose(Image.FLIP_TOP_BOTTOM) + + if data in (1,4): + composite.alpha_over(img, side, (5,4), side) + + if data in (2,8): + composite.alpha_over(img, otherside, (5,4), otherside) + + return img + +# cake! +# TODO is rendered un-bitten +@material(blockid=92, data=range(6), transparent=True, nospawn=True) +def cake(blockid, data): + + # choose textures for cake + top = terrain_images[121] + side = terrain_images[122] + top = transform_image_top(top) + side = transform_image_side(side) + otherside = side.transpose(Image.FLIP_LEFT_RIGHT) + + # darken sides slightly + 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), bgcolor) + + # composite the cake + composite.alpha_over(img, side, (1,6), side) + composite.alpha_over(img, otherside, (11,7), otherside) # workaround, fixes a hole + composite.alpha_over(img, otherside, (12,6), otherside) + composite.alpha_over(img, top, (0,6), top) + + return img + +# redstone repeaters ON and OFF +@material(blockid=[93,94], data=range(16), transparent=True, nospawn=True) +def repeater(blockid, data, north): + # north rotation + # Masked to not clobber delay info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + # generate the diode + top = terrain_images[131] if blockid == 93 else terrain_images[147] + side = terrain_images[5] + increment = 13 + + if (data & 0x3) == 0: # pointing east + pass + + if (data & 0x3) == 1: # pointing south + top = top.rotate(270) + + if (data & 0x3) == 2: # pointing west + top = top.rotate(180) + + if (data & 0x3) == 3: # pointing north + top = top.rotate(90) + + img = build_full_block( (top, increment), None, None, side, side) + + # compose a "3d" redstone torch + t = terrain_images[115].copy() if blockid == 93 else terrain_images[99].copy() + torch = Image.new("RGBA", (24,24), bgcolor) + + t_crop = t.crop((2,2,14,14)) + slice = t_crop.copy() + ImageDraw.Draw(slice).rectangle((6,0,12,12),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(slice).rectangle((0,0,4,12),outline=(0,0,0,0),fill=(0,0,0,0)) + + composite.alpha_over(torch, slice, (6,4)) + composite.alpha_over(torch, t_crop, (5,5)) + composite.alpha_over(torch, t_crop, (6,5)) + composite.alpha_over(torch, slice, (6,6)) + + # paste redstone torches everywhere! + # the torch is too tall for the repeater, crop the bottom. + ImageDraw.Draw(torch).rectangle((0,16,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # touch up the 3d effect with big rectangles, just in case, for other texture packs + ImageDraw.Draw(torch).rectangle((0,24,10,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(torch).rectangle((12,15,24,24),outline=(0,0,0,0),fill=(0,0,0,0)) + + # torch positions for every redstone torch orientation. + # + # This is a horrible list of torch orientations. I tried to + # obtain these orientations by rotating the positions for one + # orientation, but pixel rounding is horrible and messes the + # torches. + + if (data & 0x3) == 0: # pointing east + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,2) + static_torch = (-3,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,3) + static_torch = (-3,-1) + + elif (data & 0x3) == 1: # pointing south + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,-1) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,2) + static_torch = (5,-1) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,2) + static_torch = (5,-1) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,3) + static_torch = (5,-1) + + elif (data & 0x3) == 2: # pointing west + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (5,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (0,0) + static_torch = (5,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (-1,0) + static_torch = (5,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (-2,-1) + static_torch = (5,3) + + elif (data & 0x3) == 3: # pointing north + if (data & 0xC) == 0: # one tick delay + moving_torch = (1,1) + static_torch = (-3,3) + + elif (data & 0xC) == 4: # two ticks delay + moving_torch = (2,0) + static_torch = (-3,3) + + elif (data & 0xC) == 8: # three ticks delay + moving_torch = (3,0) + static_torch = (-3,3) + + elif (data & 0xC) == 12: # four ticks delay + moving_torch = (4,-1) + static_torch = (-3,3) + + # this paste order it's ok for east and south orientation + # but it's wrong for north and west orientations. But using the + # default texture pack the torches are small enough to no overlap. + composite.alpha_over(img, torch, static_torch, torch) + composite.alpha_over(img, torch, moving_torch, torch) + + return img + +# trapdoor +# TODO the trapdoor is looks like a sprite when opened, that's not good +@material(blockid=96, data=range(8), transparent=True, nospawn=True) +def trapdoor(blockid, data, north): + + # north rotation + # Masked to not clobber opened/closed info + if north == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north == 'upper-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + elif north == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + + # texture generation + texture = terrain_images[84] + if data & 0x4 == 0x4: # opened trapdoor + if data & 0x3 == 0: # west + img = build_full_block(None, None, None, None, texture) + if data & 0x3 == 1: # east + img = build_full_block(None, texture, None, None, None) + if data & 0x3 == 2: # south + img = build_full_block(None, None, texture, None, None) + if data & 0x3 == 3: # north + img = build_full_block(None, None, None, texture, None) + + elif data & 0x4 == 0: # closed trapdoor + img = build_full_block((texture, 12), None, None, texture, texture) + + return img + +# block with hidden silverfish (stone, cobblestone and stone brick) +@material(blockid=97, data=range(3), solid=True) +def hidden_silverfish(blockid, data): + if data == 0: # stone + t = terrain_images[1] + elif data == 1: # cobblestone + t = terrain_images[16] + elif data == 2: # stone brick + t = terrain_images[54] + + img = build_block(t, t) + + return img + +# stone brick +@material(blockid=98, data=range(3), solid=True) +def stone_brick(blockid, data): + if data == 0: # normal + t = terrain_images[54] + elif data == 1: # mossy + t = terrain_images[100] + else: # cracked + t = terrain_images[101] + + img = build_full_block(t, None, None, t, t) + + return img + +# huge brown and red mushroom +@material(blockid=[99,100], data=range(11), solid=True) +def huge_mushroom(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 6 + elif data == 3: data = 9 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 1 + elif data == 8: data = 4 + elif data == 9: data = 7 + elif north == 'upper-right': + if data == 1: data = 9 + elif data == 2: data = 8 + elif data == 3: data = 7 + elif data == 4: data = 6 + elif data == 6: data = 4 + elif data == 7: data = 3 + elif data == 8: data = 2 + elif data == 9: data = 1 + elif north == 'lower-right': + if data == 1: data = 7 + elif data == 2: data = 4 + elif data == 3: data = 1 + elif data == 4: data = 2 + elif data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 3 + + # texture generation + if blockid == 99: # brown + cap = terrain_images[126] + else: # red + cap = terrain_images[125] + + stem = terrain_images[141] + porous = terrain_images[142] + + if data == 0: # fleshy piece + img = build_full_block(porous, None, None, porous, porous) + + if data == 1: # north-east corner + img = build_full_block(cap, None, None, cap, porous) + + if data == 2: # east side + img = build_full_block(cap, None, None, porous, porous) + + if data == 3: # south-east corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 4: # north side + img = build_full_block(cap, None, None, cap, porous) + + if data == 5: # top piece + img = build_full_block(cap, None, None, porous, porous) + + if data == 6: # south side + img = build_full_block(cap, None, None, cap, porous) + + if data == 7: # north-west corner + img = build_full_block(cap, None, None, cap, cap) + + if data == 8: # west side + img = build_full_block(cap, None, None, porous, cap) + + if data == 9: # south-west corner + img = build_full_block(cap, None, None, porous, cap) + + if data == 10: # stem + img = build_full_block(porous, None, None, stem, stem) + + return img + +# 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 +@material(blockid=[101,102], data=range(16), transparent=True, nospawn=True) +def panes(blockid, data): + # no north rotation, uses pseudo data + if blockid == 101: + # iron bars + t = terrain_images[85] + else: + # glass panes + t = terrain_images[49] + left = t.copy() + right = t.copy() + + # generate the four small pieces of the glass pane + ImageDraw.Draw(right).rectangle((0,0,7,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(left).rectangle((8,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + up_left = transform_image_side(left) + up_right = transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM) + dw_right = transform_image_side(right) + dw_left = transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM) + + # Create img to compose the texture + img = Image.new("RGBA", (24,24), bgcolor) + + # +x axis points top right direction + # +y axis points bottom right direction + # First compose things in the back of the image, + # then things in the front. + + if (data & 0b0001) == 1 or data == 0: + composite.alpha_over(img,up_left, (6,3),up_left) # top left + if (data & 0b1000) == 8 or data == 0: + composite.alpha_over(img,up_right, (6,3),up_right) # top right + if (data & 0b0010) == 2 or data == 0: + composite.alpha_over(img,dw_left, (6,3),dw_left) # bottom left + if (data & 0b0100) == 4 or data == 0: + composite.alpha_over(img,dw_right, (6,3),dw_right) # bottom right + + return img + +# melon +block(blockid=103, top_index=137, side_index=136, 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, +# at the moment only render the growing stem +@material(blockid=[104,105], data=range(8), transparent=True) +def stem(blockid, data, north): + # the ancildata value indicates how much of the texture + # is shown. + + # not fully grown stem or no pumpkin/melon touching it, + # straight up stem + t = terrain_images[111].copy() + img = Image.new("RGBA", (16,16), bgcolor) + composite.alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t) + img = build_sprite(t) + if data & 7 == 7: + # fully grown stem gets brown color! + # there is a conditional in rendermode-normal.c to not + # tint the data value 7 + img = tintTexture(img, (211,169,116)) + return img + + +# vines +# TODO multiple sides of a block can contain vines! At the moment +# only pure directions are rendered +# (source http://www.minecraftwiki.net/wiki/Data_values#Vines) +@material(blockid=106, data=range(8), transparent=True) +def vines(blockid, data, north): + # north rotation + if north == 'upper-left': + if data == 1: data = 2 + elif data == 4: data = 8 + elif data == 8: data = 1 + elif data == 2: data = 4 + elif north == 'upper-right': + if data == 1: data = 4 + elif data == 4: data = 1 + elif data == 8: data = 2 + elif data == 2: data = 8 + elif north == 'lower-right': + if data == 1: data = 8 + elif data == 4: data = 2 + elif data == 8: data = 4 + elif data == 2: data = 1 + + # texture generation + img = Image.new("RGBA", (24,24), bgcolor) + raw_texture = terrain_images[143] + + if data == 2: # south + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (0,6), tex) + + if data == 1: # east + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (12,6), tex) + + if data == 4: # west + tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, tex, (0,0), tex) + + if data == 8: # north + tex = transform_image_side(raw_texture) + composite.alpha_over(img, tex, (12,0), tex) + + return img + +# fence gates +@material(blockid=107, data=range(8), transparent=True, nospawn=True) +def fence_gate(blockid, data, north): + + # north rotation + opened = False + if data & 0x4: + data = data & 0x3 + opened = True + if north == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 + if opened: + data = data | 0x4 + + # create the closed gate side + gate_side = terrain_images[4].copy() + gate_side_draw = ImageDraw.Draw(gate_side) + gate_side_draw.rectangle((7,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,4,9,6),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((7,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,12,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + gate_side_draw.rectangle((14,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + + # darken the sides slightly, as with the fences + sidealpha = gate_side.split()[3] + gate_side = ImageEnhance.Brightness(gate_side).enhance(0.9) + gate_side.putalpha(sidealpha) + + # create the other sides + mirror_gate_side = transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT)) + gate_side = transform_image_side(gate_side) + gate_other_side = gate_side.transpose(Image.FLIP_LEFT_RIGHT) + mirror_gate_other_side = mirror_gate_side.transpose(Image.FLIP_LEFT_RIGHT) + + # Create img to compose the fence gate + img = Image.new("RGBA", (24,24), bgcolor) + + if data & 0x4: + # opened + data = data & 0x3 + if data == 0: + composite.alpha_over(img, gate_side, (2,8), gate_side) + composite.alpha_over(img, gate_side, (13,3), gate_side) + elif data == 1: + composite.alpha_over(img, gate_other_side, (-1,3), gate_other_side) + composite.alpha_over(img, gate_other_side, (10,8), gate_other_side) + elif data == 2: + composite.alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side) + composite.alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side) + elif data == 3: + composite.alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, (13,7), mirror_gate_other_side) + else: + # closed + + # positions for pasting the fence sides, as with fences + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) + + if data == 0 or data == 2: + composite.alpha_over(img, gate_other_side, pos_top_right, gate_other_side) + composite.alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side) + elif data == 1 or data == 3: + composite.alpha_over(img, gate_side, pos_top_left, gate_side) + composite.alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side) + + return img + +# mycelium +block(blockid=110, top_index=78, side_index=77) + +# lilypad +# TODO the data-block orientation relation is not clear +@material(blockid=111, data=range(4), transparent=True) +def lilypad(blockid, data, north): + if north == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + elif north == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 + + t = terrain_images[76] # NOTE: using same data as stairs, no + # info in minepedia at the moment. + if data == 0: # pointing south + img = build_full_block(None, None, None, None, None, t) + elif data == 1: # pointing north + img = build_full_block(None, None, None, None, None, t.rotate(180)) + elif data == 2: # pointing west + img = build_full_block(None, None, None, None, None, t.rotate(270)) + elif data == 3: # pointing east + img = build_full_block(None, None, None, None, None, t.rotate(90)) + + return img + +# nether brick +block(blockid=112, top_index=224, side_index=224) + +# nether wart +@material(blockid=115, data=range(4), transparent=True) +def nether_wart(blockid, data): + if data == 0: # just come up + t = terrain_images[226] + elif data in (1, 2): + t = terrain_images[227] + else: # fully grown + t = terrain_images[228] + + # use the same technic as tall grass + img = build_billboard(t) + + return img + +# enchantment table +# TODO there's no book at the moment +@material(blockid=116, transparent=True) +def enchantment_table(blockid, data): + # no book at the moment + top = terrain_images[166] + side = terrain_images[182] + img = build_full_block((top, 4), None, None, side, side) + + return img + +# brewing stand +# TODO this is a place holder, is a 2d image pasted +@material(blockid=117, data=range(5), transparent=True) +def brewing_stand(blockid, data, north): + t = terrain_images[157] + img = build_billboard(t) + return img + +# cauldron +@material(blockid=118, data=range(4), transparent=True) +def cauldron(blockid, data): + side = terrain_images[154] + top = terrain_images[138] + bottom = terrain_images[139] + water = transform_image_top(_load_image("water.png")) + if data == 0: # empty + img = build_full_block(top, side, side, side, side) + if data == 1: # 1/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,8), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 2: # 2/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,4), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + if data == 3: # 3/3 filled + img = build_full_block(None , side, side, None, None) + composite.alpha_over(img, water, (0,0), water) + img2 = build_full_block(top , None, None, side, side) + composite.alpha_over(img, img2, (0,0), img2) + + return img + +# end portal +@material(blockid=119, transparent=True) +def end_portal(blockid, data): + img = Image.new("RGBA", (24,24), bgcolor) + # generate a black texure with white, blue and grey dots resembling stars + t = Image.new("RGBA", (16,16), (0,0,0,255)) + for color in [(155,155,155,255), (100,255,100,255), (255,255,255,255)]: + for i in range(6): + x = randint(0,15) + y = randint(0,15) + t.putpixel((x,y),color) + + t = transform_image_top(t) + composite.alpha_over(img, t, (0,0), t) + + return img + +# end portal frame +@material(blockid=120, data=range(5), transparent=True) +def end_porta_frame(blockid, data): + # The bottom 2 bits are oritation info but seems there is no + # graphical difference between orientations + top = terrain_images[158] + eye_t = terrain_images[174] + side = terrain_images[159] + img = build_full_block((top, 4), None, None, side, side) + if data & 0x4 == 0x4: # ender eye on it + # generate the eye + eye_t = terrain_images[174].copy() + eye_t_s = terrain_images[174].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)) + # trnasform images and paste + eye = transform_image_top(eye_t) + eye_s = transform_image_side(eye_t_s) + eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT) + composite.alpha_over(img, eye_s, (5,5), eye_s) + composite.alpha_over(img, eye_os, (9,5), eye_os) + composite.alpha_over(img, eye, (0,0), eye) + + return img + +# end stone +block(blockid=121, top_index=175)