6393 lines
277 KiB
Python
6393 lines
277 KiB
Python
# This file is part of the Minecraft Overviewer.
|
|
#
|
|
# Minecraft Overviewer is free software: you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or (at
|
|
# your option) any later version.
|
|
#
|
|
# Minecraft Overviewer is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
# Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with the Overviewer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from collections import OrderedDict
|
|
import sys
|
|
import imp
|
|
import os
|
|
import os.path
|
|
import zipfile
|
|
from io import BytesIO
|
|
import math
|
|
from random import randint
|
|
import numpy
|
|
from PIL import Image, ImageEnhance, ImageOps, ImageDraw
|
|
import logging
|
|
import functools
|
|
|
|
from . import util
|
|
|
|
|
|
BLOCKTEX = "assets/minecraft/textures/block/"
|
|
|
|
# global variables to collate information in @material decorators
|
|
blockmap_generators = {}
|
|
|
|
known_blocks = set()
|
|
used_datas = set()
|
|
max_blockid = 0
|
|
max_data = 0
|
|
|
|
transparent_blocks = set()
|
|
solid_blocks = set()
|
|
fluid_blocks = set()
|
|
nospawn_blocks = set()
|
|
nodata_blocks = set()
|
|
|
|
|
|
# This is here for circular import reasons.
|
|
# Please don't ask, I choose to repress these memories.
|
|
# ... okay fine I'll tell you.
|
|
# Initialising the C extension requires access to the globals above.
|
|
# Due to the circular import, this wouldn't work, unless we reload the
|
|
# module in the C extension or just move the import below its dependencies.
|
|
from .c_overviewer import alpha_over
|
|
|
|
|
|
class TextureException(Exception):
|
|
"To be thrown when a texture is not found."
|
|
pass
|
|
|
|
|
|
color_map = ["white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray",
|
|
"light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"]
|
|
|
|
|
|
##
|
|
## Textures object
|
|
##
|
|
class Textures(object):
|
|
"""An object that generates a set of block sprites to use while
|
|
rendering. It accepts a background color, north direction, and
|
|
local textures path.
|
|
"""
|
|
def __init__(self, texturepath=None, bgcolor=(26, 26, 26, 0), northdirection=0):
|
|
self.bgcolor = bgcolor
|
|
self.rotation = northdirection
|
|
self.find_file_local_path = texturepath
|
|
|
|
# not yet configurable
|
|
self.texture_size = 24
|
|
self.texture_dimensions = (self.texture_size, self.texture_size)
|
|
|
|
# this is set in in generate()
|
|
self.generated = False
|
|
|
|
# see load_image_texture()
|
|
self.texture_cache = {}
|
|
|
|
# once we find a jarfile that contains a texture, we cache the ZipFile object here
|
|
self.jars = OrderedDict()
|
|
|
|
##
|
|
## pickle support
|
|
##
|
|
|
|
def __getstate__(self):
|
|
# we must get rid of the huge image lists, and other images
|
|
attributes = self.__dict__.copy()
|
|
for attr in ['blockmap', 'biome_grass_texture', 'watertexture', 'lavatexture', 'firetexture', 'portaltexture', 'lightcolor', 'grasscolor', 'foliagecolor', 'watercolor', 'texture_cache']:
|
|
try:
|
|
del attributes[attr]
|
|
except KeyError:
|
|
pass
|
|
attributes['jars'] = OrderedDict()
|
|
return attributes
|
|
def __setstate__(self, attrs):
|
|
# regenerate textures, if needed
|
|
for attr, val in list(attrs.items()):
|
|
setattr(self, attr, val)
|
|
self.texture_cache = {}
|
|
if self.generated:
|
|
self.generate()
|
|
|
|
##
|
|
## The big one: generate()
|
|
##
|
|
|
|
def generate(self):
|
|
# Make sure we have the foliage/grasscolor images available
|
|
try:
|
|
self.load_foliage_color()
|
|
self.load_grass_color()
|
|
except TextureException as e:
|
|
logging.error(
|
|
"Your system is missing either assets/minecraft/textures/colormap/foliage.png "
|
|
"or assets/minecraft/textures/colormap/grass.png. Either complement your "
|
|
"resource pack with these texture files, or install the vanilla Minecraft "
|
|
"client to use as a fallback.")
|
|
raise e
|
|
|
|
# generate biome grass mask
|
|
self.biome_grass_texture = self.build_block(self.load_image_texture("assets/minecraft/textures/block/grass_block_top.png"), self.load_image_texture("assets/minecraft/textures/block/grass_block_side_overlay.png"))
|
|
|
|
# generate the blocks
|
|
global blockmap_generators
|
|
global known_blocks, used_datas
|
|
self.blockmap = [None] * max_blockid * max_data
|
|
|
|
for (blockid, data), texgen in list(blockmap_generators.items()):
|
|
tex = texgen(self, blockid, data)
|
|
self.blockmap[blockid * max_data + data] = self.generate_texture_tuple(tex)
|
|
|
|
if self.texture_size != 24:
|
|
# rescale biome grass
|
|
self.biome_grass_texture = self.biome_grass_texture.resize(self.texture_dimensions, Image.ANTIALIAS)
|
|
|
|
# rescale the rest
|
|
for i, tex in enumerate(blockmap):
|
|
if tex is None:
|
|
continue
|
|
block = tex[0]
|
|
scaled_block = block.resize(self.texture_dimensions, Image.ANTIALIAS)
|
|
blockmap[i] = self.generate_texture_tuple(scaled_block)
|
|
|
|
self.generated = True
|
|
|
|
##
|
|
## Helpers for opening textures
|
|
##
|
|
|
|
def find_file(self, 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:
|
|
|
|
* In the directory textures_path given in the initializer if not already open
|
|
* In an already open resource pack or client jar file
|
|
* In the resource pack given by textures_path
|
|
* The program dir (same dir as overviewer.py) for extracted textures
|
|
* On Darwin, in /Applications/Minecraft for extracted textures
|
|
* Inside a minecraft client jar. Client jars are searched for in the
|
|
following location depending on platform:
|
|
|
|
* On Windows, at %APPDATA%/.minecraft/versions/
|
|
* On Darwin, at
|
|
$HOME/Library/Application Support/minecraft/versions
|
|
* at $HOME/.minecraft/versions/
|
|
|
|
Only the latest non-snapshot version >1.6 is used
|
|
|
|
* The overviewer_core/data/textures dir
|
|
|
|
"""
|
|
if verbose: logging.info("Starting search for {0}".format(filename))
|
|
|
|
# Look for the file is stored in with the overviewer
|
|
# installation. We include a few files that aren't included with Minecraft
|
|
# textures. This used to be for things such as water and lava, since
|
|
# they were generated by the game and not stored as images. Nowdays I
|
|
# believe that's not true, but we still have a few files distributed
|
|
# with overviewer.
|
|
# Do this first so we don't try all .jar files for stuff like "water.png"
|
|
programdir = util.get_program_path()
|
|
if verbose: logging.info("Looking for texture in overviewer_core/data/textures")
|
|
path = os.path.join(programdir, "overviewer_core", "data", "textures", filename)
|
|
if os.path.isfile(path):
|
|
if verbose: logging.info("Found %s in '%s'", filename, path)
|
|
return open(path, mode)
|
|
elif hasattr(sys, "frozen") or imp.is_frozen("__main__"):
|
|
# windows special case, when the package dir doesn't exist
|
|
path = os.path.join(programdir, "textures", filename)
|
|
if os.path.isfile(path):
|
|
if verbose: logging.info("Found %s in '%s'", filename, path)
|
|
return open(path, mode)
|
|
|
|
# A texture path was given on the command line. Search this location
|
|
# for the file first.
|
|
if self.find_file_local_path:
|
|
if (self.find_file_local_path not in self.jars
|
|
and os.path.isfile(self.find_file_local_path)):
|
|
# Must be a resource pack. Look for the requested file within
|
|
# it.
|
|
try:
|
|
pack = zipfile.ZipFile(self.find_file_local_path)
|
|
# pack.getinfo() will raise KeyError if the file is
|
|
# not found.
|
|
pack.getinfo(filename)
|
|
if verbose: logging.info("Found %s in '%s'", filename,
|
|
self.find_file_local_path)
|
|
self.jars[self.find_file_local_path] = pack
|
|
# ok cool now move this to the start so we pick it first
|
|
self.jars.move_to_end(self.find_file_local_path, last=False)
|
|
return pack.open(filename)
|
|
except (zipfile.BadZipfile, KeyError, IOError):
|
|
pass
|
|
elif os.path.isdir(self.find_file_local_path):
|
|
full_path = os.path.join(self.find_file_local_path, filename)
|
|
if os.path.isfile(full_path):
|
|
if verbose: logging.info("Found %s in '%s'", filename, full_path)
|
|
return open(full_path, mode)
|
|
|
|
# We already have some jars open, better use them.
|
|
if len(self.jars) > 0:
|
|
for jarpath in self.jars:
|
|
try:
|
|
jar = self.jars[jarpath]
|
|
jar.getinfo(filename)
|
|
if verbose: logging.info("Found (cached) %s in '%s'", filename,
|
|
jarpath)
|
|
return jar.open(filename)
|
|
except (KeyError, IOError) as e:
|
|
pass
|
|
|
|
# If we haven't returned at this point, then the requested file was NOT
|
|
# found in the user-specified texture path or resource pack.
|
|
if verbose: logging.info("Did not find the file in specified texture path")
|
|
|
|
|
|
# Look in the location of the overviewer executable for the given path
|
|
path = os.path.join(programdir, filename)
|
|
if os.path.isfile(path):
|
|
if verbose: logging.info("Found %s in '%s'", filename, path)
|
|
return open(path, mode)
|
|
|
|
if sys.platform.startswith("darwin"):
|
|
path = os.path.join("/Applications/Minecraft", filename)
|
|
if os.path.isfile(path):
|
|
if verbose: logging.info("Found %s in '%s'", filename, path)
|
|
return open(path, mode)
|
|
|
|
if verbose: logging.info("Did not find the file in overviewer executable directory")
|
|
if verbose: logging.info("Looking for installed minecraft jar files...")
|
|
|
|
# Find an installed minecraft client jar and look in it for the texture
|
|
# file we need.
|
|
versiondir = ""
|
|
if "APPDATA" in os.environ and sys.platform.startswith("win"):
|
|
versiondir = os.path.join(os.environ['APPDATA'], ".minecraft", "versions")
|
|
elif "HOME" in os.environ:
|
|
# For linux:
|
|
versiondir = os.path.join(os.environ['HOME'], ".minecraft", "versions")
|
|
if not os.path.exists(versiondir) and sys.platform.startswith("darwin"):
|
|
# For Mac:
|
|
versiondir = os.path.join(os.environ['HOME'], "Library",
|
|
"Application Support", "minecraft", "versions")
|
|
|
|
try:
|
|
if verbose: logging.info("Looking in the following directory: \"%s\"" % versiondir)
|
|
versions = os.listdir(versiondir)
|
|
if verbose: logging.info("Found these versions: {0}".format(versions))
|
|
except OSError:
|
|
# Directory doesn't exist? Ignore it. It will find no versions and
|
|
# fall through the checks below to the error at the bottom of the
|
|
# method.
|
|
versions = []
|
|
|
|
available_versions = []
|
|
for version in versions:
|
|
# Look for the latest non-snapshot that is at least 1.8. This
|
|
# version is only compatible with >=1.8, and we cannot in general
|
|
# tell if a snapshot is more or less recent than a release.
|
|
|
|
# Allow two component names such as "1.8" and three component names
|
|
# such as "1.8.1"
|
|
if version.count(".") not in (1,2):
|
|
continue
|
|
try:
|
|
versionparts = [int(x) for x in version.split(".")]
|
|
except ValueError:
|
|
continue
|
|
|
|
if versionparts < [1,8]:
|
|
continue
|
|
|
|
available_versions.append(versionparts)
|
|
|
|
available_versions.sort(reverse=True)
|
|
if not available_versions:
|
|
if verbose: logging.info("Did not find any non-snapshot minecraft jars >=1.8.0")
|
|
while(available_versions):
|
|
most_recent_version = available_versions.pop(0)
|
|
if verbose: logging.info("Trying {0}. Searching it for the file...".format(".".join(str(x) for x in most_recent_version)))
|
|
|
|
jarname = ".".join(str(x) for x in most_recent_version)
|
|
jarpath = os.path.join(versiondir, jarname, jarname + ".jar")
|
|
|
|
if os.path.isfile(jarpath):
|
|
try:
|
|
jar = zipfile.ZipFile(jarpath)
|
|
jar.getinfo(filename)
|
|
if verbose: logging.info("Found %s in '%s'", filename, jarpath)
|
|
self.jars[jarpath] = jar
|
|
return jar.open(filename)
|
|
except (KeyError, IOError) as e:
|
|
pass
|
|
except (zipfile.BadZipFile) as e:
|
|
logging.warning("Your jar {0} is corrupted, I'll be skipping it, but you "
|
|
"should probably look into that.".format(jarpath))
|
|
|
|
if verbose: logging.info("Did not find file {0} in jar {1}".format(filename, jarpath))
|
|
|
|
|
|
raise TextureException("Could not find the textures while searching for '{0}'. Try specifying the 'texturepath' option in your config file.\nSet it to the path to a Minecraft Resource pack.\nAlternately, install the Minecraft client (which includes textures)\nAlso see <http://docs.overviewer.org/en/latest/running/#installing-the-textures>\n(Remember, this version of Overviewer requires a 1.19-compatible resource pack)\n(Also note that I won't automatically use snapshots; you'll have to use the texturepath option to use a snapshot jar)".format(filename))
|
|
|
|
def load_image_texture(self, filename):
|
|
# Textures may be animated or in a different resolution than 16x16.
|
|
# This method will always return a 16x16 image
|
|
|
|
img = self.load_image(filename)
|
|
|
|
w,h = img.size
|
|
if w != h:
|
|
img = img.crop((0,0,w,w))
|
|
if w != 16:
|
|
img = img.resize((16, 16), Image.ANTIALIAS)
|
|
|
|
self.texture_cache[filename] = img
|
|
return img
|
|
|
|
def load_image(self, filename):
|
|
"""Returns an image object"""
|
|
|
|
try:
|
|
img = self.texture_cache[filename]
|
|
if isinstance(img, Exception): # Did we cache an exception?
|
|
raise img # Okay then, raise it.
|
|
return img
|
|
except KeyError:
|
|
pass
|
|
|
|
try:
|
|
fileobj = self.find_file(filename, verbose=logging.getLogger().isEnabledFor(logging.DEBUG))
|
|
except (TextureException, IOError) as e:
|
|
# We cache when our good friend find_file can't find
|
|
# a texture, so that we do not repeatedly search for it.
|
|
self.texture_cache[filename] = e
|
|
raise e
|
|
buffer = BytesIO(fileobj.read())
|
|
try:
|
|
img = Image.open(buffer).convert("RGBA")
|
|
except IOError:
|
|
raise TextureException("The texture {} appears to be corrupted. Please fix it. Run "
|
|
"Overviewer in verbose mode (-v) to find out where I loaded "
|
|
"that file from.".format(filename))
|
|
self.texture_cache[filename] = img
|
|
return img
|
|
|
|
|
|
|
|
def load_water(self):
|
|
"""Special-case function for loading water."""
|
|
watertexture = getattr(self, "watertexture", None)
|
|
if watertexture:
|
|
return watertexture
|
|
watertexture = self.load_image_texture("assets/minecraft/textures/block/water_still.png")
|
|
self.watertexture = watertexture
|
|
return watertexture
|
|
|
|
def load_lava(self):
|
|
"""Special-case function for loading lava."""
|
|
lavatexture = getattr(self, "lavatexture", None)
|
|
if lavatexture:
|
|
return lavatexture
|
|
lavatexture = self.load_image_texture("assets/minecraft/textures/block/lava_still.png")
|
|
self.lavatexture = lavatexture
|
|
return lavatexture
|
|
|
|
def load_portal(self):
|
|
"""Special-case function for loading portal."""
|
|
portaltexture = getattr(self, "portaltexture", None)
|
|
if portaltexture:
|
|
return portaltexture
|
|
portaltexture = self.load_image_texture("assets/minecraft/textures/block/nether_portal.png")
|
|
self.portaltexture = portaltexture
|
|
return portaltexture
|
|
|
|
def load_light_color(self):
|
|
"""Helper function to load the light color texture."""
|
|
if hasattr(self, "lightcolor"):
|
|
return self.lightcolor
|
|
try:
|
|
lightcolor = list(self.load_image("light_normal.png").getdata())
|
|
except Exception:
|
|
logging.warning("Light color image could not be found.")
|
|
lightcolor = None
|
|
self.lightcolor = lightcolor
|
|
return lightcolor
|
|
|
|
def load_grass_color(self):
|
|
"""Helper function to load the grass color texture."""
|
|
if not hasattr(self, "grasscolor"):
|
|
self.grasscolor = list(self.load_image("assets/minecraft/textures/colormap/grass.png").getdata())
|
|
return self.grasscolor
|
|
|
|
def load_foliage_color(self):
|
|
"""Helper function to load the foliage color texture."""
|
|
if not hasattr(self, "foliagecolor"):
|
|
self.foliagecolor = list(self.load_image("assets/minecraft/textures/colormap/foliage.png").getdata())
|
|
return self.foliagecolor
|
|
|
|
#I guess "watercolor" is wrong. But I can't correct as my texture pack don't define water color.
|
|
def load_water_color(self):
|
|
"""Helper function to load the water color texture."""
|
|
if not hasattr(self, "watercolor"):
|
|
self.watercolor = list(self.load_image("watercolor.png").getdata())
|
|
return self.watercolor
|
|
|
|
def _split_terrain(self, terrain):
|
|
"""Builds and returns a length 256 array of each 16x16 chunk
|
|
of texture.
|
|
"""
|
|
textures = []
|
|
(terrain_width, terrain_height) = terrain.size
|
|
texture_resolution = terrain_width / 16
|
|
for y in range(16):
|
|
for x in range(16):
|
|
left = x*texture_resolution
|
|
upper = y*texture_resolution
|
|
right = left+texture_resolution
|
|
lower = upper+texture_resolution
|
|
region = terrain.transform(
|
|
(16, 16),
|
|
Image.EXTENT,
|
|
(left,upper,right,lower),
|
|
Image.BICUBIC)
|
|
textures.append(region)
|
|
|
|
return textures
|
|
|
|
##
|
|
## Image Transformation Functions
|
|
##
|
|
|
|
@staticmethod
|
|
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
|
|
|
|
"""
|
|
|
|
# Resize to 17x17, since the diagonal is approximately 24 pixels, a nice
|
|
# even number that can be split in half twice
|
|
img = img.resize((17, 17), Image.ANTIALIAS)
|
|
|
|
# Build the Affine transformation matrix for this perspective
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
# Translate up and left, since rotations are about the origin
|
|
transform *= numpy.matrix([[1,0,8.5],[0,1,8.5],[0,0,1]])
|
|
# Rotate 45 degrees
|
|
ratio = math.cos(math.pi/4)
|
|
#transform *= numpy.matrix("[0.707,-0.707,0;0.707,0.707,0;0,0,1]")
|
|
transform *= numpy.matrix([[ratio,-ratio,0],[ratio,ratio,0],[0,0,1]])
|
|
# Translate back down and right
|
|
transform *= numpy.matrix([[1,0,-12],[0,1,-12],[0,0,1]])
|
|
# scale the image down by a factor of 2
|
|
transform *= numpy.matrix("[1,0,0;0,2,0;0,0,1]")
|
|
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((24,12), Image.AFFINE, transform)
|
|
return newimg
|
|
|
|
@staticmethod
|
|
def transform_image_side(img):
|
|
"""Takes an image and shears it for the left side of the cube (reflect for
|
|
the right side)"""
|
|
|
|
# Size of the cube side before shear
|
|
img = img.resize((12,12), Image.ANTIALIAS)
|
|
|
|
# Apply shear
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
transform *= numpy.matrix("[1,0,0;-0.5,1,0;0,0,1]")
|
|
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((12,18), Image.AFFINE, transform)
|
|
return newimg
|
|
|
|
@staticmethod
|
|
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"""
|
|
|
|
# Take the same size as trasform_image_side
|
|
img = img.resize((12,12), Image.ANTIALIAS)
|
|
|
|
# Apply shear
|
|
transform = numpy.matrix(numpy.identity(3))
|
|
transform *= numpy.matrix("[0.75,-0.5,3;0.25,0.5,-3;0,0,1]")
|
|
transform = numpy.array(transform)[:2,:].ravel().tolist()
|
|
|
|
newimg = img.transform((24,24), Image.AFFINE, transform)
|
|
|
|
return newimg
|
|
|
|
|
|
@staticmethod
|
|
def transform_image_angle(img, angle):
|
|
"""Takes an image an shears it in arbitrary angle with the axis of
|
|
rotation being vertical.
|
|
|
|
WARNING! Don't use angle = pi/2 (or multiplies), it will return
|
|
a blank image (or maybe garbage).
|
|
|
|
NOTE: angle is in the image not in game, so for the left side of a
|
|
block angle = 30 degree.
|
|
"""
|
|
|
|
# Take the same size as trasform_image_side
|
|
img = img.resize((12,12), Image.ANTIALIAS)
|
|
|
|
# some values
|
|
cos_angle = math.cos(angle)
|
|
sin_angle = math.sin(angle)
|
|
|
|
# function_x and function_y are used to keep the result image in the
|
|
# same position, and constant_x and constant_y are the coordinates
|
|
# for the center for angle = 0.
|
|
constant_x = 6.
|
|
constant_y = 6.
|
|
function_x = 6.*(1-cos_angle)
|
|
function_y = -6*sin_angle
|
|
big_term = ( (sin_angle * (function_x + constant_x)) - cos_angle* (function_y + constant_y))/cos_angle
|
|
|
|
# The numpy array is not really used, but is helpful to
|
|
# see the matrix used for the transformation.
|
|
transform = numpy.array([[1./cos_angle, 0, -(function_x + constant_x)/cos_angle],
|
|
[-sin_angle/(cos_angle), 1., big_term ],
|
|
[0, 0, 1.]])
|
|
|
|
transform = tuple(transform[0]) + tuple(transform[1])
|
|
|
|
newimg = img.transform((24,24), Image.AFFINE, transform)
|
|
|
|
return newimg
|
|
|
|
|
|
def build_block(self, top, side):
|
|
"""From a top texture and a side texture, build a block image.
|
|
top and side should be 16x16 image objects. Returns a 24x24 image
|
|
|
|
"""
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
original_texture = top.copy()
|
|
top = self.transform_image_top(top)
|
|
|
|
if not side:
|
|
alpha_over(img, top, (0,0), top)
|
|
return img
|
|
|
|
side = self.transform_image_side(side)
|
|
otherside = 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 = 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)
|
|
|
|
alpha_over(img, top, (0,0), top)
|
|
alpha_over(img, side, (0,6), side)
|
|
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
|
|
for x,y in [(13,23), (17,21), (21,19)]:
|
|
# Copy a pixel to x,y from x-1,y
|
|
img.putpixel((x,y), img.getpixel((x-1,y)))
|
|
for x,y in [(3,4), (7,2), (11,0)]:
|
|
# Copy a pixel to x,y from x+1,y
|
|
img.putpixel((x,y), img.getpixel((x+1,y)))
|
|
|
|
return img
|
|
|
|
def build_slab_block(self, top, side, upper):
|
|
"""From a top texture and a side texture, build a slab block image.
|
|
top and side should be 16x16 image objects. Returns a 24x24 image
|
|
|
|
"""
|
|
# cut the side texture in half
|
|
mask = side.crop((0,8,16,16))
|
|
side = Image.new(side.mode, side.size, self.bgcolor)
|
|
alpha_over(side, mask,(0,0,16,8), mask)
|
|
|
|
# plain slab
|
|
top = self.transform_image_top(top)
|
|
side = self.transform_image_side(side)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
sidealpha = side.split()[3]
|
|
side = ImageEnhance.Brightness(side).enhance(0.9)
|
|
side.putalpha(sidealpha)
|
|
othersidealpha = otherside.split()[3]
|
|
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
|
|
otherside.putalpha(othersidealpha)
|
|
|
|
# upside down slab
|
|
delta = 0
|
|
if upper:
|
|
delta = 6
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, side, (0,12 - delta), side)
|
|
alpha_over(img, otherside, (12,12 - delta), otherside)
|
|
alpha_over(img, top, (0,6 - delta), top)
|
|
|
|
# Manually touch up 6 pixels that leave a gap because of how the
|
|
# shearing works out. This makes the blocks perfectly tessellate-able
|
|
if upper:
|
|
for x,y in [(3,4), (7,2), (11,0)]:
|
|
# Copy a pixel to x,y from x+1,y
|
|
img.putpixel((x,y), img.getpixel((x+1,y)))
|
|
for x,y in [(13,17), (17,15), (21,13)]:
|
|
# Copy a pixel to x,y from x-1,y
|
|
img.putpixel((x,y), img.getpixel((x-1,y)))
|
|
else:
|
|
for x,y in [(3,10), (7,8), (11,6)]:
|
|
# Copy a pixel to x,y from x+1,y
|
|
img.putpixel((x,y), img.getpixel((x+1,y)))
|
|
for x,y in [(13,23), (17,21), (21,19)]:
|
|
# Copy a pixel to x,y from x-1,y
|
|
img.putpixel((x,y), img.getpixel((x-1,y)))
|
|
|
|
return img
|
|
|
|
def build_full_block(self, 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.
|
|
|
|
side1 is in the -y face of the cube (top left, east)
|
|
side2 is in the +x (top right, south)
|
|
side3 is in the -x (bottom left, north)
|
|
side4 is in the +y (bottom right, west)
|
|
|
|
A non transparent block uses top, side 3 and side 4.
|
|
|
|
If top is a tuple then first item is the top image and the second
|
|
item is an increment (integer) from 0 to 16 (pixels in the
|
|
original minecraft texture). This increment will be used to crop the
|
|
side images and to paste the top image increment pixels lower, so if
|
|
you use an increment of 8, it will draw a half-block.
|
|
|
|
NOTE: this method uses the bottom of the texture image (as done in
|
|
minecraft with beds and cakes)
|
|
|
|
"""
|
|
|
|
increment = 0
|
|
if isinstance(top, tuple):
|
|
increment = int(round((top[1] / 16.)*12.)) # range increment in the block height in pixels (half texture size)
|
|
crop_height = increment
|
|
top = top[0]
|
|
if side1 is not None:
|
|
side1 = side1.copy()
|
|
ImageDraw.Draw(side1).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side2 is not None:
|
|
side2 = side2.copy()
|
|
ImageDraw.Draw(side2).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side3 is not None:
|
|
side3 = side3.copy()
|
|
ImageDraw.Draw(side3).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
if side4 is not None:
|
|
side4 = side4.copy()
|
|
ImageDraw.Draw(side4).rectangle((0, 0,16,crop_height),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
# first back sides
|
|
if side1 is not None :
|
|
side1 = self.transform_image_side(side1)
|
|
side1 = side1.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Darken this side.
|
|
sidealpha = side1.split()[3]
|
|
side1 = ImageEnhance.Brightness(side1).enhance(0.9)
|
|
side1.putalpha(sidealpha)
|
|
|
|
alpha_over(img, side1, (0,0), side1)
|
|
|
|
|
|
if side2 is not None :
|
|
side2 = self.transform_image_side(side2)
|
|
|
|
# Darken this side.
|
|
sidealpha2 = side2.split()[3]
|
|
side2 = ImageEnhance.Brightness(side2).enhance(0.8)
|
|
side2.putalpha(sidealpha2)
|
|
|
|
alpha_over(img, side2, (12,0), side2)
|
|
|
|
if bottom is not None :
|
|
bottom = self.transform_image_top(bottom)
|
|
alpha_over(img, bottom, (0,12), bottom)
|
|
|
|
# front sides
|
|
if side3 is not None :
|
|
side3 = self.transform_image_side(side3)
|
|
|
|
# Darken this side
|
|
sidealpha = side3.split()[3]
|
|
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
|
|
side3.putalpha(sidealpha)
|
|
|
|
alpha_over(img, side3, (0,6), side3)
|
|
|
|
if side4 is not None :
|
|
side4 = self.transform_image_side(side4)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Darken this side
|
|
sidealpha = side4.split()[3]
|
|
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
|
|
side4.putalpha(sidealpha)
|
|
|
|
alpha_over(img, side4, (12,6), side4)
|
|
|
|
if top is not None :
|
|
top = self.transform_image_top(top)
|
|
alpha_over(img, top, (0, increment), top)
|
|
|
|
# Manually touch up 6 pixels that leave a gap because of how the
|
|
# shearing works out. This makes the blocks perfectly tessellate-able
|
|
for x,y in [(13,23), (17,21), (21,19)]:
|
|
# Copy a pixel to x,y from x-1,y
|
|
img.putpixel((x,y), img.getpixel((x-1,y)))
|
|
for x,y in [(3,4), (7,2), (11,0)]:
|
|
# Copy a pixel to x,y from x+1,y
|
|
img.putpixel((x,y), img.getpixel((x+1,y)))
|
|
|
|
return img
|
|
|
|
def build_axis_block(self, top, side, data):
|
|
"""
|
|
Build an block with Axis property.
|
|
data = {'y': 0, 'x': 1, 'z': 2}[axis]
|
|
"""
|
|
def draw_x():
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
|
|
def draw_y():
|
|
return self.build_full_block(side, None, None, side.rotate(270), top)
|
|
|
|
draw_funcs = [draw_x, draw_y]
|
|
|
|
if data == 0: # up
|
|
return self.build_block(top, side)
|
|
elif data == 1: # x
|
|
return draw_funcs[(self.rotation + 0) % len(draw_funcs)]()
|
|
elif data == 2: # y
|
|
return draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
|
|
def build_sprite(self, side):
|
|
"""From a side texture, create a sprite-like texture such as those used
|
|
for spiderwebs or flowers."""
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
side = self.transform_image_side(side)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
alpha_over(img, side, (6,3), side)
|
|
alpha_over(img, otherside, (6,3), otherside)
|
|
return img
|
|
|
|
def build_billboard(self, 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), self.bgcolor)
|
|
|
|
front = tex.resize((14, 12), Image.ANTIALIAS)
|
|
alpha_over(img, front, (5,9))
|
|
return img
|
|
|
|
def generate_opaque_mask(self, img):
|
|
""" Takes the alpha channel of the image and generates a mask
|
|
(used for lighting the block) that deprecates values of alpha
|
|
smallers than 50, and sets every other value to 255. """
|
|
|
|
alpha = img.split()[3]
|
|
return alpha.point(lambda a: int(min(a, 25.5) * 10))
|
|
|
|
def tint_texture(self, 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(self, img):
|
|
""" This takes an image and returns the needed tuple for the
|
|
blockmap array."""
|
|
if img is None:
|
|
return None
|
|
return (img, self.generate_opaque_mask(img))
|
|
|
|
##
|
|
## The other big one: @material and associated framework
|
|
##
|
|
|
|
|
|
# the material registration decorator
|
|
def material(blockid=[], data=[0], **kwargs):
|
|
# mapping from property name to the set to store them in
|
|
properties = {"transparent" : transparent_blocks, "solid" : solid_blocks, "fluid" : fluid_blocks, "nospawn" : nospawn_blocks, "nodata" : nodata_blocks}
|
|
|
|
# make sure blockid and data are iterable
|
|
try:
|
|
iter(blockid)
|
|
except Exception:
|
|
blockid = [blockid,]
|
|
try:
|
|
iter(data)
|
|
except Exception:
|
|
data = [data,]
|
|
|
|
def inner_material(func):
|
|
global blockmap_generators
|
|
global max_data, max_blockid
|
|
|
|
# create a wrapper function with a known signature
|
|
@functools.wraps(func)
|
|
def func_wrapper(texobj, blockid, data):
|
|
return func(texobj, blockid, data)
|
|
|
|
used_datas.update(data)
|
|
if max(data) >= max_data:
|
|
max_data = max(data) + 1
|
|
|
|
for block in blockid:
|
|
# set the property sets appropriately
|
|
known_blocks.update([block])
|
|
if block >= max_blockid:
|
|
max_blockid = block + 1
|
|
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, nodata
|
|
def block(blockid=[], top_image=None, side_image=None, **kwargs):
|
|
new_kwargs = {'solid' : True, 'nodata' : True}
|
|
new_kwargs.update(kwargs)
|
|
|
|
if top_image is None:
|
|
raise ValueError("top_image was not provided")
|
|
|
|
if side_image is None:
|
|
side_image = top_image
|
|
|
|
@material(blockid=blockid, **new_kwargs)
|
|
def inner_block(self, unused_id, unused_data):
|
|
return self.build_block(self.load_image_texture(top_image), self.load_image_texture(side_image))
|
|
return inner_block
|
|
|
|
# shortcut function for sprite blocks, defaults to transparent, nodata
|
|
def sprite(blockid=[], imagename=None, **kwargs):
|
|
new_kwargs = {'transparent' : True, 'nodata' : True}
|
|
new_kwargs.update(kwargs)
|
|
|
|
if imagename is None:
|
|
raise ValueError("imagename was not provided")
|
|
|
|
@material(blockid=blockid, **new_kwargs)
|
|
def inner_sprite(self, unused_id, unused_data):
|
|
return self.build_sprite(self.load_image_texture(imagename))
|
|
return inner_sprite
|
|
|
|
# shortcut function for billboard blocks, defaults to transparent, nodata
|
|
def billboard(blockid=[], imagename=None, **kwargs):
|
|
new_kwargs = {'transparent' : True, 'nodata' : True}
|
|
new_kwargs.update(kwargs)
|
|
|
|
if imagename is None:
|
|
raise ValueError("imagename was not provided")
|
|
|
|
@material(blockid=blockid, **new_kwargs)
|
|
def inner_billboard(self, unused_id, unused_data):
|
|
return self.build_billboard(self.load_image_texture(imagename))
|
|
return inner_billboard
|
|
|
|
##
|
|
## and finally: actual texture definitions
|
|
##
|
|
|
|
# stone
|
|
@material(blockid=1, data=list(range(7)), solid=True)
|
|
def stone(self, blockid, data):
|
|
if data == 0: # regular old-school stone
|
|
img = self.load_image_texture("assets/minecraft/textures/block/stone.png")
|
|
elif data == 1: # granite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/granite.png")
|
|
elif data == 2: # polished granite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/polished_granite.png")
|
|
elif data == 3: # diorite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/diorite.png")
|
|
elif data == 4: # polished diorite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/polished_diorite.png")
|
|
elif data == 5: # andesite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/andesite.png")
|
|
elif data == 6: # polished andesite
|
|
img = self.load_image_texture("assets/minecraft/textures/block/polished_andesite.png")
|
|
return self.build_block(img, img)
|
|
|
|
@material(blockid=2, data=list(range(11))+[0x10,], solid=True)
|
|
def grass(self, blockid, data):
|
|
# 0x10 bit means SNOW
|
|
side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_side.png")
|
|
if data & 0x10:
|
|
side_img = self.load_image_texture("assets/minecraft/textures/block/grass_block_snow.png")
|
|
img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/grass_block_top.png"), side_img)
|
|
if not data & 0x10:
|
|
alpha_over(img, self.biome_grass_texture, (0, 0), self.biome_grass_texture)
|
|
return img
|
|
|
|
|
|
# dirt
|
|
@material(blockid=3, data=list(range(3)), solid=True)
|
|
def dirt_blocks(self, blockid, data):
|
|
texture_map = [{"top": "dirt", "side": "dirt"}, # Normal
|
|
{"top": "coarse_dirt", "side": "coarse_dirt"}, # Coarse
|
|
{"top": "podzol_top", "side": "podzol_side"}] # Podzol
|
|
top_img = self.load_image_texture("assets/minecraft/textures/block/%s.png"
|
|
% texture_map[data]["top"])
|
|
side_img = self.load_image_texture("assets/minecraft/textures/block/%s.png"
|
|
% texture_map[data]["side"])
|
|
|
|
return self.build_block(top_img, side_img)
|
|
|
|
|
|
# cobblestone
|
|
block(blockid=4, top_image="assets/minecraft/textures/block/cobblestone.png")
|
|
|
|
# wooden planks
|
|
@material(blockid=5, data=list(range(8)), solid=True)
|
|
def wooden_planks(self, blockid, data):
|
|
if data == 0: # normal
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"), self.load_image_texture("assets/minecraft/textures/block/oak_planks.png"))
|
|
if data == 1: # pine
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png"),self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png"))
|
|
if data == 2: # birch
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/birch_planks.png"),self.load_image_texture("assets/minecraft/textures/block/birch_planks.png"))
|
|
if data == 3: # jungle wood
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png"),self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png"))
|
|
if data == 4: # acacia
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png"),self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png"))
|
|
if data == 5: # dark oak
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"),self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png"))
|
|
if data == 6: # crimson
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png"),self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png"))
|
|
if data == 7: # warped
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/warped_planks.png"),self.load_image_texture("assets/minecraft/textures/block/warped_planks.png"))
|
|
|
|
@material(blockid=6, data=list(range(16)), transparent=True)
|
|
def saplings(self, blockid, data):
|
|
# usual saplings
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/oak_sapling.png")
|
|
|
|
if data & 0x3 == 1: # spruce sapling
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/spruce_sapling.png")
|
|
elif data & 0x3 == 2: # birch sapling
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/birch_sapling.png")
|
|
elif data & 0x3 == 3: # jungle sapling
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/jungle_sapling.png")
|
|
elif data & 0x3 == 4: # acacia sapling
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/acacia_sapling.png")
|
|
elif data & 0x3 == 5: # dark oak/roofed oak/big oak sapling
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/dark_oak_sapling.png")
|
|
return self.build_sprite(tex)
|
|
|
|
sprite(blockid=11385, imagename="assets/minecraft/textures/block/oak_sapling.png")
|
|
sprite(blockid=11386, imagename="assets/minecraft/textures/block/spruce_sapling.png")
|
|
sprite(blockid=11387, imagename="assets/minecraft/textures/block/birch_sapling.png")
|
|
sprite(blockid=11388, imagename="assets/minecraft/textures/block/jungle_sapling.png")
|
|
sprite(blockid=11389, imagename="assets/minecraft/textures/block/acacia_sapling.png")
|
|
sprite(blockid=11390, imagename="assets/minecraft/textures/block/dark_oak_sapling.png")
|
|
sprite(blockid=11413, imagename="assets/minecraft/textures/block/bamboo_stage0.png")
|
|
|
|
# bedrock
|
|
block(blockid=7, top_image="assets/minecraft/textures/block/bedrock.png")
|
|
|
|
# water, glass, and ice (no inner surfaces)
|
|
# uses pseudo-ancildata found in iterate.c
|
|
@material(blockid=[8, 9, 20, 79, 95], data=list(range(512)), fluid=(8, 9), transparent=True, nospawn=True, solid=(79, 20, 95))
|
|
def no_inner_surfaces(self, blockid, data):
|
|
if blockid == 8 or blockid == 9:
|
|
texture = self.load_water()
|
|
elif blockid == 20:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/glass.png")
|
|
elif blockid == 95:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_stained_glass.png" % color_map[data & 0x0f])
|
|
else:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/ice.png")
|
|
|
|
# now that we've used the lower 4 bits to get color, shift down to get the 5 bits that encode face hiding
|
|
if not (blockid == 8 or blockid == 9): # water doesn't have a shifted pseudodata
|
|
data = data >> 4
|
|
|
|
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 = self.build_full_block(top,None,None,side3,side4)
|
|
return img
|
|
|
|
@material(blockid=[10, 11], data=list(range(16)), fluid=True, transparent=False, nospawn=True)
|
|
def lava(self, blockid, data):
|
|
lavatex = self.load_lava()
|
|
return self.build_block(lavatex, lavatex)
|
|
|
|
# sand
|
|
@material(blockid=12, data=list(range(2)), solid=True)
|
|
def sand_blocks(self, blockid, data):
|
|
if data == 0: # normal
|
|
img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/sand.png"), self.load_image_texture("assets/minecraft/textures/block/sand.png"))
|
|
if data == 1: # red
|
|
img = self.build_block(self.load_image_texture("assets/minecraft/textures/block/red_sand.png"), self.load_image_texture("assets/minecraft/textures/block/red_sand.png"))
|
|
return img
|
|
|
|
# gravel
|
|
block(blockid=13, top_image="assets/minecraft/textures/block/gravel.png")
|
|
# gold ore
|
|
block(blockid=14, top_image="assets/minecraft/textures/block/gold_ore.png")
|
|
# iron ore
|
|
block(blockid=15, top_image="assets/minecraft/textures/block/iron_ore.png")
|
|
# coal ore
|
|
block(blockid=16, top_image="assets/minecraft/textures/block/coal_ore.png")
|
|
|
|
@material(blockid=[17, 162, 11306, 11307, 11308, 11309, 11310, 11311, 1008, 1009, 1126],
|
|
data=list(range(12)), solid=True)
|
|
def wood(self, blockid, data):
|
|
# extract orientation and wood type frorm data bits
|
|
wood_type = data & 3
|
|
wood_orientation = data & 12
|
|
if self.rotation == 1:
|
|
if wood_orientation == 4: wood_orientation = 8
|
|
elif wood_orientation == 8: wood_orientation = 4
|
|
elif self.rotation == 3:
|
|
if wood_orientation == 4: wood_orientation = 8
|
|
elif wood_orientation == 8: wood_orientation = 4
|
|
|
|
# dictionary of blockid : { wood_type : (top, side) }
|
|
wood_tex = {
|
|
17: {
|
|
0: ("oak_log_top.png", "oak_log.png"),
|
|
1: ("spruce_log_top.png", "spruce_log.png"),
|
|
2: ("birch_log_top.png", "birch_log.png"),
|
|
3: ("jungle_log_top.png", "jungle_log.png"),
|
|
},
|
|
162: {
|
|
0: ("acacia_log_top.png", "acacia_log.png"),
|
|
1: ("dark_oak_log_top.png", "dark_oak_log.png"),
|
|
},
|
|
11306: {
|
|
0: ("stripped_oak_log_top.png", "stripped_oak_log.png"),
|
|
1: ("stripped_spruce_log_top.png", "stripped_spruce_log.png"),
|
|
2: ("stripped_birch_log_top.png", "stripped_birch_log.png"),
|
|
3: ("stripped_jungle_log_top.png", "stripped_jungle_log.png"),
|
|
},
|
|
11307: {
|
|
0: ("stripped_acacia_log_top.png", "stripped_acacia_log.png"),
|
|
1: ("stripped_dark_oak_log_top.png", "stripped_dark_oak_log.png"),
|
|
},
|
|
11308: {
|
|
0: ("oak_log.png", None),
|
|
1: ("spruce_log.png", None),
|
|
2: ("birch_log.png", None),
|
|
3: ("jungle_log.png", None),
|
|
},
|
|
11309: {
|
|
0: ("acacia_log.png", None),
|
|
1: ("dark_oak_log.png", None),
|
|
},
|
|
11310: {
|
|
0: ("stripped_oak_log.png", None),
|
|
1: ("stripped_spruce_log.png", None),
|
|
2: ("stripped_birch_log.png", None),
|
|
3: ("stripped_jungle_log.png", None),
|
|
},
|
|
11311: {
|
|
0: ("stripped_acacia_log.png", None),
|
|
1: ("stripped_dark_oak_log.png", None),
|
|
},
|
|
1008: {
|
|
0: ("warped_stem_top.png", "warped_stem.png"),
|
|
1: ("warped_stem_top.png", "stripped_warped_stem.png"),
|
|
2: ("crimson_stem_top.png", "crimson_stem.png"),
|
|
3: ("crimson_stem_top.png", "stripped_crimson_stem.png"),
|
|
},
|
|
1009: {
|
|
0: ("warped_stem.png", None),
|
|
1: ("stripped_warped_stem.png", None),
|
|
2: ("crimson_stem.png", None),
|
|
3: ("stripped_crimson_stem.png", None),
|
|
},
|
|
1126: {
|
|
0: ("mangrove_log_top.png", "mangrove_log.png"),
|
|
1: ("stripped_mangrove_log_top.png", "stripped_mangrove_log.png"),
|
|
},
|
|
}
|
|
|
|
top_f, side_f = wood_tex[blockid].get(wood_type, wood_tex[blockid][0])
|
|
if not side_f:
|
|
side_f = top_f
|
|
top = self.load_image_texture(BLOCKTEX + top_f)
|
|
side = self.load_image_texture(BLOCKTEX + side_f)
|
|
|
|
# choose orientation and paste textures
|
|
if wood_orientation == 0:
|
|
return self.build_block(top, side)
|
|
elif wood_orientation == 4: # east-west orientation
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
elif wood_orientation == 8: # north-south orientation
|
|
return self.build_full_block(side, None, None, side.rotate(270), top)
|
|
|
|
@material(blockid=[18, 161], data=list(range(16)), transparent=True, solid=True)
|
|
def leaves(self, blockid, data):
|
|
# mask out the bits 4 and 8
|
|
# they are used for player placed and check-for-decay blocks
|
|
data = data & 0x7
|
|
t = self.load_image_texture("assets/minecraft/textures/block/oak_leaves.png")
|
|
if (blockid, data) == (18, 1): # pine!
|
|
t = self.load_image_texture("assets/minecraft/textures/block/spruce_leaves.png")
|
|
elif (blockid, data) == (18, 2): # birth tree
|
|
t = self.load_image_texture("assets/minecraft/textures/block/birch_leaves.png")
|
|
elif (blockid, data) == (18, 3): # jungle tree
|
|
t = self.load_image_texture("assets/minecraft/textures/block/jungle_leaves.png")
|
|
elif (blockid, data) == (161, 4): # acacia tree
|
|
t = self.load_image_texture("assets/minecraft/textures/block/acacia_leaves.png")
|
|
elif (blockid, data) == (161, 5):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/dark_oak_leaves.png")
|
|
elif (blockid, data) == (18, 6):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/flowering_azalea_leaves.png")
|
|
elif (blockid, data) == (18, 7):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/azalea_leaves.png")
|
|
elif (blockid, data) == (18, 8):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/mangrove_leaves.png")
|
|
return self.build_block(t, t)
|
|
|
|
# sponge
|
|
block(blockid=19, top_image="assets/minecraft/textures/block/sponge.png")
|
|
# lapis lazuli ore
|
|
block(blockid=21, top_image="assets/minecraft/textures/block/lapis_ore.png")
|
|
# lapis lazuli block
|
|
block(blockid=22, top_image="assets/minecraft/textures/block/lapis_block.png")
|
|
|
|
|
|
# dispenser, dropper, furnace, blast furnace, and smoker
|
|
@material(blockid=[23, 61, 158, 11362, 11364], data=list(range(14)), solid=True)
|
|
def furnaces(self, blockid, data):
|
|
# first, do the rotation if needed
|
|
# Masked as bit 4 indicates whether the block is lit/triggered or not
|
|
if self.rotation in [1, 2, 3] and data & 0b111 in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = data & 0b1000 | rotation_map[self.rotation][data & 0b111]
|
|
|
|
# Rotation angles for top texture using data & 0b111 as an index
|
|
top_rotation_map = [0, 0, 180, 0, 270, 90, 0, 0]
|
|
|
|
# Dispenser
|
|
texture_map = {23: {'top': 'furnace_top', 'side': 'furnace_side',
|
|
'front': 'dispenser_front', 'top_vert': 'dispenser_front_vertical'},
|
|
# Furnace
|
|
61: {'top': 'furnace_top', 'side': 'furnace_side',
|
|
'front': 'furnace_front', 'front_on': 'furnace_front_on'},
|
|
# Dropper
|
|
158: {'top': 'furnace_top', 'side': 'furnace_side',
|
|
'front': 'dropper_front', 'top_vert': 'dropper_front_vertical'},
|
|
# Blast furance
|
|
11362: {'top': 'blast_furnace_top', 'side': 'blast_furnace_side',
|
|
'front': 'blast_furnace_front', 'front_on': 'blast_furnace_front_on'},
|
|
# Smoker
|
|
11364: {'top': 'smoker_top', 'side': 'smoker_side',
|
|
'front': 'smoker_front', 'front_on': 'smoker_front_on'}}
|
|
|
|
if data & 0b111 in [0, 1] and 'top_vert' in texture_map[blockid]:
|
|
# Block has a special top texture when it faces up/down
|
|
# This also affects which texture is used for the sides/front
|
|
top_name = 'top_vert' if data & 0b111 == 1 else 'top'
|
|
side_name = 'top'
|
|
front_name = 'top'
|
|
else:
|
|
top_name = 'top'
|
|
side_name = 'side'
|
|
# Use block's lit/on front texture if it is defined & bit 4 is set
|
|
# Note: Some front_on texture images have multiple frames,
|
|
# but load_image_texture() crops this appropriately
|
|
# as long as the image width is 16px
|
|
if data & 0b1000 == 8 and 'front_on' in texture_map[blockid]:
|
|
front_name = 'front_on'
|
|
else:
|
|
front_name = 'front'
|
|
|
|
top = self.load_image_texture("assets/minecraft/textures/block/%s.png" %
|
|
texture_map[blockid][top_name]).copy()
|
|
top = top.rotate(top_rotation_map[data & 0b111])
|
|
side = self.load_image_texture("assets/minecraft/textures/block/%s.png" %
|
|
texture_map[blockid][side_name])
|
|
front = self.load_image_texture("assets/minecraft/textures/block/%s.png" %
|
|
texture_map[blockid][front_name])
|
|
|
|
if data & 0b111 == 3: # pointing west
|
|
return self.build_full_block(top, None, None, side, front)
|
|
elif data & 0b111 == 4: # pointing north
|
|
return self.build_full_block(top, None, None, front, side)
|
|
else: # in any other direction the front can't be seen
|
|
return self.build_full_block(top, None, None, side, side)
|
|
|
|
# sandstone
|
|
@material(blockid=24, data=list(range(3)), solid=True)
|
|
def sandstone(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png")
|
|
if data == 0: # normal
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/sandstone.png"))
|
|
if data == 1: # hieroglyphic
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/chiseled_sandstone.png"))
|
|
if data == 2: # soft
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_sandstone.png"))
|
|
|
|
# red sandstone
|
|
@material(blockid=179, data=list(range(3)), solid=True)
|
|
def sandstone(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png")
|
|
if data == 0: # normal
|
|
side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone.png")
|
|
return self.build_full_block(top, None, None, side, side, self.load_image_texture("assets/minecraft/textures/block/red_sandstone_bottom.png") )
|
|
if data == 1: # hieroglyphic
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/chiseled_red_sandstone.png"))
|
|
if data == 2: # soft
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/cut_red_sandstone.png"))
|
|
|
|
# note block
|
|
block(blockid=25, top_image="assets/minecraft/textures/block/note_block.png")
|
|
|
|
# Bed
|
|
@material(blockid=26, data=list(range(256)), transparent=True, nospawn=True)
|
|
def bed(self, blockid, data):
|
|
# Bits 1-2 Rotation
|
|
# Bit 3 Occupancy, no impact on appearance
|
|
# Bit 4 Foot/Head of bed (0 = foot, 1 = head)
|
|
# Bits 5-8 Color
|
|
|
|
# first get rotation done
|
|
# Masked to not clobber block head/foot & color info
|
|
data = data & 0b11111100 | ((self.rotation + (data & 0b11)) % 4)
|
|
|
|
bed_texture = self.load_image("assets/minecraft/textures/entity/bed/%s.png" % color_map[data >> 4])
|
|
increment = 8
|
|
left_face = None
|
|
right_face = None
|
|
top_face = None
|
|
if data & 0x8 == 0x8: # head of the bed
|
|
top = bed_texture.copy().crop((6, 6, 22, 22))
|
|
|
|
# Composing the side
|
|
side = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
side_part1 = bed_texture.copy().crop((0, 6, 6, 22)).rotate(90, expand=True)
|
|
# foot of the bed
|
|
side_part2 = bed_texture.copy().crop((53, 3, 56, 6))
|
|
side_part2_f = side_part2.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(side, side_part1, (0, 7), side_part1)
|
|
alpha_over(side, side_part2, (0, 13), side_part2)
|
|
|
|
end = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
end_part = bed_texture.copy().crop((6, 0, 22, 6)).rotate(180)
|
|
alpha_over(end, end_part, (0, 7), end_part)
|
|
alpha_over(end, side_part2, (0, 13), side_part2)
|
|
alpha_over(end, side_part2_f, (13, 13), side_part2_f)
|
|
if data & 0x03 == 0x00: # South
|
|
top_face = top.rotate(180)
|
|
left_face = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
right_face = end
|
|
elif data & 0x03 == 0x01: # West
|
|
top_face = top.rotate(90)
|
|
left_face = end
|
|
right_face = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
elif data & 0x03 == 0x02: # North
|
|
top_face = top
|
|
left_face = side
|
|
elif data & 0x03 == 0x03: # East
|
|
top_face = top.rotate(270)
|
|
right_face = side
|
|
|
|
else: # foot of the bed
|
|
top = bed_texture.copy().crop((6, 28, 22, 44))
|
|
side = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
side_part1 = bed_texture.copy().crop((0, 28, 6, 44)).rotate(90, expand=True)
|
|
side_part2 = bed_texture.copy().crop((53, 3, 56, 6))
|
|
side_part2_f = side_part2.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(side, side_part1, (0, 7), side_part1)
|
|
alpha_over(side, side_part2, (13, 13), side_part2)
|
|
|
|
end = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
end_part = bed_texture.copy().crop((22, 22, 38, 28)).rotate(180)
|
|
alpha_over(end, end_part, (0, 7), end_part)
|
|
alpha_over(end, side_part2, (0, 13), side_part2)
|
|
alpha_over(end, side_part2_f, (13, 13), side_part2_f)
|
|
if data & 0x03 == 0x00: # South
|
|
top_face = top.rotate(180)
|
|
left_face = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
elif data & 0x03 == 0x01: # West
|
|
top_face = top.rotate(90)
|
|
right_face = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
elif data & 0x03 == 0x02: # North
|
|
top_face = top
|
|
left_face = side
|
|
right_face = end
|
|
elif data & 0x03 == 0x03: # East
|
|
top_face = top.rotate(270)
|
|
left_face = end
|
|
right_face = side
|
|
|
|
top_face = (top_face, increment)
|
|
return self.build_full_block(top_face, None, None, left_face, right_face)
|
|
|
|
# powered, detector, activator and normal rails
|
|
@material(blockid=[27, 28, 66, 157], data=list(range(14)), transparent=True)
|
|
def rails(self, blockid, data):
|
|
# first, do rotation
|
|
# Masked to not clobber powered rail on/off info
|
|
# Ascending and flat straight
|
|
if self.rotation == 1:
|
|
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 self.rotation == 2:
|
|
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 self.rotation == 3:
|
|
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 self.rotation == 1:
|
|
if data == 6: data = 7
|
|
elif data == 7: data = 8
|
|
elif data == 8: data = 6
|
|
elif data == 9: data = 9
|
|
elif self.rotation == 2:
|
|
if data == 6: data = 8
|
|
elif data == 7: data = 9
|
|
elif data == 8: data = 6
|
|
elif data == 9: data = 7
|
|
elif self.rotation == 3:
|
|
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), self.bgcolor)
|
|
|
|
if blockid == 27: # powered rail
|
|
if data & 0x8 == 0: # unpowered
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/powered_rail.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png") # they don't exist but make the code
|
|
# much simplier
|
|
elif data & 0x8 == 0x8: # powered
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/powered_rail_on.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png") # leave corners for code simplicity
|
|
# filter the 'powered' bit
|
|
data = data & 0x7
|
|
|
|
elif blockid == 28: # detector rail
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/detector_rail.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png") # leave corners for code simplicity
|
|
|
|
elif blockid == 66: # normal rail
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/rail.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png")
|
|
|
|
elif blockid == 157: # activator rail
|
|
if data & 0x8 == 0: # unpowered
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/activator_rail.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png") # they don't exist but make the code
|
|
# much simplier
|
|
elif data & 0x8 == 0x8: # powered
|
|
raw_straight = self.load_image_texture("assets/minecraft/textures/block/activator_rail_on.png")
|
|
raw_corner = self.load_image_texture("assets/minecraft/textures/block/rail_corner.png") # leave corners for code simplicity
|
|
# filter the 'powered' bit
|
|
data = data & 0x7
|
|
|
|
## use transform_image to scale and shear
|
|
if data == 0:
|
|
track = self.transform_image_top(raw_straight)
|
|
alpha_over(img, track, (0,12), track)
|
|
elif data == 6:
|
|
track = self.transform_image_top(raw_corner)
|
|
alpha_over(img, track, (0,12), track)
|
|
elif data == 7:
|
|
track = self.transform_image_top(raw_corner.rotate(270))
|
|
alpha_over(img, track, (0,12), track)
|
|
elif data == 8:
|
|
# flip
|
|
track = self.transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90))
|
|
alpha_over(img, track, (0,12), track)
|
|
elif data == 9:
|
|
track = self.transform_image_top(raw_corner.transpose(Image.FLIP_TOP_BOTTOM))
|
|
alpha_over(img, track, (0,12), track)
|
|
elif data == 1:
|
|
track = self.transform_image_top(raw_straight.rotate(90))
|
|
alpha_over(img, track, (0,12), track)
|
|
|
|
#slopes
|
|
elif data == 2: # slope going up in +x direction
|
|
track = self.transform_image_slope(raw_straight)
|
|
track = track.transpose(Image.FLIP_LEFT_RIGHT)
|
|
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 = self.transform_image_slope(raw_straight)
|
|
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(self, blockid, data):
|
|
# first, rotation
|
|
# Masked to not clobber block head/foot info
|
|
if self.rotation in [1, 2, 3] and (data & 0b111) in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = (data & 0b1000) | rotation_map[self.rotation][data & 0b111]
|
|
|
|
if blockid == 29: # sticky
|
|
piston_t = self.load_image_texture("assets/minecraft/textures/block/piston_top_sticky.png").copy()
|
|
else: # normal
|
|
piston_t = self.load_image_texture("assets/minecraft/textures/block/piston_top.png").copy()
|
|
|
|
# other textures
|
|
side_t = self.load_image_texture("assets/minecraft/textures/block/piston_side.png").copy()
|
|
back_t = self.load_image_texture("assets/minecraft/textures/block/piston_bottom.png").copy()
|
|
interior_t = self.load_image_texture("assets/minecraft/textures/block/piston_inner.png").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 = self.build_full_block(back_t, None, None, side_t, side_t)
|
|
elif data & 0x07 == 0x1: # up
|
|
img = self.build_full_block((interior_t, 4), None, None, side_t, side_t)
|
|
elif data & 0x07 == 0x2: # north
|
|
img = self.build_full_block(side_t, None, None, side_t.rotate(90), back_t)
|
|
elif data & 0x07 == 0x3: # south
|
|
img = self.build_full_block(side_t.rotate(180), None, None, side_t.rotate(270), None)
|
|
temp = self.transform_image_side(interior_t)
|
|
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, temp, (9, 4), temp)
|
|
elif data & 0x07 == 0x4: # west
|
|
img = self.build_full_block(side_t.rotate(90), None, None, None, side_t.rotate(270))
|
|
temp = self.transform_image_side(interior_t)
|
|
alpha_over(img, temp, (3, 4), temp)
|
|
elif data & 0x07 == 0x5: # east
|
|
img = self.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 = self.build_full_block(back_t, None, None, side_t, side_t)
|
|
elif data & 0x07 == 0x1: # up
|
|
img = self.build_full_block(piston_t, None, None, side_t, side_t)
|
|
elif data & 0x07 == 0x2: # north
|
|
img = self.build_full_block(side_t, None, None, side_t.rotate(90), back_t)
|
|
elif data & 0x07 == 0x3: # south
|
|
img = self.build_full_block(side_t.rotate(180), None, None, side_t.rotate(270), piston_t)
|
|
elif data & 0x07 == 0x4: # west
|
|
img = self.build_full_block(side_t.rotate(90), None, None, piston_t, side_t.rotate(270))
|
|
elif data & 0x07 == 0x5: # east
|
|
img = self.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(self, blockid, data):
|
|
# first, rotation
|
|
# Masked to not clobber block head/foot info
|
|
if self.rotation in [1, 2, 3] and (data & 0b111) in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = (data & 0b1000) | rotation_map[self.rotation][data & 0b111]
|
|
|
|
if data & 0x8 == 0x8: # sticky
|
|
piston_t = self.load_image_texture("assets/minecraft/textures/block/piston_top_sticky.png").copy()
|
|
else: # normal
|
|
piston_t = self.load_image_texture("assets/minecraft/textures/block/piston_top.png").copy()
|
|
|
|
# other textures
|
|
side_t = self.load_image_texture("assets/minecraft/textures/block/piston_side.png").copy()
|
|
back_t = self.load_image_texture("assets/minecraft/textures/block/piston_top.png").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), self.bgcolor)
|
|
temp = self.transform_image_side(side_t)
|
|
alpha_over(h_stick, temp, (1, 7), temp)
|
|
temp = self.transform_image_top(side_t.rotate(90))
|
|
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), self.bgcolor)
|
|
temp = self.transform_image_side(side_t.rotate(90))
|
|
alpha_over(v_stick, temp, (12, 6), temp)
|
|
temp = temp.transpose(Image.FLIP_LEFT_RIGHT)
|
|
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 = self.build_full_block((back_t, 12), None, None, side_t, side_t)
|
|
alpha_over(img, v_stick, (0, -3), v_stick)
|
|
elif data & 0x07 == 0x1: # up
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
img2 = self.build_full_block(piston_t, None, None, side_t, side_t)
|
|
alpha_over(img, v_stick, (0, 4), v_stick)
|
|
alpha_over(img, img2, (0, 0), img2)
|
|
elif data & 0x07 == 0x2: # north
|
|
img = self.build_full_block(side_t, None, None, side_t.rotate(90), None)
|
|
temp = self.transform_image_side(back_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, temp, (2, 2), temp)
|
|
alpha_over(img, h_stick, (6, 3), h_stick)
|
|
elif data & 0x07 == 0x3: # south
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
img2 = self.build_full_block(side_t.rotate(180), None, None, side_t.rotate(270), piston_t)
|
|
alpha_over(img, h_stick, (0, 0), h_stick)
|
|
alpha_over(img, img2, (0, 0), img2)
|
|
elif data & 0x07 == 0x4: # west
|
|
img = self.build_full_block(side_t.rotate(90), None, None, piston_t, side_t.rotate(270))
|
|
h_stick = h_stick.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, h_stick, (0, 0), h_stick)
|
|
elif data & 0x07 == 0x5: # east
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
img2 = self.build_full_block(side_t.rotate(270), None, None, None, side_t.rotate(90))
|
|
h_stick = h_stick.transpose(Image.FLIP_LEFT_RIGHT)
|
|
temp = self.transform_image_side(back_t)
|
|
alpha_over(img2, temp, (10, 2), temp)
|
|
alpha_over(img, img2, (0, 0), img2)
|
|
alpha_over(img, h_stick, (-3, 2), h_stick)
|
|
|
|
return img
|
|
|
|
|
|
# cobweb
|
|
sprite(blockid=30, imagename="assets/minecraft/textures/block/cobweb.png", nospawn=True)
|
|
|
|
@material(blockid=31, data=list(range(3)), transparent=True)
|
|
def tall_grass(self, blockid, data):
|
|
if data == 0: # dead shrub
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/dead_bush.png")
|
|
elif data == 1: # tall grass
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/grass.png")
|
|
elif data == 2: # fern
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/fern.png")
|
|
|
|
return self.build_billboard(texture)
|
|
|
|
# dead bush
|
|
billboard(blockid=32, imagename="assets/minecraft/textures/block/dead_bush.png")
|
|
|
|
@material(blockid=35, data=list(range(16)), solid=True)
|
|
def wool(self, blockid, data):
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data])
|
|
|
|
return self.build_block(texture, texture)
|
|
|
|
# dandelion
|
|
sprite(blockid=37, imagename="assets/minecraft/textures/block/dandelion.png")
|
|
|
|
# flowers
|
|
@material(blockid=38, data=list(range(13)), transparent=True)
|
|
def flower(self, blockid, data):
|
|
flower_map = ["poppy", "blue_orchid", "allium", "azure_bluet", "red_tulip", "orange_tulip",
|
|
"white_tulip", "pink_tulip", "oxeye_daisy", "dandelion", "wither_rose",
|
|
"cornflower", "lily_of_the_valley"]
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s.png" % flower_map[data])
|
|
return self.build_billboard(texture)
|
|
|
|
# brown mushroom
|
|
sprite(blockid=39, imagename="assets/minecraft/textures/block/brown_mushroom.png")
|
|
# red mushroom
|
|
sprite(blockid=40, imagename="assets/minecraft/textures/block/red_mushroom.png")
|
|
# warped fungus
|
|
sprite(blockid=1016, imagename="assets/minecraft/textures/block/warped_fungus.png")
|
|
# crimson fungus
|
|
sprite(blockid=1017, imagename="assets/minecraft/textures/block/crimson_fungus.png")
|
|
# warped roots
|
|
sprite(blockid=1018, imagename="assets/minecraft/textures/block/warped_roots.png")
|
|
# crimson roots
|
|
sprite(blockid=1019, imagename="assets/minecraft/textures/block/crimson_roots.png")
|
|
|
|
# block of gold
|
|
block(blockid=41, top_image="assets/minecraft/textures/block/gold_block.png")
|
|
# block of iron
|
|
block(blockid=42, top_image="assets/minecraft/textures/block/iron_block.png")
|
|
|
|
# double slabs and slabs
|
|
# these wooden slabs are unobtainable without cheating, they are still
|
|
# here because lots of pre-1.3 worlds use this blocks, add prismarine slabs
|
|
@material(blockid=[43, 44, 181, 182, 204, 205, 1124] + list(range(11340, 11359)) +
|
|
list(range(1027, 1030)) + list(range(1072, 1080)) + list(range(1103, 1107)),
|
|
data=list(range(16)),
|
|
transparent=[44, 182, 205, 1124] + list(range(11340, 11359)) + list(range(1027, 1030)) +
|
|
list(range(1072, 1080)) + list(range(1103, 1107)), solid=True)
|
|
def slabs(self, blockid, data):
|
|
if blockid == 44 or blockid == 182:
|
|
texture = data & 7
|
|
else: # data > 8 are special double slabs
|
|
texture = data
|
|
|
|
if blockid == 44 or blockid == 43:
|
|
if texture== 0: # stone slab
|
|
top = self.load_image_texture("assets/minecraft/textures/block/stone.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/stone.png")
|
|
elif texture== 1: # sandstone slab
|
|
top = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/sandstone.png")
|
|
elif texture== 2: # wooden slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png")
|
|
elif texture== 3: # cobblestone slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/cobblestone.png")
|
|
elif texture== 4: # brick
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/bricks.png")
|
|
elif texture== 5: # stone brick
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png")
|
|
elif texture== 6: # nether brick slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png")
|
|
elif texture== 7: #quartz
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/quartz_block_side.png")
|
|
elif texture== 8: # special stone double slab with top texture only
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/smooth_stone.png")
|
|
elif texture== 9: # special sandstone double slab with top texture only
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png")
|
|
else:
|
|
return None
|
|
|
|
elif blockid == 182: # single red sandstone slab
|
|
if texture == 0:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone.png")
|
|
else:
|
|
return None
|
|
|
|
elif blockid == 181: # double red sandstone slab
|
|
if texture == 0: # red sandstone
|
|
top = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone.png")
|
|
elif texture == 8: # 'full' red sandstone (smooth)
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png");
|
|
else:
|
|
return None
|
|
elif blockid == 204 or blockid == 205: # purpur slab (single=205 double=204)
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/purpur_block.png");
|
|
|
|
elif blockid == 11340: # prismarine slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/prismarine.png").copy()
|
|
elif blockid == 11341: # dark prismarine slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/dark_prismarine.png").copy()
|
|
elif blockid == 11342: # prismarine brick slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/prismarine_bricks.png").copy()
|
|
elif blockid == 11343: # andesite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/andesite.png").copy()
|
|
elif blockid == 11344: # diorite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/diorite.png").copy()
|
|
elif blockid == 11345: # granite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/granite.png").copy()
|
|
elif blockid == 11346: # polished andesite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/polished_andesite.png").copy()
|
|
elif blockid == 11347: # polished diorite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/polished_diorite.png").copy()
|
|
elif blockid == 11348: # polished granite slabs
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/polished_granite.png").copy()
|
|
elif blockid == 11349: # red nether brick slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/red_nether_bricks.png").copy()
|
|
elif blockid == 11350: # smooth sandstone slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/sandstone_top.png").copy()
|
|
elif blockid == 11351: # cut sandstone slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/cut_sandstone.png").copy()
|
|
elif blockid == 11352: # smooth red sandstone slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/red_sandstone_top.png").copy()
|
|
elif blockid == 11353: # cut red sandstone slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/cut_red_sandstone.png").copy()
|
|
elif blockid == 11354: # end_stone_brick_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/end_stone_bricks.png").copy()
|
|
elif blockid == 11355: # mossy_cobblestone_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/mossy_cobblestone.png").copy()
|
|
elif blockid == 11356: # mossy_stone_brick_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/mossy_stone_bricks.png").copy()
|
|
elif blockid == 11357: # smooth_quartz_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/quartz_block_bottom.png").copy()
|
|
elif blockid == 11358: # smooth_stone_slab
|
|
top = self.load_image_texture("assets/minecraft/textures/block/smooth_stone.png").copy()
|
|
side = self.load_image_texture("assets/minecraft/textures/block/smooth_stone_slab_side.png").copy()
|
|
elif blockid == 1027: # blackstone_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/blackstone.png").copy()
|
|
elif blockid == 1028: # polished_blackstone_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/polished_blackstone.png").copy()
|
|
elif blockid == 1029: # polished_blackstone_brick_slab
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/polished_blackstone_bricks.png").copy()
|
|
elif blockid in range(1072, 1080):
|
|
copper_tex = {
|
|
1072: "assets/minecraft/textures/block/cut_copper.png",
|
|
1076: "assets/minecraft/textures/block/cut_copper.png",
|
|
1073: "assets/minecraft/textures/block/exposed_cut_copper.png",
|
|
1077: "assets/minecraft/textures/block/exposed_cut_copper.png",
|
|
1074: "assets/minecraft/textures/block/weathered_cut_copper.png",
|
|
1078: "assets/minecraft/textures/block/weathered_cut_copper.png",
|
|
1075: "assets/minecraft/textures/block/oxidized_cut_copper.png",
|
|
1079: "assets/minecraft/textures/block/oxidized_cut_copper.png",
|
|
}
|
|
top = side = self.load_image_texture(copper_tex[blockid]).copy()
|
|
elif blockid in range(1103, 1107):
|
|
deepslate_tex = {
|
|
1103: "assets/minecraft/textures/block/cobbled_deepslate.png",
|
|
1104: "assets/minecraft/textures/block/polished_deepslate.png",
|
|
1105: "assets/minecraft/textures/block/deepslate_bricks.png",
|
|
1106: "assets/minecraft/textures/block/deepslate_tiles.png",
|
|
}
|
|
top = side = self.load_image_texture(deepslate_tex[blockid]).copy()
|
|
elif blockid == 1124:
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/mud_bricks.png").copy()
|
|
|
|
if blockid == 43 or blockid == 181 or blockid == 204: # double slab
|
|
return self.build_block(top, side)
|
|
|
|
return self.build_slab_block(top, side, data & 8 == 8);
|
|
|
|
# brick block
|
|
block(blockid=45, top_image="assets/minecraft/textures/block/bricks.png")
|
|
# TNT
|
|
block(blockid=46, top_image="assets/minecraft/textures/block/tnt_top.png", side_image="assets/minecraft/textures/block/tnt_side.png", nospawn=True)
|
|
# bookshelf
|
|
block(blockid=47, top_image="assets/minecraft/textures/block/oak_planks.png", side_image="assets/minecraft/textures/block/bookshelf.png")
|
|
# moss stone
|
|
block(blockid=48, top_image="assets/minecraft/textures/block/mossy_cobblestone.png")
|
|
# obsidian
|
|
block(blockid=49, top_image="assets/minecraft/textures/block/obsidian.png")
|
|
|
|
# torch, redstone torch (off), redstone torch(on), soul_torch
|
|
@material(blockid=[50, 75, 76, 1039], data=[1, 2, 3, 4, 5], transparent=True)
|
|
def torches(self, blockid, data):
|
|
# first, rotations
|
|
if self.rotation == 1:
|
|
if data == 1: data = 3
|
|
elif data == 2: data = 4
|
|
elif data == 3: data = 2
|
|
elif data == 4: data = 1
|
|
elif self.rotation == 2:
|
|
if data == 1: data = 2
|
|
elif data == 2: data = 1
|
|
elif data == 3: data = 4
|
|
elif data == 4: data = 3
|
|
elif self.rotation == 3:
|
|
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 = self.load_image_texture("assets/minecraft/textures/block/torch.png")
|
|
elif blockid == 75: # off redstone torch
|
|
small = self.load_image_texture("assets/minecraft/textures/block/redstone_torch_off.png")
|
|
elif blockid == 76: # on redstone torch
|
|
small = self.load_image_texture("assets/minecraft/textures/block/redstone_torch.png")
|
|
elif blockid == 1039: # soul torch
|
|
small= self.load_image_texture("assets/minecraft/textures/block/soul_torch.png")
|
|
# compose a torch bigger than the normal
|
|
# (better for doing transformations)
|
|
torch = Image.new("RGBA", (16,16), self.bgcolor)
|
|
alpha_over(torch,small,(-4,-3))
|
|
alpha_over(torch,small,(-5,-2))
|
|
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 = self.build_full_block(None, None, None, torch, None, None)
|
|
|
|
elif data == 2: # pointing north
|
|
torch = torch.rotate(rotation, Image.NEAREST)
|
|
img = self.build_full_block(None, None, torch, None, None, None)
|
|
|
|
elif data == 3: # pointing west
|
|
torch = torch.rotate(rotation, Image.NEAREST)
|
|
img = self.build_full_block(None, torch, None, None, None, None)
|
|
|
|
elif data == 4: # pointing east
|
|
torch = torch.rotate(-rotation, Image.NEAREST)
|
|
img = self.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), self.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))
|
|
|
|
alpha_over(img, slice, (7,5))
|
|
alpha_over(img, small_crop, (6,6))
|
|
alpha_over(img, small_crop, (7,6))
|
|
alpha_over(img, slice, (7,7))
|
|
|
|
return img
|
|
|
|
# lantern
|
|
@material(blockid=[11373, 1038], data=[0, 1], transparent=True)
|
|
def lantern(self, blockid, data):
|
|
# get the multipart texture of the lantern
|
|
if blockid == 11373:
|
|
inputtexture = self.load_image_texture("assets/minecraft/textures/block/lantern.png")
|
|
if blockid == 1038:
|
|
inputtexture = self.load_image_texture("assets/minecraft/textures/block/soul_lantern.png")
|
|
|
|
|
|
# # now create a textures, using the parts defined in lantern.json
|
|
|
|
# JSON data for sides
|
|
# from": [ 5, 1, 5 ],
|
|
# "to": [11, 8, 11 ],
|
|
# { "uv": [ 0, 2, 6, 9 ], "texture": "#all" }
|
|
|
|
side_crop = inputtexture.crop((0, 2, 6, 9))
|
|
side_slice = side_crop.copy()
|
|
side_texture = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
side_texture.paste(side_slice,(5, 8))
|
|
|
|
# JSON data for top
|
|
# { "uv": [ 0, 9, 6, 15 ], "texture": "#all" }
|
|
top_crop = inputtexture.crop((0, 9, 6, 15))
|
|
top_slice = top_crop.copy()
|
|
top_texture = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
top_texture.paste(top_slice,(5, 5))
|
|
|
|
# mimic parts of build_full_block, to get an object smaller than a block
|
|
# build_full_block(self, top, side1, side2, side3, side4, bottom=None):
|
|
# a non transparent block uses top, side 3 and side 4.
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
# prepare the side textures
|
|
# side3
|
|
side3 = self.transform_image_side(side_texture)
|
|
# Darken this side
|
|
sidealpha = side3.split()[3]
|
|
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
|
|
side3.putalpha(sidealpha)
|
|
# place the transformed texture
|
|
hangoff = 0
|
|
if data == 1:
|
|
hangoff = 8
|
|
xoff = 4
|
|
yoff =- hangoff
|
|
alpha_over(img, side3, (xoff+0, yoff+6), side3)
|
|
# side4
|
|
side4 = self.transform_image_side(side_texture)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Darken this side
|
|
sidealpha = side4.split()[3]
|
|
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
|
|
side4.putalpha(sidealpha)
|
|
alpha_over(img, side4, (12-xoff, yoff+6), side4)
|
|
# top
|
|
top = self.transform_image_top(top_texture)
|
|
alpha_over(img, top, (0, 8-hangoff), top)
|
|
return img
|
|
|
|
# bamboo
|
|
@material(blockid=11416, transparent=True)
|
|
def bamboo(self, blockid, data):
|
|
# get the multipart texture of the lantern
|
|
inputtexture = self.load_image_texture("assets/minecraft/textures/block/bamboo_stalk.png")
|
|
|
|
# # now create a textures, using the parts defined in bamboo1_age0.json
|
|
# { "from": [ 7, 0, 7 ],
|
|
# "to": [ 9, 16, 9 ],
|
|
# "faces": {
|
|
# "down": { "uv": [ 13, 4, 15, 6 ], "texture": "#all", "cullface": "down" },
|
|
# "up": { "uv": [ 13, 0, 15, 2], "texture": "#all", "cullface": "up" },
|
|
# "north": { "uv": [ 0, 0, 2, 16 ], "texture": "#all" },
|
|
# "south": { "uv": [ 0, 0, 2, 16 ], "texture": "#all" },
|
|
# "west": { "uv": [ 0, 0, 2, 16 ], "texture": "#all" },
|
|
# "east": { "uv": [ 0, 0, 2, 16 ], "texture": "#all" }
|
|
# }
|
|
# }
|
|
|
|
side_crop = inputtexture.crop((0, 0, 3, 16))
|
|
side_slice = side_crop.copy()
|
|
side_texture = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
side_texture.paste(side_slice,(0, 0))
|
|
|
|
# JSON data for top
|
|
# "up": { "uv": [ 13, 0, 15, 2], "texture": "#all", "cullface": "up" },
|
|
top_crop = inputtexture.crop((13, 0, 16, 3))
|
|
top_slice = top_crop.copy()
|
|
top_texture = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
top_texture.paste(top_slice,(5, 5))
|
|
|
|
# mimic parts of build_full_block, to get an object smaller than a block
|
|
# build_full_block(self, top, side1, side2, side3, side4, bottom=None):
|
|
# a non transparent block uses top, side 3 and side 4.
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
# prepare the side textures
|
|
# side3
|
|
side3 = self.transform_image_side(side_texture)
|
|
# Darken this side
|
|
sidealpha = side3.split()[3]
|
|
side3 = ImageEnhance.Brightness(side3).enhance(0.9)
|
|
side3.putalpha(sidealpha)
|
|
# place the transformed texture
|
|
xoff = 3
|
|
yoff = 0
|
|
alpha_over(img, side3, (4+xoff, yoff), side3)
|
|
# side4
|
|
side4 = self.transform_image_side(side_texture)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Darken this side
|
|
sidealpha = side4.split()[3]
|
|
side4 = ImageEnhance.Brightness(side4).enhance(0.8)
|
|
side4.putalpha(sidealpha)
|
|
alpha_over(img, side4, (-4+xoff, yoff), side4)
|
|
# top
|
|
top = self.transform_image_top(top_texture)
|
|
alpha_over(img, top, (-4+xoff, -5), top)
|
|
return img
|
|
|
|
# composter
|
|
@material(blockid=11417, data=list(range(9)), transparent=True)
|
|
def composter(self, blockid, data):
|
|
side = self.load_image_texture("assets/minecraft/textures/block/composter_side.png")
|
|
top = self.load_image_texture("assets/minecraft/textures/block/composter_top.png")
|
|
# bottom = self.load_image_texture("assets/minecraft/textures/block/composter_bottom.png")
|
|
|
|
if data == 0: # empty
|
|
return self.build_full_block(top, side, side, side, side)
|
|
|
|
if data == 8:
|
|
compost = self.transform_image_top(
|
|
self.load_image_texture("assets/minecraft/textures/block/composter_ready.png"))
|
|
else:
|
|
compost = self.transform_image_top(
|
|
self.load_image_texture("assets/minecraft/textures/block/composter_compost.png"))
|
|
|
|
nudge = {1: (0, 9), 2: (0, 8), 3: (0, 7), 4: (0, 6), 5: (0, 4), 6: (0, 2), 7: (0, 0), 8: (0, 0)}
|
|
|
|
img = self.build_full_block(None, side, side, None, None)
|
|
alpha_over(img, compost, nudge[data], compost)
|
|
img2 = self.build_full_block(top, None, None, side, side)
|
|
alpha_over(img, img2, (0, 0), img2)
|
|
return img
|
|
|
|
# fire and soul_fire
|
|
@material(blockid=[51, 1040], transparent=True)
|
|
def fire(self, blockid, data):
|
|
if blockid == 51:
|
|
textureNS = self.load_image_texture("assets/minecraft/textures/block/fire_0.png")
|
|
textureEW = self.load_image_texture("assets/minecraft/textures/block/fire_1.png")
|
|
elif blockid == 1040:
|
|
textureNS = self.load_image_texture("assets/minecraft/textures/block/soul_fire_0.png")
|
|
textureEW = self.load_image_texture("assets/minecraft/textures/block/soul_fire_1.png")
|
|
side1 = self.transform_image_side(textureNS)
|
|
side2 = self.transform_image_side(textureEW).transpose(Image.FLIP_LEFT_RIGHT)
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, side1, (12,0), side1)
|
|
alpha_over(img, side2, (0,0), side2)
|
|
alpha_over(img, side1, (0,6), side1)
|
|
alpha_over(img, side2, (12,6), side2)
|
|
return img
|
|
|
|
# monster spawner
|
|
block(blockid=52, top_image="assets/minecraft/textures/block/spawner.png", transparent=True)
|
|
|
|
# wooden, cobblestone, red brick, stone brick, netherbrick, sandstone, spruce, birch,
|
|
# jungle, quartz, red sandstone, purper_stairs, crimson_stairs, warped_stairs, (dark) prismarine,
|
|
# mossy brick and mossy cobblestone, stone smooth_quartz
|
|
# polished_granite polished_andesite polished_diorite granite diorite andesite end_stone_bricks red_nether_brick stairs
|
|
# smooth_red_sandstone blackstone polished_blackstone polished_blackstone_brick
|
|
# also all the copper variants
|
|
# also all deepslate variants
|
|
@material(blockid=[53, 67, 108, 109, 114, 128, 134, 135, 136, 156, 163, 164, 180, 203, 509, 510,
|
|
11337, 11338, 11339, 11370, 11371, 11374, 11375, 11376, 11377, 11378, 11379,
|
|
11380, 11381, 11382, 11383, 11384, 11415, 1030, 1031, 1032, 1064, 1065, 1066,
|
|
1067, 1068, 1069, 1070, 1071, 1099, 1100, 1101, 1102],
|
|
data=list(range(128)), transparent=True, solid=True, nospawn=True)
|
|
def stairs(self, blockid, data):
|
|
# preserve the upside-down bit
|
|
upside_down = data & 0x4
|
|
|
|
# find solid quarters within the top or bottom half of the block
|
|
# NW NE SE SW
|
|
quarters = [data & 0x8, data & 0x10, data & 0x20, data & 0x40]
|
|
|
|
# rotate the quarters so we can pretend northdirection is always upper-left
|
|
numpy.roll(quarters, [0,1,3,2][self.rotation])
|
|
nw,ne,se,sw = quarters
|
|
|
|
stair_id_to_tex = {
|
|
53: "assets/minecraft/textures/block/oak_planks.png",
|
|
67: "assets/minecraft/textures/block/cobblestone.png",
|
|
108: "assets/minecraft/textures/block/bricks.png",
|
|
109: "assets/minecraft/textures/block/stone_bricks.png",
|
|
114: "assets/minecraft/textures/block/nether_bricks.png",
|
|
128: "assets/minecraft/textures/block/sandstone.png",
|
|
134: "assets/minecraft/textures/block/spruce_planks.png",
|
|
135: "assets/minecraft/textures/block/birch_planks.png",
|
|
136: "assets/minecraft/textures/block/jungle_planks.png",
|
|
156: "assets/minecraft/textures/block/quartz_block_side.png",
|
|
163: "assets/minecraft/textures/block/acacia_planks.png",
|
|
164: "assets/minecraft/textures/block/dark_oak_planks.png",
|
|
180: "assets/minecraft/textures/block/red_sandstone.png",
|
|
203: "assets/minecraft/textures/block/purpur_block.png",
|
|
509: "assets/minecraft/textures/block/crimson_planks.png",
|
|
510: "assets/minecraft/textures/block/warped_planks.png",
|
|
11337: "assets/minecraft/textures/block/prismarine.png",
|
|
11338: "assets/minecraft/textures/block/dark_prismarine.png",
|
|
11339: "assets/minecraft/textures/block/prismarine_bricks.png",
|
|
11370: "assets/minecraft/textures/block/mossy_stone_bricks.png",
|
|
11371: "assets/minecraft/textures/block/mossy_cobblestone.png",
|
|
11374: "assets/minecraft/textures/block/sandstone_top.png",
|
|
11375: "assets/minecraft/textures/block/quartz_block_side.png",
|
|
11376: "assets/minecraft/textures/block/polished_granite.png",
|
|
11377: "assets/minecraft/textures/block/polished_diorite.png",
|
|
11378: "assets/minecraft/textures/block/polished_andesite.png",
|
|
11379: "assets/minecraft/textures/block/stone.png",
|
|
11380: "assets/minecraft/textures/block/granite.png",
|
|
11381: "assets/minecraft/textures/block/diorite.png",
|
|
11382: "assets/minecraft/textures/block/andesite.png",
|
|
11383: "assets/minecraft/textures/block/end_stone_bricks.png",
|
|
11384: "assets/minecraft/textures/block/red_nether_bricks.png",
|
|
11415: "assets/minecraft/textures/block/red_sandstone_top.png",
|
|
1030: "assets/minecraft/textures/block/blackstone.png",
|
|
1031: "assets/minecraft/textures/block/polished_blackstone.png",
|
|
1032: "assets/minecraft/textures/block/polished_blackstone_bricks.png",
|
|
# Cut copper stairs
|
|
1064: "assets/minecraft/textures/block/cut_copper.png",
|
|
1065: "assets/minecraft/textures/block/exposed_cut_copper.png",
|
|
1066: "assets/minecraft/textures/block/weathered_cut_copper.png",
|
|
1067: "assets/minecraft/textures/block/oxidized_cut_copper.png",
|
|
# Waxed cut copper stairs
|
|
1068: "assets/minecraft/textures/block/cut_copper.png",
|
|
1069: "assets/minecraft/textures/block/exposed_cut_copper.png",
|
|
1070: "assets/minecraft/textures/block/weathered_cut_copper.png",
|
|
1071: "assets/minecraft/textures/block/oxidized_cut_copper.png",
|
|
# Deepslate
|
|
1099: "assets/minecraft/textures/block/cobbled_deepslate.png",
|
|
1100: "assets/minecraft/textures/block/polished_deepslate.png",
|
|
1101: "assets/minecraft/textures/block/deepslate_bricks.png",
|
|
1102: "assets/minecraft/textures/block/deepslate_tiles.png",
|
|
}
|
|
|
|
texture = self.load_image_texture(stair_id_to_tex[blockid]).copy()
|
|
|
|
outside_l = texture.copy()
|
|
outside_r = texture.copy()
|
|
inside_l = texture.copy()
|
|
inside_r = texture.copy()
|
|
|
|
# sandstone, red sandstone, and quartz stairs have special top texture
|
|
special_tops = {
|
|
128: "assets/minecraft/textures/block/sandstone_top.png",
|
|
156: "assets/minecraft/textures/block/quartz_block_top.png",
|
|
180: "assets/minecraft/textures/block/red_sandstone_top.png",
|
|
11375: "assets/minecraft/textures/block/quartz_block_top.png",
|
|
}
|
|
|
|
if blockid in special_tops:
|
|
texture = self.load_image_texture(special_tops[blockid]).copy()
|
|
|
|
|
|
slab_top = texture.copy()
|
|
|
|
push = 8 if upside_down else 0
|
|
|
|
def rect(tex,coords):
|
|
ImageDraw.Draw(tex).rectangle(coords,outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
# cut out top or bottom half from inner surfaces
|
|
rect(inside_l, (0,8-push,15,15-push))
|
|
rect(inside_r, (0,8-push,15,15-push))
|
|
|
|
# cut out missing or obstructed quarters from each surface
|
|
if not nw:
|
|
rect(outside_l, (0,push,7,7+push))
|
|
rect(texture, (0,0,7,7))
|
|
if not nw or sw:
|
|
rect(inside_r, (8,push,15,7+push)) # will be flipped
|
|
if not ne:
|
|
rect(texture, (8,0,15,7))
|
|
if not ne or nw:
|
|
rect(inside_l, (0,push,7,7+push))
|
|
if not ne or se:
|
|
rect(inside_r, (0,push,7,7+push)) # will be flipped
|
|
if not se:
|
|
rect(outside_r, (0,push,7,7+push)) # will be flipped
|
|
rect(texture, (8,8,15,15))
|
|
if not se or sw:
|
|
rect(inside_l, (8,push,15,7+push))
|
|
if not sw:
|
|
rect(outside_l, (8,push,15,7+push))
|
|
rect(outside_r, (8,push,15,7+push)) # will be flipped
|
|
rect(texture, (0,8,7,15))
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
if upside_down:
|
|
# top should have no cut-outs after all
|
|
texture = slab_top
|
|
else:
|
|
# render the slab-level surface
|
|
slab_top = self.transform_image_top(slab_top)
|
|
alpha_over(img, slab_top, (0,6))
|
|
|
|
# render inner left surface
|
|
inside_l = self.transform_image_side(inside_l)
|
|
# Darken the vertical part of the second step
|
|
sidealpha = inside_l.split()[3]
|
|
# darken it a bit more than usual, looks better
|
|
inside_l = ImageEnhance.Brightness(inside_l).enhance(0.8)
|
|
inside_l.putalpha(sidealpha)
|
|
alpha_over(img, inside_l, (6,3))
|
|
|
|
# render inner right surface
|
|
inside_r = self.transform_image_side(inside_r).transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Darken the vertical part of the second step
|
|
sidealpha = inside_r.split()[3]
|
|
# darken it a bit more than usual, looks better
|
|
inside_r = ImageEnhance.Brightness(inside_r).enhance(0.7)
|
|
inside_r.putalpha(sidealpha)
|
|
alpha_over(img, inside_r, (6,3))
|
|
|
|
# render outer surfaces
|
|
alpha_over(img, self.build_full_block(texture, None, None, outside_l, outside_r))
|
|
|
|
return img
|
|
|
|
# normal, locked (used in april's fool day), ender and trapped chest
|
|
# NOTE: locked chest used to be id95 (which is now stained glass)
|
|
@material(blockid=[54, 130, 146], data=list(range(30)), transparent = True)
|
|
def chests(self, blockid, data):
|
|
# the first 3 bits are the orientation as stored in minecraft,
|
|
# bits 0x8 and 0x10 indicate which half of the double chest is it.
|
|
|
|
# first, do the rotation if needed
|
|
orientation_data = data & 7
|
|
if self.rotation == 1:
|
|
if orientation_data == 2: data = 5 | (data & 24)
|
|
elif orientation_data == 3: data = 4 | (data & 24)
|
|
elif orientation_data == 4: data = 2 | (data & 24)
|
|
elif orientation_data == 5: data = 3 | (data & 24)
|
|
elif self.rotation == 2:
|
|
if orientation_data == 2: data = 3 | (data & 24)
|
|
elif orientation_data == 3: data = 2 | (data & 24)
|
|
elif orientation_data == 4: data = 5 | (data & 24)
|
|
elif orientation_data == 5: data = 4 | (data & 24)
|
|
elif self.rotation == 3:
|
|
if orientation_data == 2: data = 4 | (data & 24)
|
|
elif orientation_data == 3: data = 5 | (data & 24)
|
|
elif orientation_data == 4: data = 3 | (data & 24)
|
|
elif orientation_data == 5: data = 2 | (data & 24)
|
|
|
|
if blockid == 130 and not data in [2, 3, 4, 5]: return None
|
|
# iterate.c will only return the ancil data (without pseudo
|
|
# ancil data) for locked and ender chests, so only
|
|
# ancilData = 2,3,4,5 are used for this blockids
|
|
|
|
if data & 24 == 0:
|
|
if blockid == 130: t = self.load_image("assets/minecraft/textures/entity/chest/ender.png")
|
|
else:
|
|
try:
|
|
t = self.load_image("assets/minecraft/textures/entity/chest/normal.png")
|
|
except (TextureException, IOError):
|
|
t = self.load_image("assets/minecraft/textures/entity/chest/chest.png")
|
|
|
|
t = ImageOps.flip(t) # for some reason the 1.15 images are upside down
|
|
|
|
# the textures is no longer in terrain.png, get it from
|
|
# item/chest.png and get by cropping all the needed stuff
|
|
if t.size != (64, 64): t = t.resize((64, 64), Image.ANTIALIAS)
|
|
# top
|
|
top = t.crop((28, 50, 42, 64))
|
|
top.load() # every crop need a load, crop is a lazy operation
|
|
# see PIL manual
|
|
img = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(img, top, (1, 1))
|
|
top = img
|
|
# front
|
|
front_top = t.crop((42, 45, 56, 50))
|
|
front_top.load()
|
|
front_bottom = t.crop((42, 21, 56, 31))
|
|
front_bottom.load()
|
|
front_lock = t.crop((1, 59, 3, 63))
|
|
front_lock.load()
|
|
front = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(front, front_top, (1, 1))
|
|
alpha_over(front, front_bottom, (1, 5))
|
|
alpha_over(front, front_lock, (7, 3))
|
|
# left side
|
|
# left side, right side, and back are essentially the same for
|
|
# the default texture, we take it anyway just in case other
|
|
# textures make use of it.
|
|
side_l_top = t.crop((14, 45, 28, 50))
|
|
side_l_top.load()
|
|
side_l_bottom = t.crop((14, 21, 28, 31))
|
|
side_l_bottom.load()
|
|
side_l = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(side_l, side_l_top, (1, 1))
|
|
alpha_over(side_l, side_l_bottom, (1, 5))
|
|
# right side
|
|
side_r_top = t.crop((28, 45, 42, 50))
|
|
side_r_top.load()
|
|
side_r_bottom = t.crop((28, 21, 42, 31))
|
|
side_r_bottom.load()
|
|
side_r = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(side_r, side_r_top, (1, 1))
|
|
alpha_over(side_r, side_r_bottom, (1, 5))
|
|
# back
|
|
back_top = t.crop((0, 45, 14, 50))
|
|
back_top.load()
|
|
back_bottom = t.crop((0, 21, 14, 31))
|
|
back_bottom.load()
|
|
back = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(back, back_top, (1, 1))
|
|
alpha_over(back, back_bottom, (1, 5))
|
|
|
|
else:
|
|
# large chest
|
|
# the textures is no longer in terrain.png, get it from
|
|
# item/chest.png and get all the needed stuff
|
|
t_left = self.load_image("assets/minecraft/textures/entity/chest/normal_left.png")
|
|
t_right = self.load_image("assets/minecraft/textures/entity/chest/normal_right.png")
|
|
# for some reason the 1.15 images are upside down
|
|
t_left = ImageOps.flip(t_left)
|
|
t_right = ImageOps.flip(t_right)
|
|
|
|
# Top
|
|
top_left = t_right.crop((29, 50, 44, 64))
|
|
top_left.load()
|
|
top_right = t_left.crop((29, 50, 44, 64))
|
|
top_right.load()
|
|
|
|
top = Image.new("RGBA", (32, 16), self.bgcolor)
|
|
alpha_over(top,top_left, (1, 1))
|
|
alpha_over(top,top_right, (16, 1))
|
|
|
|
# Front
|
|
front_top_left = t_left.crop((43, 45, 58, 50))
|
|
front_top_left.load()
|
|
front_top_right = t_right.crop((43, 45, 58, 50))
|
|
front_top_right.load()
|
|
|
|
front_bottom_left = t_left.crop((43, 21, 58, 31))
|
|
front_bottom_left.load()
|
|
front_bottom_right = t_right.crop((43, 21, 58, 31))
|
|
front_bottom_right.load()
|
|
|
|
front_lock = t_left.crop((1, 59, 3, 63))
|
|
front_lock.load()
|
|
|
|
front = Image.new("RGBA", (32, 16), self.bgcolor)
|
|
alpha_over(front, front_top_left, (1, 1))
|
|
alpha_over(front, front_top_right, (16, 1))
|
|
alpha_over(front, front_bottom_left, (1, 5))
|
|
alpha_over(front, front_bottom_right, (16, 5))
|
|
alpha_over(front, front_lock, (15, 3))
|
|
|
|
# Back
|
|
back_top_left = t_right.crop((14, 45, 29, 50))
|
|
back_top_left.load()
|
|
back_top_right = t_left.crop((14, 45, 29, 50))
|
|
back_top_right.load()
|
|
|
|
back_bottom_left = t_right.crop((14, 21, 29, 31))
|
|
back_bottom_left.load()
|
|
back_bottom_right = t_left.crop((14, 21, 29, 31))
|
|
back_bottom_right.load()
|
|
|
|
back = Image.new("RGBA", (32, 16), self.bgcolor)
|
|
alpha_over(back, back_top_left, (1, 1))
|
|
alpha_over(back, back_top_right, (16, 1))
|
|
alpha_over(back, back_bottom_left, (1, 5))
|
|
alpha_over(back, back_bottom_right, (16, 5))
|
|
|
|
# left side
|
|
side_l_top = t_left.crop((29, 45, 43, 50))
|
|
side_l_top.load()
|
|
side_l_bottom = t_left.crop((29, 21, 43, 31))
|
|
side_l_bottom.load()
|
|
side_l = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(side_l, side_l_top, (1, 1))
|
|
alpha_over(side_l, side_l_bottom, (1, 5))
|
|
# right side
|
|
side_r_top = t_right.crop((0, 45, 14, 50))
|
|
side_r_top.load()
|
|
side_r_bottom = t_right.crop((0, 21, 14, 31))
|
|
side_r_bottom.load()
|
|
side_r = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
alpha_over(side_r, side_r_top, (1, 1))
|
|
alpha_over(side_r, side_r_bottom, (1, 5))
|
|
|
|
# double chest, left half
|
|
if ((data & 24 == 8 and data & 7 in [3, 5]) or (data & 24 == 16 and data & 7 in [2, 4])):
|
|
top = top.crop((0, 0, 16, 16))
|
|
top.load()
|
|
front = front.crop((0, 0, 16, 16))
|
|
front.load()
|
|
back = back.crop((0, 0, 16, 16))
|
|
back.load()
|
|
#~ side = side_l
|
|
|
|
# double chest, right half
|
|
elif ((data & 24 == 16 and data & 7 in [3, 5]) or (data & 24 == 8 and data & 7 in [2, 4])):
|
|
top = top.crop((16, 0, 32, 16))
|
|
top.load()
|
|
front = front.crop((16, 0, 32, 16))
|
|
front.load()
|
|
back = back.crop((16, 0, 32, 16))
|
|
back.load()
|
|
#~ side = side_r
|
|
|
|
else: # just in case
|
|
return None
|
|
|
|
# compose the final block
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
if data & 7 == 2: # north
|
|
side = self.transform_image_side(side_r)
|
|
alpha_over(img, side, (1, 7))
|
|
back = self.transform_image_side(back)
|
|
alpha_over(img, back.transpose(Image.FLIP_LEFT_RIGHT), (11, 7))
|
|
front = self.transform_image_side(front)
|
|
top = self.transform_image_top(top.rotate(180))
|
|
alpha_over(img, top, (0, 2))
|
|
|
|
elif data & 7 == 3: # south
|
|
side = self.transform_image_side(side_l)
|
|
alpha_over(img, side, (1, 7))
|
|
front = self.transform_image_side(front).transpose(Image.FLIP_LEFT_RIGHT)
|
|
top = self.transform_image_top(top.rotate(180))
|
|
alpha_over(img, top, (0, 2))
|
|
alpha_over(img, front, (11, 7))
|
|
|
|
elif data & 7 == 4: # west
|
|
side = self.transform_image_side(side_r)
|
|
alpha_over(img, side.transpose(Image.FLIP_LEFT_RIGHT), (11, 7))
|
|
front = self.transform_image_side(front)
|
|
alpha_over(img, front, (1, 7))
|
|
top = self.transform_image_top(top.rotate(270))
|
|
alpha_over(img, top, (0, 2))
|
|
|
|
elif data & 7 == 5: # east
|
|
back = self.transform_image_side(back)
|
|
side = self.transform_image_side(side_l).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, side, (11, 7))
|
|
alpha_over(img, back, (1, 7))
|
|
top = self.transform_image_top(top.rotate(270))
|
|
alpha_over(img, top, (0, 2))
|
|
|
|
else: # just in case
|
|
img = None
|
|
|
|
return img
|
|
|
|
# redstone wire
|
|
# uses pseudo-ancildata found in iterate.c
|
|
@material(blockid=55, data=list(range(128)), transparent=True)
|
|
def wire(self, blockid, data):
|
|
|
|
if data & 0b1000000 == 64: # powered redstone wire
|
|
redstone_wire_t = self.load_image_texture("assets/minecraft/textures/block/redstone_dust_line0.png").rotate(90)
|
|
redstone_wire_t = self.tint_texture(redstone_wire_t,(255,0,0))
|
|
|
|
redstone_cross_t = self.load_image_texture("assets/minecraft/textures/block/redstone_dust_dot.png")
|
|
redstone_cross_t = self.tint_texture(redstone_cross_t,(255,0,0))
|
|
|
|
|
|
else: # unpowered redstone wire
|
|
redstone_wire_t = self.load_image_texture("assets/minecraft/textures/block/redstone_dust_line0.png").rotate(90)
|
|
redstone_wire_t = self.tint_texture(redstone_wire_t,(48,0,0))
|
|
|
|
redstone_cross_t = self.load_image_texture("assets/minecraft/textures/block/redstone_dust_dot.png")
|
|
redstone_cross_t = self.tint_texture(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()
|
|
|
|
# see iterate.c for where these masks come from
|
|
has_x = (data & 0b1010) > 0
|
|
has_z = (data & 0b0101) > 0
|
|
if has_x and has_z:
|
|
bottom = redstone_cross_t.copy()
|
|
if has_x:
|
|
alpha_over(bottom, redstone_wire_t.copy())
|
|
if has_z:
|
|
alpha_over(bottom, redstone_wire_t.copy().rotate(90))
|
|
|
|
else:
|
|
if has_x:
|
|
bottom = redstone_wire_t.copy()
|
|
elif has_z:
|
|
bottom = redstone_wire_t.copy().rotate(90)
|
|
elif data & 0b1111 == 0:
|
|
bottom = redstone_cross_t.copy()
|
|
|
|
# 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 = self.build_full_block(None,side1,side2,None,None,bottom)
|
|
|
|
return img
|
|
|
|
# diamond ore
|
|
block(blockid=56, top_image="assets/minecraft/textures/block/diamond_ore.png")
|
|
# diamond block
|
|
block(blockid=57, top_image="assets/minecraft/textures/block/diamond_block.png")
|
|
|
|
|
|
# Table blocks with no facing or other properties where sides are not all the same
|
|
# Includes: Crafting table, fletching table, cartography table, smithing table
|
|
@material(blockid=[58, 11359, 11360, 11361], solid=True, nodata=True)
|
|
def block_table(self, blockid, data):
|
|
block_name = {58: "crafting_table",
|
|
11359: "fletching_table",
|
|
11360: "cartography_table",
|
|
11361: "smithing_table"}[blockid]
|
|
|
|
# Top texture doesn't vary with self.rotation, but texture rotation does
|
|
top_tex = block_name + "_top"
|
|
top_rot = [0, 270, 180, 90][self.rotation]
|
|
|
|
# List of side textures from side 1 to 4 for each blockid
|
|
side_tex_map = {58: ["front", "side", "front", "side"],
|
|
11359: ["front", "side", "side", "front"],
|
|
11360: ["side3", "side3", "side2", "side1"],
|
|
11361: ["front", "side", "side", "front"]}[blockid]
|
|
# Determine which side textures to use
|
|
side3_id = [2, 3, 1, 0][self.rotation]
|
|
side4_id = [3, 1, 0, 2][self.rotation]
|
|
side3_tex = block_name + "_" + side_tex_map[side3_id]
|
|
side4_tex = block_name + "_" + side_tex_map[side4_id]
|
|
|
|
tex_path = "assets/minecraft/textures/block"
|
|
top = self.load_image_texture("{}/{}.png".format(tex_path, top_tex)).copy()
|
|
side3 = self.load_image_texture("{}/{}.png".format(tex_path, side3_tex))
|
|
side4 = self.load_image_texture("{}/{}.png".format(tex_path, side4_tex)).copy()
|
|
top = top.rotate(top_rot)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
return self.build_full_block(top, None, None, side3, side4, None)
|
|
|
|
|
|
@material(blockid=11366, data=list(range(8)), transparent=True, solid=True, nospawn=True)
|
|
def lectern(self, blockid, data):
|
|
# Do rotation, mask to not clobber book data
|
|
data = data & 0b100 | ((self.rotation + (data & 0b11)) % 4)
|
|
|
|
# Load textures
|
|
base_raw_t = self.load_image_texture("assets/minecraft/textures/block/lectern_base.png")
|
|
front_raw_t = self.load_image_texture("assets/minecraft/textures/block/lectern_front.png")
|
|
side_raw_t = self.load_image_texture("assets/minecraft/textures/block/lectern_sides.png")
|
|
top_raw_t = self.load_image_texture("assets/minecraft/textures/block/lectern_top.png")
|
|
|
|
def create_tile(img_src, coord_crop, coord_paste, rot):
|
|
# Takes an image, crops a region, optionally rotates the
|
|
# texture, then finally pastes it onto a 16x16 image
|
|
img_out = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
img_in = img_src.crop(coord_crop)
|
|
if rot != 0:
|
|
img_in = img_in.rotate(rot, expand=True)
|
|
img_out.paste(img_in, coord_paste)
|
|
return img_out
|
|
|
|
def darken_image(img_src, darken_value):
|
|
# Takes an image & alters the brightness, leaving alpha intact
|
|
alpha = img_src.split()[3]
|
|
img_out = ImageEnhance.Brightness(img_src).enhance(darken_value)
|
|
img_out.putalpha(alpha)
|
|
return img_out
|
|
|
|
# Generate base
|
|
base_top_t = base_raw_t.rotate([0, 270, 180, 90][data & 0b11])
|
|
# Front & side textures are one pixel taller than they should be
|
|
# pre-transformation as otherwise the topmost row of pixels
|
|
# post-transformation are rather transparent, which results in
|
|
# a visible gap between the base's sides & top
|
|
base_front_t = create_tile(base_raw_t, (0, 13, 16, 16), (0, 13), 0)
|
|
base_side_t = create_tile(base_raw_t, (0, 5, 16, 8), (0, 13), 0)
|
|
base_side3_t = base_front_t if data & 0b11 == 1 else base_side_t
|
|
base_side4_t = base_front_t if data & 0b11 == 0 else base_side_t
|
|
img = self.build_full_block((base_top_t, 14), None, None, base_side3_t, base_side4_t, None)
|
|
|
|
# Generate central pillar
|
|
side_flip_t = side_raw_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Define parameters used to obtain the texture for each side
|
|
pillar_param = [{'img': front_raw_t, 'crop': (8, 4, 16, 16), 'paste': (4, 2), 'rot': 0}, # South
|
|
{'img': side_raw_t, 'crop': (2, 8, 15, 16), 'paste': (4, 1), 'rot': 270}, # West
|
|
{'img': front_raw_t, 'crop': (0, 4, 8, 13), 'paste': (4, 5), 'rot': 0}, # North
|
|
{'img': side_flip_t, 'crop': (2, 8, 15, 16), 'paste': (4, 1), 'rot': 90}] # East
|
|
# Determine which sides are rendered
|
|
pillar_side = [pillar_param[(3 - (data & 0b11)) % 4], pillar_param[(2 - (data & 0b11)) % 4]]
|
|
|
|
pillar_side3_t = create_tile(pillar_side[0]['img'], pillar_side[0]['crop'],
|
|
pillar_side[0]['paste'], pillar_side[0]['rot'])
|
|
pillar_side4_t = create_tile(pillar_side[1]['img'], pillar_side[1]['crop'],
|
|
pillar_side[1]['paste'], pillar_side[1]['rot'])
|
|
pillar_side4_t = pillar_side4_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
pillar_side3_t = self.transform_image_side(pillar_side3_t)
|
|
pillar_side3_t = darken_image(pillar_side3_t, 0.9)
|
|
pillar_side4_t = self.transform_image_side(pillar_side4_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
pillar_side4_t = darken_image(pillar_side4_t, 0.8)
|
|
alpha_over(img, pillar_side3_t, (3, 4), pillar_side3_t)
|
|
alpha_over(img, pillar_side4_t, (9, 4), pillar_side4_t)
|
|
|
|
# Generate stand
|
|
if (data & 0b11) in [0, 1]: # South, West
|
|
stand_side3_t = create_tile(side_raw_t, (0, 0, 16, 4), (0, 4), 0)
|
|
stand_side4_t = create_tile(side_raw_t, (0, 4, 13, 8), (0, 0), -22.5)
|
|
else: # North, East
|
|
stand_side3_t = create_tile(side_raw_t, (0, 4, 16, 8), (0, 0), 0)
|
|
stand_side4_t = create_tile(side_raw_t, (0, 4, 13, 8), (0, 0), 22.5)
|
|
|
|
stand_side3_t = self.transform_image_angle(stand_side3_t, math.radians(22.5))
|
|
stand_side3_t = darken_image(stand_side3_t, 0.9)
|
|
stand_side4_t = self.transform_image_side(stand_side4_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
stand_side4_t = darken_image(stand_side4_t, 0.8)
|
|
stand_top_t = create_tile(top_raw_t, (0, 1, 16, 14), (0, 1), 0)
|
|
if data & 0b100:
|
|
# Lectern has a book, modify the stand top texture
|
|
book_raw_t = self.load_image("assets/minecraft/textures/entity/enchanting_table_book.png")
|
|
book_t = Image.new("RGBA", (14, 10), self.bgcolor)
|
|
book_part_t = book_raw_t.crop((0, 0, 7, 10)) # Left cover
|
|
alpha_over(stand_top_t, book_part_t, (1, 3), book_part_t)
|
|
book_part_t = book_raw_t.crop((15, 0, 22, 10)) # Right cover
|
|
alpha_over(stand_top_t, book_part_t, (8, 3))
|
|
book_part_t = book_raw_t.crop((24, 10, 29, 18)).rotate(180) # Left page
|
|
alpha_over(stand_top_t, book_part_t, (3, 4), book_part_t)
|
|
book_part_t = book_raw_t.crop((29, 10, 34, 18)).rotate(180) # Right page
|
|
alpha_over(stand_top_t, book_part_t, (8, 4), book_part_t)
|
|
|
|
# Perform affine transformation
|
|
transform_matrix = numpy.matrix(numpy.identity(3))
|
|
if (data & 0b11) in [0, 1]: # South, West
|
|
# Translate: 8 -X, 8 -Y
|
|
transform_matrix *= numpy.matrix([[1, 0, 8], [0, 1, 8], [0, 0, 1]])
|
|
# Rotate 40 degrees clockwise
|
|
tc = math.cos(math.radians(40))
|
|
ts = math.sin(math.radians(40))
|
|
transform_matrix *= numpy.matrix([[tc, ts, 0], [-ts, tc, 0], [0, 0, 1]])
|
|
# Shear in the Y direction
|
|
tt = math.tan(math.radians(10))
|
|
transform_matrix *= numpy.matrix([[1, 0, 0], [tt, 1, 0], [0, 0, 1]])
|
|
# Scale to 70% height & 110% width
|
|
transform_matrix *= numpy.matrix([[1 / 1.1, 0, 0], [0, 1 / 0.7, 0], [0, 0, 1]])
|
|
# Translate: 12 +X, 8 +Y
|
|
transform_matrix *= numpy.matrix([[1, 0, -12], [0, 1, -8], [0, 0, 1]])
|
|
else: # North, East
|
|
# Translate: 8 -X, 8 -Y
|
|
transform_matrix *= numpy.matrix([[1, 0, 8], [0, 1, 8], [0, 0, 1]])
|
|
# Shear in the X direction
|
|
tt = math.tan(math.radians(25))
|
|
transform_matrix *= numpy.matrix([[1, tt, 0], [0, 1, 0], [0, 0, 1]])
|
|
# Scale to 80% height
|
|
transform_matrix *= numpy.matrix([[1, 0, 0], [0, 1 / 0.8, 0], [0, 0, 1]])
|
|
# Rotate 220 degrees clockwise
|
|
tc = math.cos(math.radians(40 + 180))
|
|
ts = math.sin(math.radians(40 + 180))
|
|
transform_matrix *= numpy.matrix([[tc, ts, 0], [-ts, tc, 0], [0, 0, 1]])
|
|
# Scale to 60% height
|
|
transform_matrix *= numpy.matrix([[1, 0, 0], [0, 1 / 0.6, 0], [0, 0, 1]])
|
|
# Translate: +13 X, +7 Y
|
|
transform_matrix *= numpy.matrix([[1, 0, -13], [0, 1, -7], [0, 0, 1]])
|
|
|
|
transform_matrix = numpy.array(transform_matrix)[:2, :].ravel().tolist()
|
|
stand_top_t = stand_top_t.transform((24, 24), Image.AFFINE, transform_matrix)
|
|
|
|
img_stand = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(img_stand, stand_side3_t, (-4, 2), stand_side3_t) # Fix some holes
|
|
alpha_over(img_stand, stand_side3_t, (-3, 3), stand_side3_t)
|
|
alpha_over(img_stand, stand_side4_t, (12, 5), stand_side4_t)
|
|
alpha_over(img_stand, stand_top_t, (0, 0), stand_top_t)
|
|
# Flip the stand if North or South facing
|
|
if (data & 0b11) in [0, 2]:
|
|
img_stand = img_stand.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, img_stand, (0, -2), img_stand)
|
|
|
|
return img
|
|
|
|
|
|
@material(blockid=11367, data=list(range(4)), solid=True)
|
|
def loom(self, blockid, data):
|
|
# Do rotation
|
|
data = (self.rotation + data) % 4
|
|
|
|
top_rot = [180, 90, 0, 270][data]
|
|
side3_tex = "front" if data == 1 else "side"
|
|
side4_tex = "front" if data == 0 else "side"
|
|
|
|
tex_path = "assets/minecraft/textures/block"
|
|
top = self.load_image_texture("{}/loom_top.png".format(tex_path)).copy()
|
|
side3 = self.load_image_texture("{}/loom_{}.png".format(tex_path, side3_tex))
|
|
side4 = self.load_image_texture("{}/loom_{}.png".format(tex_path, side4_tex)).copy()
|
|
top = top.rotate(top_rot)
|
|
side4 = side4.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
return self.build_full_block(top, None, None, side3, side4, None)
|
|
|
|
|
|
@material(blockid=11368, data=list(range(4)), transparent=True, solid=True, nospawn=True)
|
|
def stonecutter(self, blockid, data):
|
|
# Do rotation
|
|
data = (self.rotation + data) % 4
|
|
|
|
top_t = self.load_image_texture("assets/minecraft/textures/block/stonecutter_top.png").copy()
|
|
side_t = self.load_image_texture("assets/minecraft/textures/block/stonecutter_side.png")
|
|
# Stonecutter saw texture contains multiple tiles, since it's
|
|
# 16px wide rely on load_image_texture() to crop appropriately
|
|
blade_t = self.load_image_texture("assets/minecraft/textures/block/stonecutter_saw.png").copy()
|
|
|
|
top_t = top_t.rotate([180, 90, 0, 270][data])
|
|
img = self.build_full_block((top_t, 7), None, None, side_t, side_t, None)
|
|
|
|
# Add saw blade
|
|
if data in [0, 2]:
|
|
blade_t = blade_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
blade_t = self.transform_image_side(blade_t)
|
|
if data in [0, 2]:
|
|
blade_t = blade_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, blade_t, (6, -4), blade_t)
|
|
|
|
return img
|
|
|
|
|
|
@material(blockid=11369, data=list(range(12)), transparent=True, solid=True, nospawn=True)
|
|
def grindstone(self, blockid, data):
|
|
# Do rotation, mask to not clobber mounting info
|
|
data = data & 0b1100 | ((self.rotation + (data & 0b11)) % 4)
|
|
|
|
# Load textures
|
|
side_raw_t = self.load_image_texture("assets/minecraft/textures/block/grindstone_side.png").copy()
|
|
round_raw_t = self.load_image_texture("assets/minecraft/textures/block/grindstone_round.png").copy()
|
|
pivot_raw_t = self.load_image_texture("assets/minecraft/textures/block/grindstone_pivot.png").copy()
|
|
leg_raw_t = self.load_image_texture("assets/minecraft/textures/block/dark_oak_log.png").copy()
|
|
|
|
def create_tile(img_src, coord_crop, coord_paste, scale):
|
|
# Takes an image, crops a region, optionally scales the
|
|
# texture, then finally pastes it onto a 16x16 image
|
|
img_out = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
img_in = img_src.crop(coord_crop)
|
|
if scale >= 0 and scale != 1:
|
|
w, h = img_in.size
|
|
img_in = img_in.resize((int(w * scale), int(h * scale)), Image.NEAREST)
|
|
img_out.paste(img_in, coord_paste)
|
|
return img_out
|
|
|
|
# Set variables defining positions of various parts
|
|
wall_mounted = bool(data & 0b0100)
|
|
rot_leg = [0, 270, 0][data >> 2]
|
|
if wall_mounted:
|
|
pos_leg = (32, 28) if data & 0b11 in [2, 3] else (10, 18)
|
|
coord_leg = [(0, 0), (-10, -1), (2, 3)]
|
|
offset_final = [(2, 1), (-2, 1), (-2, -1), (2, -1)][data & 0b11]
|
|
else:
|
|
pos_leg = [(22, 31), (22, 9)][data >> 3]
|
|
coord_leg = [(0, 0), (-1, 2), (-2, -3)]
|
|
offset_final = (0, 2 * (data >> 2) - 1)
|
|
|
|
# Create parts
|
|
# Scale up small parts like pivot & leg to avoid ugly results
|
|
# when shearing & combining parts, then scale down to original
|
|
# size just before final image composition
|
|
scale_factor = 2
|
|
side_t = create_tile(side_raw_t, (0, 0, 12, 12), (2, 0), 1)
|
|
round_ud_t = create_tile(round_raw_t, (0, 0, 8, 12), (4, 2), 1)
|
|
round_lr_t = create_tile(round_raw_t, (0, 0, 8, 12), (4, 0), 1)
|
|
pivot_outer_t = create_tile(pivot_raw_t, (0, 0, 6, 6), (2, 2), scale_factor)
|
|
pivot_lr_t = create_tile(pivot_raw_t, (6, 0, 8, 6), (2, 2), scale_factor)
|
|
pivot_ud_t = create_tile(pivot_raw_t, (8, 0, 10, 6), (2, 2), scale_factor)
|
|
leg_outer_t = create_tile(leg_raw_t, (6, 9, 10, 16), (2, 2), scale_factor).rotate(rot_leg)
|
|
leg_lr_t = create_tile(leg_raw_t, (12, 9, 14, 16), (2, 2), scale_factor).rotate(rot_leg)
|
|
leg_ud_t = create_tile(leg_raw_t, (2, 6, 4, 10), (2, 2), scale_factor)
|
|
|
|
# Transform to block sides & tops
|
|
side_t = self.transform_image_side(side_t)
|
|
round_ud_t = self.transform_image_top(round_ud_t)
|
|
round_lr_t = self.transform_image_side(round_lr_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
pivot_outer_t = self.transform_image_side(pivot_outer_t)
|
|
pivot_lr_t = self.transform_image_side(pivot_lr_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
pivot_ud_t = self.transform_image_top(pivot_ud_t)
|
|
leg_outer_t = self.transform_image_side(leg_outer_t)
|
|
if wall_mounted:
|
|
leg_lr_t = self.transform_image_top(leg_lr_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
leg_ud_t = self.transform_image_side(leg_ud_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
else:
|
|
leg_lr_t = self.transform_image_side(leg_lr_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
leg_ud_t = self.transform_image_top(leg_ud_t)
|
|
|
|
# Compose leg texture
|
|
img_leg = Image.new("RGBA", (24 * scale_factor, 24 * scale_factor), self.bgcolor)
|
|
alpha_over(img_leg, leg_outer_t, coord_leg[0], leg_outer_t)
|
|
alpha_over(img_leg, leg_lr_t, coord_leg[1], leg_lr_t)
|
|
alpha_over(img_leg, leg_ud_t, coord_leg[2], leg_ud_t)
|
|
|
|
# Compose pivot texture (& combine with leg)
|
|
img_pivot = Image.new("RGBA", (24 * scale_factor, 24 * scale_factor), self.bgcolor)
|
|
alpha_over(img_pivot, pivot_ud_t, (20, 18), pivot_ud_t)
|
|
alpha_over(img_pivot, pivot_lr_t, (23, 24), pivot_lr_t) # Fix gaps between face edges
|
|
alpha_over(img_pivot, pivot_lr_t, (24, 24), pivot_lr_t)
|
|
alpha_over(img_pivot, img_leg, pos_leg, img_leg)
|
|
alpha_over(img_pivot, pivot_outer_t, (21, 21), pivot_outer_t)
|
|
if hasattr(Image, "LANCZOS"): # workaround for older Pillow
|
|
img_pivot = img_pivot.resize((24, 24), Image.LANCZOS)
|
|
else:
|
|
img_pivot = img_pivot.resize((24, 24))
|
|
|
|
# Combine leg, side, round & pivot
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
img_final = img.copy()
|
|
alpha_over(img, img_pivot, (1, -5), img_pivot)
|
|
alpha_over(img, round_ud_t, (0, 2), round_ud_t) # Fix gaps between face edges
|
|
alpha_over(img, side_t, (3, 6), side_t)
|
|
alpha_over(img, round_ud_t, (0, 1), round_ud_t)
|
|
alpha_over(img, round_lr_t, (10, 6), round_lr_t)
|
|
alpha_over(img, img_pivot, (-5, -1), img_pivot)
|
|
if (data & 0b11) in [1, 3]:
|
|
img = img.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img_final, img, offset_final, img)
|
|
|
|
return img_final
|
|
|
|
|
|
# crops with 8 data values (like wheat)
|
|
@material(blockid=59, data=list(range(8)), transparent=True, nospawn=True)
|
|
def crops8(self, blockid, data):
|
|
raw_crop = self.load_image_texture("assets/minecraft/textures/block/wheat_stage%d.png" % data)
|
|
crop1 = self.transform_image_top(raw_crop)
|
|
crop2 = self.transform_image_side(raw_crop)
|
|
crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, crop1, (0,12), crop1)
|
|
alpha_over(img, crop2, (6,3), crop2)
|
|
alpha_over(img, crop3, (6,3), crop3)
|
|
return img
|
|
|
|
|
|
# farmland and grass path (15/16 blocks)
|
|
@material(blockid=[60, 208], data=list(range(2)), solid=True, transparent=True, nospawn=True)
|
|
def farmland(self, blockid, data):
|
|
if blockid == 60:
|
|
side = self.load_image_texture("assets/minecraft/textures/block/dirt.png").copy()
|
|
if data == 0:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/farmland.png")
|
|
else:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/farmland_moist.png")
|
|
# dirt.png is 16 pixels tall, so we need to crop it before building full block
|
|
side = side.crop((0, 1, 16, 16))
|
|
else:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/dirt_path_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/dirt_path_side.png")
|
|
# side already has 1 transparent pixel at the top, so it doesn't need to be modified
|
|
# just shift the top image down 1 pixel
|
|
|
|
return self.build_full_block((top, 1), side, side, side, side)
|
|
|
|
|
|
# signposts
|
|
@material(blockid=[63,11401,11402,11403,11404,11405,11406,12505,12506], data=list(range(16)), transparent=True)
|
|
def signpost(self, blockid, data):
|
|
|
|
# first rotations
|
|
if self.rotation == 1:
|
|
data = (data + 4) % 16
|
|
elif self.rotation == 2:
|
|
data = (data + 8) % 16
|
|
elif self.rotation == 3:
|
|
data = (data + 12) % 16
|
|
|
|
sign_texture = {
|
|
# (texture on sign, texture on stick)
|
|
63: ("oak_planks.png", "oak_log.png"),
|
|
11401: ("oak_planks.png", "oak_log.png"),
|
|
11402: ("spruce_planks.png", "spruce_log.png"),
|
|
11403: ("birch_planks.png", "birch_log.png"),
|
|
11404: ("jungle_planks.png", "jungle_log.png"),
|
|
11405: ("acacia_planks.png", "acacia_log.png"),
|
|
11406: ("dark_oak_planks.png", "dark_oak_log.png"),
|
|
12505: ("crimson_planks.png", "crimson_stem.png"),
|
|
12506: ("warped_planks.png", "warped_stem.png"),
|
|
}
|
|
texture_path, texture_stick_path = ["assets/minecraft/textures/block/" + x for x in sign_texture[blockid]]
|
|
|
|
texture = self.load_image_texture(texture_path).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 = self.load_image_texture(texture_stick_path)
|
|
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), self.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 = self.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
|
|
|
|
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)
|
|
alpha_over(img, post2,(incrementx, -3),post2)
|
|
alpha_over(img, post, (0,-2), post)
|
|
|
|
return img
|
|
|
|
|
|
# wooden and iron door
|
|
# uses pseudo-ancildata found in iterate.c
|
|
@material(blockid=[64,71,193,194,195,196,197, 499, 500], data=list(range(32)), transparent=True)
|
|
def door(self, blockid, data):
|
|
#Masked to not clobber block top/bottom & swung info
|
|
if self.rotation == 1:
|
|
if (data & 0b00011) == 0: data = data & 0b11100 | 1
|
|
elif (data & 0b00011) == 1: data = data & 0b11100 | 2
|
|
elif (data & 0b00011) == 2: data = data & 0b11100 | 3
|
|
elif (data & 0b00011) == 3: data = data & 0b11100 | 0
|
|
elif self.rotation == 2:
|
|
if (data & 0b00011) == 0: data = data & 0b11100 | 2
|
|
elif (data & 0b00011) == 1: data = data & 0b11100 | 3
|
|
elif (data & 0b00011) == 2: data = data & 0b11100 | 0
|
|
elif (data & 0b00011) == 3: data = data & 0b11100 | 1
|
|
elif self.rotation == 3:
|
|
if (data & 0b00011) == 0: data = data & 0b11100 | 3
|
|
elif (data & 0b00011) == 1: data = data & 0b11100 | 0
|
|
elif (data & 0b00011) == 2: data = data & 0b11100 | 1
|
|
elif (data & 0b00011) == 3: data = data & 0b11100 | 2
|
|
|
|
if data & 0x8 == 0x8: # top of the door
|
|
if blockid == 64: # classic wood door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/oak_door_top.png")
|
|
elif blockid == 71: # iron door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/iron_door_top.png")
|
|
elif blockid == 193: # spruce door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/spruce_door_top.png")
|
|
elif blockid == 194: # birch door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/birch_door_top.png")
|
|
elif blockid == 195: # jungle door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/jungle_door_top.png")
|
|
elif blockid == 196: # acacia door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/acacia_door_top.png")
|
|
elif blockid == 197: # dark_oak door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/dark_oak_door_top.png")
|
|
elif blockid == 499: # crimson door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/crimson_door_top.png")
|
|
elif blockid == 500: # warped door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/warped_door_top.png")
|
|
else: # bottom of the door
|
|
if blockid == 64:
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/oak_door_bottom.png")
|
|
elif blockid == 71: # iron door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/iron_door_bottom.png")
|
|
elif blockid == 193: # spruce door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/spruce_door_bottom.png")
|
|
elif blockid == 194: # birch door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/birch_door_bottom.png")
|
|
elif blockid == 195: # jungle door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/jungle_door_bottom.png")
|
|
elif blockid == 196: # acacia door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/acacia_door_bottom.png")
|
|
elif blockid == 197: # dark_oak door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/dark_oak_door_bottom.png")
|
|
elif blockid == 499: # crimson door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/crimson_door_bottom.png")
|
|
elif blockid == 500: # warped door
|
|
raw_door = self.load_image_texture("assets/minecraft/textures/block/warped_door_bottom.png")
|
|
|
|
# if you want to render all doors as closed, then force
|
|
# force closed to be True
|
|
if data & 0x4 == 0x4:
|
|
closed = False
|
|
else:
|
|
closed = True
|
|
|
|
if data & 0x10 == 0x10:
|
|
# hinge on the left (facing same door direction)
|
|
hinge_on_left = True
|
|
else:
|
|
# hinge on the right (default single door)
|
|
hinge_on_left = False
|
|
|
|
# mask out the high bits to figure out the orientation
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
if (data & 0x03) == 0: # facing west when closed
|
|
if hinge_on_left:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
alpha_over(img, tex, (0,6), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (12,6), tex)
|
|
else:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door)
|
|
alpha_over(img, tex, (0,6), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (0,0), tex)
|
|
|
|
if (data & 0x03) == 1: # facing north when closed
|
|
if hinge_on_left:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (0,0), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door)
|
|
alpha_over(img, tex, (0,6), tex)
|
|
|
|
else:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (0,0), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door)
|
|
alpha_over(img, tex, (12,0), tex)
|
|
|
|
|
|
if (data & 0x03) == 2: # facing east when closed
|
|
if hinge_on_left:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door)
|
|
alpha_over(img, tex, (12,0), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door)
|
|
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (0,0), tex)
|
|
else:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
alpha_over(img, tex, (12,0), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (12,6), tex)
|
|
|
|
if (data & 0x03) == 3: # facing south when closed
|
|
if hinge_on_left:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (12,6), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
alpha_over(img, tex, (12,0), tex)
|
|
else:
|
|
if closed:
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
tex = tex.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (12,6), tex)
|
|
else:
|
|
# flip first to set the doornob on the correct side
|
|
tex = self.transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT))
|
|
alpha_over(img, tex, (0,6), tex)
|
|
|
|
return img
|
|
|
|
# ladder
|
|
@material(blockid=65, data=[2, 3, 4, 5], transparent=True)
|
|
def ladder(self, blockid, data):
|
|
|
|
# first rotations
|
|
if self.rotation == 1:
|
|
if data == 2: data = 5
|
|
elif data == 3: data = 4
|
|
elif data == 4: data = 2
|
|
elif data == 5: data = 3
|
|
elif self.rotation == 2:
|
|
if data == 2: data = 3
|
|
elif data == 3: data = 2
|
|
elif data == 4: data = 5
|
|
elif data == 5: data = 4
|
|
elif self.rotation == 3:
|
|
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), self.bgcolor)
|
|
raw_texture = self.load_image_texture("assets/minecraft/textures/block/ladder.png")
|
|
|
|
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 = self.transform_image_side(raw_texture)
|
|
alpha_over(img, tex, (0,6), tex)
|
|
return img
|
|
if data == 2:
|
|
tex = self.transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (12,6), tex)
|
|
return img
|
|
if data == 3:
|
|
tex = self.transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, tex, (0,0), tex)
|
|
return img
|
|
if data == 4:
|
|
tex = self.transform_image_side(raw_texture)
|
|
alpha_over(img, tex, (12,0), tex)
|
|
return img
|
|
|
|
|
|
# wall signs
|
|
@material(blockid=[68,11407,11408,11409,11410,11411,11412,12507,12508], data=[2, 3, 4, 5], transparent=True)
|
|
def wall_sign(self, blockid, data): # wall sign
|
|
|
|
# first rotations
|
|
if self.rotation == 1:
|
|
if data == 2: data = 5
|
|
elif data == 3: data = 4
|
|
elif data == 4: data = 2
|
|
elif data == 5: data = 3
|
|
elif self.rotation == 2:
|
|
if data == 2: data = 3
|
|
elif data == 3: data = 2
|
|
elif data == 4: data = 5
|
|
elif data == 5: data = 4
|
|
elif self.rotation == 3:
|
|
if data == 2: data = 4
|
|
elif data == 3: data = 5
|
|
elif data == 4: data = 3
|
|
elif data == 5: data = 2
|
|
|
|
sign_texture = {
|
|
68: "oak_planks.png",
|
|
11407: "oak_planks.png",
|
|
11408: "spruce_planks.png",
|
|
11409: "birch_planks.png",
|
|
11410: "jungle_planks.png",
|
|
11411: "acacia_planks.png",
|
|
11412: "dark_oak_planks.png",
|
|
12507: "crimson_planks.png",
|
|
12508: "warped_planks.png",
|
|
}
|
|
texture_path = "assets/minecraft/textures/block/" + sign_texture[blockid]
|
|
texture = self.load_image_texture(texture_path).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), self.bgcolor)
|
|
|
|
incrementx = 0
|
|
if data == 2: # east
|
|
incrementx = +1
|
|
sign = self.build_full_block(None, None, None, None, texture)
|
|
elif data == 3: # west
|
|
incrementx = -1
|
|
sign = self.build_full_block(None, texture, None, None, None)
|
|
elif data == 4: # north
|
|
incrementx = +1
|
|
sign = self.build_full_block(None, None, texture, None, None)
|
|
elif data == 5: # south
|
|
incrementx = -1
|
|
sign = self.build_full_block(None, None, None, texture, None)
|
|
|
|
sign2 = ImageEnhance.Brightness(sign).enhance(1.2)
|
|
alpha_over(img, sign2,(incrementx, 2),sign2)
|
|
alpha_over(img, sign, (0,3), sign)
|
|
|
|
return img
|
|
|
|
# levers
|
|
@material(blockid=69, data=list(range(16)), transparent=True)
|
|
def levers(self, blockid, data):
|
|
if data & 8 == 8: powered = True
|
|
else: powered = False
|
|
|
|
data = data & 7
|
|
|
|
# first rotations
|
|
if self.rotation == 1:
|
|
# on wall levers
|
|
if data == 1: data = 3
|
|
elif data == 2: data = 4
|
|
elif data == 3: data = 2
|
|
elif data == 4: data = 1
|
|
# on floor levers
|
|
elif data == 5: data = 6
|
|
elif data == 6: data = 5
|
|
elif self.rotation == 2:
|
|
if data == 1: data = 2
|
|
elif data == 2: data = 1
|
|
elif data == 3: data = 4
|
|
elif data == 4: data = 3
|
|
elif data == 5: data = 5
|
|
elif data == 6: data = 6
|
|
elif self.rotation == 3:
|
|
if data == 1: data = 4
|
|
elif data == 2: data = 3
|
|
elif data == 3: data = 1
|
|
elif data == 4: data = 2
|
|
elif data == 5: data = 6
|
|
elif data == 6: data = 5
|
|
|
|
# generate the texture for the base of the lever
|
|
t_base = self.load_image_texture("assets/minecraft/textures/block/stone.png").copy()
|
|
|
|
ImageDraw.Draw(t_base).rectangle((0,0,15,3),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(t_base).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(t_base).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(t_base).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
# generate the texture for the stick
|
|
stick = self.load_image_texture("assets/minecraft/textures/block/lever.png").copy()
|
|
c_stick = Image.new("RGBA", (16,16), self.bgcolor)
|
|
|
|
tmp = ImageEnhance.Brightness(stick).enhance(0.8)
|
|
alpha_over(c_stick, tmp, (1,0), tmp)
|
|
alpha_over(c_stick, stick, (0,0), stick)
|
|
t_stick = self.transform_image_side(c_stick.rotate(45, Image.NEAREST))
|
|
|
|
# where the lever will be composed
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
# wall levers
|
|
if data == 1: # facing SOUTH
|
|
# levers can't be placed in transparent blocks, so this
|
|
# direction is almost invisible
|
|
return None
|
|
|
|
elif data == 2: # facing NORTH
|
|
base = self.transform_image_side(t_base)
|
|
|
|
# paste it twice with different brightness to make a fake 3D effect
|
|
alpha_over(img, base, (12,-1), base)
|
|
|
|
alpha = base.split()[3]
|
|
base = ImageEnhance.Brightness(base).enhance(0.9)
|
|
base.putalpha(alpha)
|
|
|
|
alpha_over(img, base, (11,0), base)
|
|
|
|
# paste the lever stick
|
|
pos = (7,-7)
|
|
if powered:
|
|
t_stick = t_stick.transpose(Image.FLIP_TOP_BOTTOM)
|
|
pos = (7,6)
|
|
alpha_over(img, t_stick, pos, t_stick)
|
|
|
|
elif data == 3: # facing WEST
|
|
base = self.transform_image_side(t_base)
|
|
|
|
# paste it twice with different brightness to make a fake 3D effect
|
|
base = base.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, base, (0,-1), base)
|
|
|
|
alpha = base.split()[3]
|
|
base = ImageEnhance.Brightness(base).enhance(0.9)
|
|
base.putalpha(alpha)
|
|
|
|
alpha_over(img, base, (1,0), base)
|
|
|
|
# paste the lever stick
|
|
t_stick = t_stick.transpose(Image.FLIP_LEFT_RIGHT)
|
|
pos = (5,-7)
|
|
if powered:
|
|
t_stick = t_stick.transpose(Image.FLIP_TOP_BOTTOM)
|
|
pos = (6,6)
|
|
alpha_over(img, t_stick, pos, t_stick)
|
|
|
|
elif data == 4: # facing EAST
|
|
# levers can't be placed in transparent blocks, so this
|
|
# direction is almost invisible
|
|
return None
|
|
|
|
# floor levers
|
|
elif data == 5: # pointing south when off
|
|
# lever base, fake 3d again
|
|
base = self.transform_image_top(t_base)
|
|
|
|
alpha = base.split()[3]
|
|
tmp = ImageEnhance.Brightness(base).enhance(0.8)
|
|
tmp.putalpha(alpha)
|
|
|
|
alpha_over(img, tmp, (0,12), tmp)
|
|
alpha_over(img, base, (0,11), base)
|
|
|
|
# lever stick
|
|
pos = (3,2)
|
|
if not powered:
|
|
t_stick = t_stick.transpose(Image.FLIP_LEFT_RIGHT)
|
|
pos = (11,2)
|
|
alpha_over(img, t_stick, pos, t_stick)
|
|
|
|
elif data == 6: # pointing east when off
|
|
# lever base, fake 3d again
|
|
base = self.transform_image_top(t_base.rotate(90))
|
|
|
|
alpha = base.split()[3]
|
|
tmp = ImageEnhance.Brightness(base).enhance(0.8)
|
|
tmp.putalpha(alpha)
|
|
|
|
alpha_over(img, tmp, (0,12), tmp)
|
|
alpha_over(img, base, (0,11), base)
|
|
|
|
# lever stick
|
|
pos = (2,3)
|
|
if not powered:
|
|
t_stick = t_stick.transpose(Image.FLIP_LEFT_RIGHT)
|
|
pos = (10,2)
|
|
alpha_over(img, t_stick, pos, t_stick)
|
|
|
|
return img
|
|
|
|
# wooden and stone pressure plates, and weighted pressure plates
|
|
@material(blockid=[70, 72,147,148,11301,11302,11303,11304,11305, 1033,11517,11518], data=[0,1], transparent=True)
|
|
def pressure_plate(self, blockid, data):
|
|
texture_name = {70:"assets/minecraft/textures/block/stone.png", # stone
|
|
72:"assets/minecraft/textures/block/oak_planks.png", # oak
|
|
11301:"assets/minecraft/textures/block/spruce_planks.png", # spruce
|
|
11302:"assets/minecraft/textures/block/birch_planks.png", # birch
|
|
11303:"assets/minecraft/textures/block/jungle_planks.png", # jungle
|
|
11304:"assets/minecraft/textures/block/acacia_planks.png", # acacia
|
|
11305:"assets/minecraft/textures/block/dark_oak_planks.png", # dark oak
|
|
11517:"assets/minecraft/textures/block/crimson_planks.png", # crimson
|
|
11518:"assets/minecraft/textures/block/warped_planks.png", # warped
|
|
147:"assets/minecraft/textures/block/gold_block.png", # light golden
|
|
148:"assets/minecraft/textures/block/iron_block.png", # heavy iron
|
|
1033:"assets/minecraft/textures/block/polished_blackstone.png"
|
|
}[blockid]
|
|
t = self.load_image_texture(texture_name).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), self.bgcolor)
|
|
|
|
top = self.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:
|
|
alpha_over(img,topd, (0,12),topd)
|
|
alpha_over(img,top, (0,11),top)
|
|
elif data == 1:
|
|
alpha_over(img,top, (0,12),top)
|
|
|
|
return img
|
|
|
|
# normal and glowing redstone ore
|
|
block(blockid=[73, 74], top_image="assets/minecraft/textures/block/redstone_ore.png")
|
|
|
|
# stone and wood buttons
|
|
@material(blockid=(77,143,11326,11327,11328,11329,11330,1034,11515,11516), data=list(range(16)), transparent=True)
|
|
def buttons(self, blockid, data):
|
|
|
|
# 0x8 is set if the button is pressed mask this info and render
|
|
# it as unpressed
|
|
data = data & 0x7
|
|
|
|
if self.rotation == 1:
|
|
if data == 1: data = 3
|
|
elif data == 2: data = 4
|
|
elif data == 3: data = 2
|
|
elif data == 4: data = 1
|
|
elif data == 5: data = 6
|
|
elif data == 6: data = 5
|
|
elif self.rotation == 2:
|
|
if data == 1: data = 2
|
|
elif data == 2: data = 1
|
|
elif data == 3: data = 4
|
|
elif data == 4: data = 3
|
|
elif self.rotation == 3:
|
|
if data == 1: data = 4
|
|
elif data == 2: data = 3
|
|
elif data == 3: data = 1
|
|
elif data == 4: data = 2
|
|
elif data == 5: data = 6
|
|
elif data == 6: data = 5
|
|
|
|
texturepath = {77:"assets/minecraft/textures/block/stone.png",
|
|
143:"assets/minecraft/textures/block/oak_planks.png",
|
|
11326:"assets/minecraft/textures/block/spruce_planks.png",
|
|
11327:"assets/minecraft/textures/block/birch_planks.png",
|
|
11328:"assets/minecraft/textures/block/jungle_planks.png",
|
|
11329:"assets/minecraft/textures/block/acacia_planks.png",
|
|
11330:"assets/minecraft/textures/block/dark_oak_planks.png",
|
|
1034:"assets/minecraft/textures/block/polished_blackstone.png",
|
|
11515:"assets/minecraft/textures/block/crimson_planks.png",
|
|
11516:"assets/minecraft/textures/block/warped_planks.png"
|
|
}[blockid]
|
|
t = self.load_image_texture(texturepath).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), self.bgcolor)
|
|
|
|
if data < 5:
|
|
button = self.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
|
|
alpha_over(img, button, (12,-1), button)
|
|
|
|
alpha = button.split()[3]
|
|
button = ImageEnhance.Brightness(button).enhance(0.9)
|
|
button.putalpha(alpha)
|
|
|
|
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)
|
|
alpha_over(img, button, (0,-1), button)
|
|
|
|
alpha = button.split()[3]
|
|
button = ImageEnhance.Brightness(button).enhance(0.9)
|
|
button.putalpha(alpha)
|
|
|
|
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
|
|
|
|
else:
|
|
if data == 5: # long axis east-west
|
|
button = self.transform_image_top(t)
|
|
else: # long axis north-south
|
|
button = self.transform_image_top(t.rotate(90))
|
|
|
|
# paste it twice with different brightness to make a 3D effect
|
|
alpha_over(img, button, (0,12), button)
|
|
|
|
alpha = button.split()[3]
|
|
button = ImageEnhance.Brightness(button).enhance(0.9)
|
|
button.putalpha(alpha)
|
|
|
|
alpha_over(img, button, (0,11), button)
|
|
|
|
return img
|
|
|
|
# end rod
|
|
@material(blockid=198, data=list(range(6)), transparent=True, solid=True)
|
|
def end_rod(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/end_rod.png")
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
|
|
mask = tex.crop((0, 0, 2, 15))
|
|
sidetex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(sidetex, mask, (14, 0), mask)
|
|
|
|
mask = tex.crop((2, 3, 6, 7))
|
|
bottom = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(bottom, mask, (5, 6), mask)
|
|
|
|
if data == 1 or data == 0:
|
|
side = self.transform_image_side(sidetex)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
bottom = self.transform_image_top(bottom)
|
|
|
|
if data == 1: # up
|
|
mask = tex.crop((2, 0, 4, 2))
|
|
top = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(top, mask, (7, 2), mask)
|
|
top = self.transform_image_top(top)
|
|
|
|
alpha_over(img, bottom, (0, 11), bottom)
|
|
alpha_over(img, side, (0, 0), side)
|
|
alpha_over(img, otherside, (11, 0), otherside)
|
|
alpha_over(img, top, (3, 1), top)
|
|
elif data == 0: # down
|
|
alpha_over(img, side, (0, 0), side)
|
|
alpha_over(img, otherside, (11, 0), otherside)
|
|
alpha_over(img, bottom, (0, 0), bottom)
|
|
else:
|
|
otherside = self.transform_image_top(sidetex)
|
|
|
|
sidetex = sidetex.rotate(90)
|
|
side = self.transform_image_side(sidetex)
|
|
bottom = self.transform_image_side(bottom)
|
|
bottom = bottom.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
def draw_south():
|
|
alpha_over(img, bottom, (0, 0), bottom)
|
|
alpha_over(img, side, (7, 8), side)
|
|
alpha_over(img, otherside, (-3, 9), otherside)
|
|
|
|
def draw_north():
|
|
alpha_over(img, side, (7, 8), side)
|
|
alpha_over(img, otherside, (-3, 9), otherside)
|
|
alpha_over(img, bottom, (12, 6), bottom)
|
|
|
|
def draw_west():
|
|
_bottom = bottom.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _bottom, (13, 0), _bottom)
|
|
_side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _side, (7, 8), _side)
|
|
_otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _otherside, (4, 9), _otherside)
|
|
|
|
def draw_east():
|
|
_side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _side, (7, 8), _side)
|
|
_otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _otherside, (4, 9), _otherside)
|
|
_bottom = bottom.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _bottom, (0, 6), _bottom)
|
|
|
|
draw_funcs = [ draw_south, draw_west, draw_north, draw_east ]
|
|
|
|
if data == 3: # south
|
|
draw_funcs[self.rotation]()
|
|
elif data == 2: # north
|
|
draw_funcs[(self.rotation + 2) % len(draw_funcs)]()
|
|
elif data == 4: # west
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
elif data == 5: # east
|
|
draw_funcs[(self.rotation + 3) % len(draw_funcs)]()
|
|
|
|
return img
|
|
|
|
# snow
|
|
@material(blockid=78, data=list(range(1, 9)), transparent=True, solid=True)
|
|
def snow(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/snow.png")
|
|
|
|
y = 16 - (data * 2)
|
|
mask = tex.crop((0, y, 16, 16))
|
|
sidetex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(sidetex, mask, (0,y,16,16), mask)
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
top = self.transform_image_top(tex)
|
|
side = self.transform_image_side(sidetex)
|
|
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)
|
|
|
|
alpha_over(img, side, (0, 6), side)
|
|
alpha_over(img, otherside, (12, 6), otherside)
|
|
alpha_over(img, top, (0, 12 - int(12 / 8 * data)), top)
|
|
|
|
return img
|
|
|
|
# snow block
|
|
block(blockid=80, top_image="assets/minecraft/textures/block/snow.png")
|
|
|
|
# cactus
|
|
@material(blockid=81, data=list(range(15)), transparent=True, solid=True, nospawn=True)
|
|
def cactus(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/cactus_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/cactus_side.png")
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
top = self.transform_image_top(top)
|
|
side = self.transform_image_side(side)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
sidealpha = side.split()[3]
|
|
side = ImageEnhance.Brightness(side).enhance(0.9)
|
|
side.putalpha(sidealpha)
|
|
othersidealpha = otherside.split()[3]
|
|
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
|
|
otherside.putalpha(othersidealpha)
|
|
|
|
alpha_over(img, side, (1,6), side)
|
|
alpha_over(img, otherside, (11,6), otherside)
|
|
alpha_over(img, top, (0,0), top)
|
|
|
|
return img
|
|
|
|
# clay block
|
|
block(blockid=82, top_image="assets/minecraft/textures/block/clay.png")
|
|
|
|
# sugar cane
|
|
@material(blockid=83, data=list(range(16)), transparent=True)
|
|
def sugar_cane(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/sugar_cane.png")
|
|
return self.build_sprite(tex)
|
|
|
|
# jukebox
|
|
@material(blockid=84, data=list(range(16)), solid=True)
|
|
def jukebox(self, blockid, data):
|
|
return self.build_block(self.load_image_texture("assets/minecraft/textures/block/jukebox_top.png"), self.load_image_texture("assets/minecraft/textures/block/note_block.png"))
|
|
|
|
# nether and normal fences
|
|
@material(blockid=[85, 188, 189, 190, 191, 192, 113, 511, 512], data=list(range(16)), transparent=True, nospawn=True)
|
|
def fence(self, blockid, data):
|
|
# create needed images for Big stick fence
|
|
if blockid == 85: # normal fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png").copy()
|
|
elif blockid == 188: # spruce fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png").copy()
|
|
elif blockid == 189: # birch fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/birch_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/birch_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/birch_planks.png").copy()
|
|
elif blockid == 190: # jungle fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png").copy()
|
|
elif blockid == 191: # big/dark oak fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png").copy()
|
|
elif blockid == 192: # acacia fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy()
|
|
elif blockid == 511: # crimson_fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy()
|
|
elif blockid == 512: # warped fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/warped_planks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/warped_planks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/warped_planks.png").copy()
|
|
else: # netherbrick fence
|
|
fence_top = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png").copy()
|
|
fence_side = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png").copy()
|
|
fence_small_side = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png").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 = self.transform_image_side(fence_side)
|
|
fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
fence_top = self.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), self.bgcolor)
|
|
alpha_over(fence_big,fence_side, (5,4),fence_side)
|
|
alpha_over(fence_big,fence_other_side, (7,4),fence_other_side)
|
|
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 = self.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), self.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 then small sticks in the front.
|
|
def draw_north():
|
|
alpha_over(img, fence_small_side, pos_top_left, fence_small_side)
|
|
def draw_east():
|
|
alpha_over(img, fence_small_other_side, pos_top_right, fence_small_other_side)
|
|
def draw_south():
|
|
alpha_over(img, fence_small_side, pos_bottom_right, fence_small_side)
|
|
def draw_west():
|
|
alpha_over(img, fence_small_other_side, pos_bottom_left, fence_small_other_side)
|
|
|
|
draw_funcs = [draw_north, draw_east, draw_south, draw_west]
|
|
|
|
if (data & 0b0001):
|
|
draw_funcs[(self.rotation + 0) % len(draw_funcs)]()
|
|
if (data & 0b0010):
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
|
|
alpha_over(img, fence_big, (0, 0), fence_big)
|
|
|
|
if (data & 0b0100):
|
|
draw_funcs[(self.rotation + 2) % len(draw_funcs)]()
|
|
if (data & 0b1000):
|
|
draw_funcs[(self.rotation + 3) % len(draw_funcs)]()
|
|
|
|
return img
|
|
|
|
# pumpkin
|
|
@material(blockid=[86, 91,11300], data=list(range(4)), solid=True)
|
|
def pumpkin(self, blockid, data): # pumpkins, jack-o-lantern
|
|
# rotation
|
|
if self.rotation == 1:
|
|
if data == 0: data = 1
|
|
elif data == 1: data = 2
|
|
elif data == 2: data = 3
|
|
elif data == 3: data = 0
|
|
elif self.rotation == 2:
|
|
if data == 0: data = 2
|
|
elif data == 1: data = 3
|
|
elif data == 2: data = 0
|
|
elif data == 3: data = 1
|
|
elif self.rotation == 3:
|
|
if data == 0: data = 3
|
|
elif data == 1: data = 0
|
|
elif data == 2: data = 1
|
|
elif data == 3: data = 2
|
|
|
|
# texture generation
|
|
top = self.load_image_texture("assets/minecraft/textures/block/pumpkin_top.png")
|
|
frontName = {86: "assets/minecraft/textures/block/pumpkin_side.png",
|
|
91: "assets/minecraft/textures/block/jack_o_lantern.png",
|
|
11300: "assets/minecraft/textures/block/carved_pumpkin.png"
|
|
}[blockid]
|
|
front = self.load_image_texture(frontName)
|
|
side = self.load_image_texture("assets/minecraft/textures/block/pumpkin_side.png")
|
|
|
|
if data == 0: # pointing west
|
|
img = self.build_full_block(top, None, None, side, front)
|
|
|
|
elif data == 1: # pointing north
|
|
img = self.build_full_block(top, None, None, front, side)
|
|
|
|
else: # in any other direction the front can't be seen
|
|
img = self.build_full_block(top, None, None, side, side)
|
|
|
|
return img
|
|
|
|
# netherrack
|
|
block(blockid=87, top_image="assets/minecraft/textures/block/netherrack.png")
|
|
|
|
# soul sand
|
|
block(blockid=88, top_image="assets/minecraft/textures/block/soul_sand.png")
|
|
|
|
# glowstone
|
|
block(blockid=89, top_image="assets/minecraft/textures/block/glowstone.png")
|
|
# shroomlight
|
|
block(blockid=1011, top_image="assets/minecraft/textures/block/shroomlight.png")
|
|
|
|
# portal
|
|
@material(blockid=90, data=[1, 2, 4, 5, 8, 10], transparent=True)
|
|
def portal(self, blockid, data):
|
|
# no rotations, uses pseudo data
|
|
portaltexture = self.load_portal()
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
side = self.transform_image_side(portaltexture)
|
|
otherside = side.transpose(Image.FLIP_TOP_BOTTOM)
|
|
|
|
if data in (1,4,5):
|
|
alpha_over(img, side, (5,4), side)
|
|
|
|
if data in (2,8,10):
|
|
alpha_over(img, otherside, (5,4), otherside)
|
|
|
|
return img
|
|
|
|
|
|
# cake!
|
|
@material(blockid=92, data=list(range(7)), transparent=True, nospawn=True)
|
|
def cake(self, blockid, data):
|
|
# cake textures
|
|
top = self.load_image_texture("assets/minecraft/textures/block/cake_top.png").copy()
|
|
side = self.load_image_texture("assets/minecraft/textures/block/cake_side.png").copy()
|
|
fullside = side.copy()
|
|
inside = self.load_image_texture("assets/minecraft/textures/block/cake_inner.png")
|
|
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
if data == 0: # unbitten cake
|
|
top = self.transform_image_top(top)
|
|
side = self.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)
|
|
|
|
# composite the cake
|
|
alpha_over(img, side, (1, 6), side)
|
|
alpha_over(img, otherside, (11, 5), otherside) # workaround, fixes a hole
|
|
alpha_over(img, otherside, (12, 6), otherside)
|
|
alpha_over(img, top, (0, 6), top)
|
|
|
|
else:
|
|
# cut the textures for a bitten cake
|
|
bite_width = int(14 / 7) # Cake is 14px wide with 7 slices
|
|
coord = 1 + bite_width * data
|
|
ImageDraw.Draw(side).rectangle((16 - coord, 0, 16, 16), outline=(0, 0, 0, 0),
|
|
fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(top).rectangle((0, 0, coord - 1, 16), outline=(0, 0, 0, 0),
|
|
fill=(0, 0, 0, 0))
|
|
|
|
# the bitten part of the cake always points to the west
|
|
# composite the cake for every north orientation
|
|
if self.rotation == 0: # north top-left
|
|
# create right side
|
|
rs = self.transform_image_side(side).transpose(Image.FLIP_LEFT_RIGHT)
|
|
# create bitten side and its coords
|
|
deltax = bite_width * data
|
|
deltay = -1 * data
|
|
if data in [3, 4, 5, 6]:
|
|
deltax -= 1
|
|
ls = self.transform_image_side(inside)
|
|
# create top side
|
|
t = self.transform_image_top(top)
|
|
# darken sides slightly
|
|
sidealpha = ls.split()[3]
|
|
ls = ImageEnhance.Brightness(ls).enhance(0.9)
|
|
ls.putalpha(sidealpha)
|
|
othersidealpha = rs.split()[3]
|
|
rs = ImageEnhance.Brightness(rs).enhance(0.8)
|
|
rs.putalpha(othersidealpha)
|
|
# compose the cake
|
|
alpha_over(img, rs, (12, 6), rs)
|
|
alpha_over(img, ls, (1 + deltax, 6 + deltay), ls)
|
|
alpha_over(img, t, (1, 6), t)
|
|
|
|
elif self.rotation == 1: # north top-right
|
|
# bitten side not shown
|
|
# create left side
|
|
ls = self.transform_image_side(side.transpose(Image.FLIP_LEFT_RIGHT))
|
|
# create top
|
|
t = self.transform_image_top(top.rotate(-90))
|
|
# create right side
|
|
rs = self.transform_image_side(fullside).transpose(Image.FLIP_LEFT_RIGHT)
|
|
# darken sides slightly
|
|
sidealpha = ls.split()[3]
|
|
ls = ImageEnhance.Brightness(ls).enhance(0.9)
|
|
ls.putalpha(sidealpha)
|
|
othersidealpha = rs.split()[3]
|
|
rs = ImageEnhance.Brightness(rs).enhance(0.8)
|
|
rs.putalpha(othersidealpha)
|
|
# compose the cake
|
|
alpha_over(img, ls, (2, 6), ls)
|
|
alpha_over(img, t, (1, 6), t)
|
|
alpha_over(img, rs, (12, 6), rs)
|
|
|
|
elif self.rotation == 2: # north bottom-right
|
|
# bitten side not shown
|
|
# left side
|
|
ls = self.transform_image_side(fullside)
|
|
# top
|
|
t = self.transform_image_top(top.rotate(180))
|
|
# right side
|
|
rs = self.transform_image_side(side.transpose(Image.FLIP_LEFT_RIGHT))
|
|
rs = rs.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# darken sides slightly
|
|
sidealpha = ls.split()[3]
|
|
ls = ImageEnhance.Brightness(ls).enhance(0.9)
|
|
ls.putalpha(sidealpha)
|
|
othersidealpha = rs.split()[3]
|
|
rs = ImageEnhance.Brightness(rs).enhance(0.8)
|
|
rs.putalpha(othersidealpha)
|
|
# compose the cake
|
|
alpha_over(img, ls, (2, 6), ls)
|
|
alpha_over(img, t, (1, 6), t)
|
|
alpha_over(img, rs, (12, 6), rs)
|
|
|
|
elif self.rotation == 3: # north bottom-left
|
|
# create left side
|
|
ls = self.transform_image_side(side)
|
|
# create top
|
|
t = self.transform_image_top(top.rotate(90))
|
|
# create right side and its coords
|
|
deltax = 12 - bite_width * data
|
|
deltay = -1 * data
|
|
if data in [3, 4, 5, 6]:
|
|
deltax += 1
|
|
rs = self.transform_image_side(inside).transpose(Image.FLIP_LEFT_RIGHT)
|
|
# darken sides slightly
|
|
sidealpha = ls.split()[3]
|
|
ls = ImageEnhance.Brightness(ls).enhance(0.9)
|
|
ls.putalpha(sidealpha)
|
|
othersidealpha = rs.split()[3]
|
|
rs = ImageEnhance.Brightness(rs).enhance(0.8)
|
|
rs.putalpha(othersidealpha)
|
|
# compose the cake
|
|
alpha_over(img, ls, (2, 6), ls)
|
|
alpha_over(img, t, (1, 6), t)
|
|
alpha_over(img, rs, (1 + deltax, 6 + deltay), rs)
|
|
|
|
return img
|
|
|
|
|
|
# redstone repeaters ON and OFF
|
|
@material(blockid=[93,94], data=list(range(16)), transparent=True, nospawn=True)
|
|
def repeater(self, blockid, data):
|
|
# rotation
|
|
# Masked to not clobber delay info
|
|
if self.rotation == 1:
|
|
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 self.rotation == 2:
|
|
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 self.rotation == 3:
|
|
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 = self.load_image_texture("assets/minecraft/textures/block/repeater.png") if blockid == 93 else self.load_image_texture("assets/minecraft/textures/block/repeater_on.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/smooth_stone_slab_side.png")
|
|
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 = self.build_full_block( (top, increment), None, None, side, side)
|
|
|
|
# compose a "3d" redstone torch
|
|
t = self.load_image_texture("assets/minecraft/textures/block/redstone_torch_off.png").copy() if blockid == 93 else self.load_image_texture("assets/minecraft/textures/block/redstone_torch.png").copy()
|
|
torch = Image.new("RGBA", (24,24), self.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))
|
|
|
|
alpha_over(torch, slice, (6,4))
|
|
alpha_over(torch, t_crop, (5,5))
|
|
alpha_over(torch, t_crop, (6,5))
|
|
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.
|
|
alpha_over(img, torch, static_torch, torch)
|
|
alpha_over(img, torch, moving_torch, torch)
|
|
|
|
return img
|
|
|
|
# redstone comparator (149 is inactive, 150 is active)
|
|
@material(blockid=[149,150], data=list(range(16)), transparent=True, nospawn=True)
|
|
def comparator(self, blockid, data):
|
|
|
|
# rotation
|
|
# add self.rotation to the lower 2 bits, mod 4
|
|
data = data & 0b1100 | (((data & 0b11) + self.rotation) % 4)
|
|
|
|
|
|
top = self.load_image_texture("assets/minecraft/textures/block/comparator.png") if blockid == 149 else self.load_image_texture("assets/minecraft/textures/block/comparator_on.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/smooth_stone_slab_side.png")
|
|
increment = 13
|
|
|
|
if (data & 0x3) == 0: # pointing north
|
|
pass
|
|
static_torch = (-3,-1)
|
|
torch = ((0,2),(6,-1))
|
|
|
|
if (data & 0x3) == 1: # pointing east
|
|
top = top.rotate(270)
|
|
static_torch = (5,-1)
|
|
torch = ((-4,-1),(0,2))
|
|
|
|
if (data & 0x3) == 2: # pointing south
|
|
top = top.rotate(180)
|
|
static_torch = (5,3)
|
|
torch = ((0,-4),(-4,-1))
|
|
|
|
if (data & 0x3) == 3: # pointing west
|
|
top = top.rotate(90)
|
|
static_torch = (-3,3)
|
|
torch = ((1,-4),(6,-1))
|
|
|
|
|
|
def build_torch(active):
|
|
# compose a "3d" redstone torch
|
|
t = self.load_image_texture("assets/minecraft/textures/block/redstone_torch_off.png").copy() if not active else self.load_image_texture("assets/minecraft/textures/block/redstone_torch.png").copy()
|
|
torch = Image.new("RGBA", (24,24), self.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))
|
|
|
|
alpha_over(torch, slice, (6,4))
|
|
alpha_over(torch, t_crop, (5,5))
|
|
alpha_over(torch, t_crop, (6,5))
|
|
alpha_over(torch, slice, (6,6))
|
|
|
|
return torch
|
|
|
|
active_torch = build_torch(True)
|
|
inactive_torch = build_torch(False)
|
|
back_torch = active_torch if (blockid == 150 or data & 0b1000 == 0b1000) else inactive_torch
|
|
static_torch_img = active_torch if (data & 0b100 == 0b100) else inactive_torch
|
|
|
|
img = self.build_full_block( (top, increment), None, None, side, side)
|
|
|
|
alpha_over(img, static_torch_img, static_torch, static_torch_img)
|
|
alpha_over(img, back_torch, torch[0], back_torch)
|
|
alpha_over(img, back_torch, torch[1], back_torch)
|
|
return img
|
|
|
|
|
|
# trapdoor
|
|
# the trapdoor is looks like a sprite when opened, that's not good
|
|
@material(blockid=[96,167,11332,11333,11334,11335,11336,12501,12502], data=list(range(16)), transparent=True, nospawn=True)
|
|
def trapdoor(self, blockid, data):
|
|
|
|
# rotation
|
|
# Masked to not clobber opened/closed info
|
|
if self.rotation == 1:
|
|
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 self.rotation == 2:
|
|
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 self.rotation == 3:
|
|
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
|
|
texturepath = {96:"assets/minecraft/textures/block/oak_trapdoor.png",
|
|
167:"assets/minecraft/textures/block/iron_trapdoor.png",
|
|
11332:"assets/minecraft/textures/block/spruce_trapdoor.png",
|
|
11333:"assets/minecraft/textures/block/birch_trapdoor.png",
|
|
11334:"assets/minecraft/textures/block/jungle_trapdoor.png",
|
|
11335:"assets/minecraft/textures/block/acacia_trapdoor.png",
|
|
11336:"assets/minecraft/textures/block/dark_oak_trapdoor.png",
|
|
12501:"assets/minecraft/textures/block/crimson_trapdoor.png",
|
|
12502:"assets/minecraft/textures/block/warped_trapdoor.png",
|
|
}[blockid]
|
|
|
|
if data & 0x4 == 0x4: # opened trapdoor
|
|
if data & 0x08 == 0x08: texture = self.load_image_texture(texturepath).transpose(Image.FLIP_TOP_BOTTOM)
|
|
else: texture = self.load_image_texture(texturepath)
|
|
|
|
if data & 0x3 == 0: # west
|
|
img = self.build_full_block(None, None, None, None, texture)
|
|
if data & 0x3 == 1: # east
|
|
img = self.build_full_block(None, texture, None, None, None)
|
|
if data & 0x3 == 2: # south
|
|
img = self.build_full_block(None, None, texture, None, None)
|
|
if data & 0x3 == 3: # north
|
|
img = self.build_full_block(None, None, None, texture, None)
|
|
|
|
elif data & 0x4 == 0: # closed trapdoor
|
|
texture = self.load_image_texture(texturepath)
|
|
if data & 0x8 == 0x8: # is a top trapdoor
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
t = self.build_full_block((texture, 12), None, None, texture, texture)
|
|
alpha_over(img, t, (0,-9),t)
|
|
else: # is a bottom trapdoor
|
|
img = self.build_full_block((texture, 12), None, None, texture, texture)
|
|
|
|
return img
|
|
|
|
# block with hidden silverfish (stone, cobblestone and stone brick)
|
|
@material(blockid=97, data=list(range(3)), solid=True)
|
|
def hidden_silverfish(self, blockid, data):
|
|
if data == 0: # stone
|
|
t = self.load_image_texture("assets/minecraft/textures/block/stone.png")
|
|
elif data == 1: # cobblestone
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cobblestone.png")
|
|
elif data == 2: # stone brick
|
|
t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png")
|
|
|
|
img = self.build_block(t, t)
|
|
|
|
return img
|
|
|
|
# stone brick
|
|
@material(blockid=98, data=list(range(4)), solid=True)
|
|
def stone_brick(self, blockid, data):
|
|
if data == 0: # normal
|
|
t = self.load_image_texture("assets/minecraft/textures/block/stone_bricks.png")
|
|
elif data == 1: # mossy
|
|
t = self.load_image_texture("assets/minecraft/textures/block/mossy_stone_bricks.png")
|
|
elif data == 2: # cracked
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cracked_stone_bricks.png")
|
|
elif data == 3: # "circle" stone brick
|
|
t = self.load_image_texture("assets/minecraft/textures/block/chiseled_stone_bricks.png")
|
|
|
|
img = self.build_full_block(t, None, None, t, t)
|
|
|
|
return img
|
|
|
|
|
|
# huge brown/red mushrooms, and mushroom stems
|
|
@material(blockid=[99, 100, 139], data=list(range(64)), solid=True)
|
|
def huge_mushroom(self, blockid, data):
|
|
# Re-arrange the bits in data based on self.rotation
|
|
# rotation bit: 654321
|
|
# 0 DUENWS
|
|
# 1 DUNWSE
|
|
# 2 DUWSEN
|
|
# 3 DUSENW
|
|
if self.rotation in [1, 2, 3]:
|
|
bit_map = {1: [6, 5, 3, 2, 1, 4],
|
|
2: [6, 5, 2, 1, 4, 3],
|
|
3: [6, 5, 1, 4, 3, 2]}
|
|
new_data = 0
|
|
|
|
# Add the ith bit to new_data then shift left one at a time,
|
|
# re-ordering data's bits in the order specified in bit_map
|
|
for i in bit_map[self.rotation]:
|
|
new_data = new_data << 1
|
|
new_data |= (data >> (i - 1)) & 1
|
|
data = new_data
|
|
|
|
# texture generation
|
|
texture_map = {99: "brown_mushroom_block",
|
|
100: "red_mushroom_block",
|
|
139: "mushroom_stem"}
|
|
cap = self.load_image_texture("assets/minecraft/textures/block/%s.png" % texture_map[blockid])
|
|
porous = self.load_image_texture("assets/minecraft/textures/block/mushroom_block_inside.png")
|
|
|
|
# Faces visible after amending data for rotation are: up, West, and South
|
|
side_up = cap if data & 0b010000 else porous # Up
|
|
side_west = cap if data & 0b000010 else porous # West
|
|
side_south = cap if data & 0b000001 else porous # South
|
|
side_south = side_south.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
return self.build_full_block(side_up, None, None, side_west, side_south)
|
|
|
|
|
|
# iron bars and glass pane
|
|
# TODO glass pane is not a sprite, it has a texture for the side,
|
|
# at the moment is not used
|
|
@material(blockid=[101,102, 160], data=list(range(256)), transparent=True, nospawn=True)
|
|
def panes(self, blockid, data):
|
|
# no rotation, uses pseudo data
|
|
if blockid == 101:
|
|
# iron bars
|
|
t = self.load_image_texture("assets/minecraft/textures/block/iron_bars.png")
|
|
elif blockid == 160:
|
|
t = self.load_image_texture("assets/minecraft/textures/block/%s_stained_glass.png" % color_map[data & 0xf])
|
|
else:
|
|
# glass panes
|
|
t = self.load_image_texture("assets/minecraft/textures/block/glass.png")
|
|
left = t.copy()
|
|
right = t.copy()
|
|
center = 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))
|
|
ImageDraw.Draw(center).rectangle((0,0,6,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(center).rectangle((9,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
up_center = self.transform_image_side(center)
|
|
up_left = self.transform_image_side(left)
|
|
up_right = self.transform_image_side(right).transpose(Image.FLIP_TOP_BOTTOM)
|
|
dw_right = self.transform_image_side(right)
|
|
dw_left = self.transform_image_side(left).transpose(Image.FLIP_TOP_BOTTOM)
|
|
|
|
# Create img to compose the texture
|
|
img = Image.new("RGBA", (24,24), self.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.
|
|
|
|
# the lower 4 bits encode color, the upper 4 encode adjencies
|
|
data = data >> 4
|
|
|
|
if data == 0:
|
|
alpha_over(img, up_center, (6, 3), up_center) # center
|
|
else:
|
|
def draw_top_left():
|
|
alpha_over(img, up_left, (6, 3), up_left) # top left
|
|
|
|
def draw_top_right():
|
|
alpha_over(img, up_right, (6, 3), up_right) # top right
|
|
|
|
def draw_bottom_right():
|
|
alpha_over(img, dw_right, (6, 3), dw_right) # bottom right
|
|
|
|
def draw_bottom_left():
|
|
alpha_over(img, dw_left, (6, 3), dw_left) # bottom left
|
|
|
|
draw_funcs = [draw_top_left, draw_top_right, draw_bottom_right, draw_bottom_left]
|
|
|
|
if (data & 0b0001) == 1:
|
|
draw_funcs[(self.rotation + 0) % len(draw_funcs)]()
|
|
if (data & 0b0010) == 2:
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
if (data & 0b0100) == 4:
|
|
draw_funcs[(self.rotation + 2) % len(draw_funcs)]()
|
|
if (data & 0b1000) == 8:
|
|
draw_funcs[(self.rotation + 3) % len(draw_funcs)]()
|
|
|
|
return img
|
|
|
|
# melon
|
|
block(blockid=103, top_image="assets/minecraft/textures/block/melon_top.png", side_image="assets/minecraft/textures/block/melon_side.png", solid=True)
|
|
|
|
# pumpkin and melon stem
|
|
# TODO To render it as in game needs from pseudo data and ancil data:
|
|
# once fully grown the stem bends to the melon/pumpkin block,
|
|
# at the moment only render the growing stem
|
|
@material(blockid=[104,105], data=list(range(8)), transparent=True)
|
|
def stem(self, blockid, data):
|
|
# 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 = self.load_image_texture("assets/minecraft/textures/block/melon_stem.png").copy()
|
|
img = Image.new("RGBA", (16,16), self.bgcolor)
|
|
alpha_over(img, t, (0, int(16 - 16*((data + 1)/8.))), t)
|
|
img = self.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 = self.tint_texture(img, (211,169,116))
|
|
return img
|
|
|
|
|
|
# nether vines
|
|
billboard(blockid=1012, imagename="assets/minecraft/textures/block/twisting_vines.png")
|
|
billboard(blockid=1013, imagename="assets/minecraft/textures/block/twisting_vines_plant.png")
|
|
billboard(blockid=1014, imagename="assets/minecraft/textures/block/weeping_vines.png")
|
|
billboard(blockid=1015, imagename="assets/minecraft/textures/block/weeping_vines_plant.png")
|
|
|
|
# vines
|
|
@material(blockid=106, data=list(range(32)), transparent=True, solid=False, nospawn=True)
|
|
def vines(self, blockid, data):
|
|
# Re-arrange the bits in data based on self.rotation
|
|
# rotation bit: 54321
|
|
# 0 UENWS
|
|
# 1 UNWSE
|
|
# 2 UWSEN
|
|
# 3 USENW
|
|
if self.rotation in [1, 2, 3]:
|
|
bit_map = {1: [5, 3, 2, 1, 4],
|
|
2: [5, 2, 1, 4, 3],
|
|
3: [5, 1, 4, 3, 2]}
|
|
new_data = 0
|
|
|
|
# Add the ith bit to new_data then shift left one at a time,
|
|
# re-ordering data's bits in the order specified in bit_map
|
|
for i in bit_map[self.rotation]:
|
|
new_data = new_data << 1
|
|
new_data |= (data >> (i - 1)) & 1
|
|
data = new_data
|
|
|
|
# decode data and prepare textures
|
|
raw_texture = self.load_image_texture("assets/minecraft/textures/block/vine.png")
|
|
|
|
side_up = raw_texture if data & 0b10000 else None # Up
|
|
side_east = raw_texture if data & 0b01000 else None # East
|
|
side_north = raw_texture if data & 0b00100 else None # North
|
|
side_west = raw_texture if data & 0b00010 else None # West
|
|
side_south = raw_texture if data & 0b00001 else None # South
|
|
|
|
return self.build_full_block(side_up, side_north, side_east, side_west, side_south)
|
|
|
|
|
|
# fence gates
|
|
@material(blockid=[107, 183, 184, 185, 186, 187, 513, 514], data=list(range(8)), transparent=True, nospawn=True)
|
|
def fence_gate(self, blockid, data):
|
|
|
|
# rotation
|
|
opened = False
|
|
if data & 0x4:
|
|
data = data & 0x3
|
|
opened = True
|
|
if self.rotation == 1:
|
|
if data == 0: data = 1
|
|
elif data == 1: data = 2
|
|
elif data == 2: data = 3
|
|
elif data == 3: data = 0
|
|
elif self.rotation == 2:
|
|
if data == 0: data = 2
|
|
elif data == 1: data = 3
|
|
elif data == 2: data = 0
|
|
elif data == 3: data = 1
|
|
elif self.rotation == 3:
|
|
if data == 0: data = 3
|
|
elif data == 1: data = 0
|
|
elif data == 2: data = 1
|
|
elif data == 3: data = 2
|
|
if opened:
|
|
data = data | 0x4
|
|
|
|
# create the closed gate side
|
|
if blockid == 107: # Oak
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png").copy()
|
|
elif blockid == 183: # Spruce
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png").copy()
|
|
elif blockid == 184: # Birch
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/birch_planks.png").copy()
|
|
elif blockid == 185: # Jungle
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png").copy()
|
|
elif blockid == 186: # Dark Oak
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png").copy()
|
|
elif blockid == 187: # Acacia
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png").copy()
|
|
elif blockid == 513: # Crimson
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png").copy()
|
|
elif blockid == 514: # Warped
|
|
gate_side = self.load_image_texture("assets/minecraft/textures/block/warped_planks.png").copy()
|
|
else:
|
|
return None
|
|
|
|
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 = self.transform_image_side(gate_side.transpose(Image.FLIP_LEFT_RIGHT))
|
|
gate_side = self.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), self.bgcolor)
|
|
|
|
if data & 0x4:
|
|
# opened
|
|
data = data & 0x3
|
|
if data == 0:
|
|
alpha_over(img, gate_side, (2,8), gate_side)
|
|
alpha_over(img, gate_side, (13,3), gate_side)
|
|
elif data == 1:
|
|
alpha_over(img, gate_other_side, (-1,3), gate_other_side)
|
|
alpha_over(img, gate_other_side, (10,8), gate_other_side)
|
|
elif data == 2:
|
|
alpha_over(img, mirror_gate_side, (-1,7), mirror_gate_side)
|
|
alpha_over(img, mirror_gate_side, (10,2), mirror_gate_side)
|
|
elif data == 3:
|
|
alpha_over(img, mirror_gate_other_side, (2,1), mirror_gate_other_side)
|
|
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:
|
|
alpha_over(img, gate_other_side, pos_top_right, gate_other_side)
|
|
alpha_over(img, mirror_gate_other_side, pos_bottom_left, mirror_gate_other_side)
|
|
elif data == 1 or data == 3:
|
|
alpha_over(img, gate_side, pos_top_left, gate_side)
|
|
alpha_over(img, mirror_gate_side, pos_bottom_right, mirror_gate_side)
|
|
|
|
return img
|
|
|
|
# mycelium
|
|
block(blockid=110, top_image="assets/minecraft/textures/block/mycelium_top.png", side_image="assets/minecraft/textures/block/mycelium_side.png")
|
|
# warped_nylium & crimson_nylium
|
|
block(blockid=1006, top_image="assets/minecraft/textures/block/warped_nylium.png", side_image="assets/minecraft/textures/block/warped_nylium_side.png")
|
|
block(blockid=1007, top_image="assets/minecraft/textures/block/crimson_nylium.png", side_image="assets/minecraft/textures/block/crimson_nylium_side.png")
|
|
|
|
|
|
# lilypad
|
|
# At the moment of writing this lilypads has no ancil data and their
|
|
# orientation depends on their position on the map. So it uses pseudo
|
|
# ancildata.
|
|
@material(blockid=111, data=list(range(4)), transparent=True)
|
|
def lilypad(self, blockid, data):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/lily_pad.png").copy()
|
|
if data == 0:
|
|
t = t.rotate(180)
|
|
elif data == 1:
|
|
t = t.rotate(270)
|
|
elif data == 2:
|
|
t = t
|
|
elif data == 3:
|
|
t = t.rotate(90)
|
|
|
|
return self.build_full_block(None, None, None, None, None, t)
|
|
|
|
# nether bricks
|
|
@material(blockid=112, data=list(range(3)), solid=True)
|
|
def nether_bricks(self, blockid, data):
|
|
if data == 0: # normal
|
|
t = self.load_image_texture("assets/minecraft/textures/block/nether_bricks.png")
|
|
elif data == 1: # cracked
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cracked_nether_bricks.png")
|
|
elif data == 2: # chiseled
|
|
t = self.load_image_texture("assets/minecraft/textures/block/chiseled_nether_bricks.png")
|
|
|
|
img = self.build_full_block(t, None, None, t, t)
|
|
|
|
return img
|
|
|
|
# nether wart
|
|
@material(blockid=115, data=list(range(4)), transparent=True)
|
|
def nether_wart(self, blockid, data):
|
|
if data == 0: # just come up
|
|
t = self.load_image_texture("assets/minecraft/textures/block/nether_wart_stage0.png")
|
|
elif data in (1, 2):
|
|
t = self.load_image_texture("assets/minecraft/textures/block/nether_wart_stage1.png")
|
|
else: # fully grown
|
|
t = self.load_image_texture("assets/minecraft/textures/block/nether_wart_stage2.png")
|
|
|
|
# use the same technic as tall grass
|
|
img = self.build_billboard(t)
|
|
|
|
return img
|
|
|
|
# enchantment table
|
|
# TODO there's no book at the moment
|
|
@material(blockid=116, transparent=True, nodata=True)
|
|
def enchantment_table(self, blockid, data):
|
|
# no book at the moment
|
|
top = self.load_image_texture("assets/minecraft/textures/block/enchanting_table_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/enchanting_table_side.png")
|
|
img = self.build_full_block((top, 4), None, None, side, side)
|
|
|
|
return img
|
|
|
|
# brewing stand
|
|
# TODO this is a place holder, is a 2d image pasted
|
|
@material(blockid=117, data=list(range(5)), transparent=True)
|
|
def brewing_stand(self, blockid, data):
|
|
base = self.load_image_texture("assets/minecraft/textures/block/brewing_stand_base.png")
|
|
img = self.build_full_block(None, None, None, None, None, base)
|
|
t = self.load_image_texture("assets/minecraft/textures/block/brewing_stand.png")
|
|
stand = self.build_billboard(t)
|
|
alpha_over(img,stand,(0,-2))
|
|
return img
|
|
|
|
|
|
# cauldron
|
|
@material(blockid=118, data=list(range(16)), transparent=True, solid=True, nospawn=True)
|
|
def cauldron(self, blockid, data):
|
|
side = self.load_image_texture("assets/minecraft/textures/block/cauldron_side.png").copy()
|
|
top = self.load_image_texture("assets/minecraft/textures/block/cauldron_top.png")
|
|
filltype = (data & (3 << 2)) >> 2
|
|
if filltype == 3:
|
|
water = self.transform_image_top(self.load_image_texture("assets/minecraft/textures/block/powder_snow.png"))
|
|
elif filltype == 2:
|
|
water = self.transform_image_top(self.load_image_texture("assets/minecraft/textures/block/lava_still.png"))
|
|
else: # filltype == 1 or 0
|
|
water = self.transform_image_top(self.load_image_texture("water.png"))
|
|
# Side texture isn't transparent between the feet, so adjust the texture
|
|
ImageDraw.Draw(side).rectangle((5, 14, 11, 16), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
|
|
level = (data & 3)
|
|
if level == 0: # Empty
|
|
img = self.build_full_block(top, side, side, side, side)
|
|
else: # Part or fully filled
|
|
# Is filled in increments of a third, with level indicating how many thirds are filled
|
|
img = self.build_full_block(None, side, side, None, None)
|
|
alpha_over(img, water, (0, 12 - level * 4), water)
|
|
img2 = self.build_full_block(top, None, None, side, side)
|
|
alpha_over(img, img2, (0, 0), img2)
|
|
return img
|
|
|
|
|
|
# end portal and end_gateway
|
|
@material(blockid=[119,209], transparent=True, nodata=True)
|
|
def end_portal(self, blockid, data):
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
# generate a black texure with white, blue and grey dots resembling stars
|
|
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)
|
|
if blockid == 209: # end_gateway
|
|
return self.build_block(t, t)
|
|
|
|
t = self.transform_image_top(t)
|
|
alpha_over(img, t, (0,0), t)
|
|
|
|
return img
|
|
|
|
|
|
# end portal frame (data range 8 to get all orientations of filled)
|
|
@material(blockid=120, data=list(range(8)), transparent=True, solid=True, nospawn=True)
|
|
def end_portal_frame(self, blockid, data):
|
|
# Do rotation, only seems to affect ender eye & top of frame
|
|
data = data & 0b100 | ((self.rotation + (data & 0b11)) % 4)
|
|
|
|
top = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_top.png").copy()
|
|
top = top.rotate((data % 4) * 90)
|
|
side = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_side.png")
|
|
img = self.build_full_block((top, 4), None, None, side, side)
|
|
if data & 0x4 == 0x4: # ender eye on it
|
|
# generate the eye
|
|
eye_t = self.load_image_texture("assets/minecraft/textures/block/end_portal_frame_eye.png").copy()
|
|
eye_t_s = eye_t.copy()
|
|
# cut out from the texture the side and the top of the eye
|
|
ImageDraw.Draw(eye_t).rectangle((0, 0, 15, 4), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(eye_t_s).rectangle((0, 4, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
# transform images and paste
|
|
eye = self.transform_image_top(eye_t.rotate((data % 4) * 90))
|
|
eye_s = self.transform_image_side(eye_t_s)
|
|
eye_os = eye_s.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, eye_s, (5, 5), eye_s)
|
|
alpha_over(img, eye_os, (9, 5), eye_os)
|
|
alpha_over(img, eye, (0, 0), eye)
|
|
|
|
return img
|
|
|
|
|
|
# end stone
|
|
block(blockid=121, top_image="assets/minecraft/textures/block/end_stone.png")
|
|
|
|
# dragon egg
|
|
# NOTE: this isn't a block, but I think it's better than nothing
|
|
block(blockid=122, top_image="assets/minecraft/textures/block/dragon_egg.png")
|
|
|
|
# inactive redstone lamp
|
|
block(blockid=123, top_image="assets/minecraft/textures/block/redstone_lamp.png")
|
|
|
|
# active redstone lamp
|
|
block(blockid=124, top_image="assets/minecraft/textures/block/redstone_lamp_on.png")
|
|
|
|
# daylight sensor.
|
|
@material(blockid=[151,178], transparent=True)
|
|
def daylight_sensor(self, blockid, data):
|
|
if blockid == 151: # daylight sensor
|
|
top = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_top.png")
|
|
else: # inverted daylight sensor
|
|
top = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_inverted_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/daylight_detector_side.png")
|
|
|
|
# cut the side texture in half
|
|
mask = side.crop((0,8,16,16))
|
|
side = Image.new(side.mode, side.size, self.bgcolor)
|
|
alpha_over(side, mask,(0,0,16,8), mask)
|
|
|
|
# plain slab
|
|
top = self.transform_image_top(top)
|
|
side = self.transform_image_side(side)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
sidealpha = side.split()[3]
|
|
side = ImageEnhance.Brightness(side).enhance(0.9)
|
|
side.putalpha(sidealpha)
|
|
othersidealpha = otherside.split()[3]
|
|
otherside = ImageEnhance.Brightness(otherside).enhance(0.8)
|
|
otherside.putalpha(othersidealpha)
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, side, (0,12), side)
|
|
alpha_over(img, otherside, (12,12), otherside)
|
|
alpha_over(img, top, (0,6), top)
|
|
|
|
return img
|
|
|
|
|
|
# wooden double and normal slabs
|
|
# these are the new wooden slabs, blockids 43 44 still have wooden
|
|
# slabs, but those are unobtainable without cheating
|
|
@material(blockid=[125, 126], data=list(range(16)), transparent=(44,), solid=True)
|
|
def wooden_slabs(self, blockid, data):
|
|
texture = data & 7
|
|
if texture== 0: # oak
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/oak_planks.png")
|
|
elif texture== 1: # spruce
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/spruce_planks.png")
|
|
elif texture== 2: # birch
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/birch_planks.png")
|
|
elif texture== 3: # jungle
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/jungle_planks.png")
|
|
elif texture== 4: # acacia
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/acacia_planks.png")
|
|
elif texture== 5: # dark wood
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png")
|
|
elif texture== 6: # crimson
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/crimson_planks.png")
|
|
elif texture== 7: # warped
|
|
top = side = self.load_image_texture("assets/minecraft/textures/block/warped_planks.png")
|
|
else:
|
|
return None
|
|
|
|
if blockid == 125: # double slab
|
|
return self.build_block(top, side)
|
|
|
|
return self.build_slab_block(top, side, data & 8 == 8);
|
|
|
|
# emerald ore
|
|
block(blockid=129, top_image="assets/minecraft/textures/block/emerald_ore.png")
|
|
|
|
# emerald block
|
|
block(blockid=133, top_image="assets/minecraft/textures/block/emerald_block.png")
|
|
|
|
# cocoa plant
|
|
@material(blockid=127, data=list(range(12)), transparent=True)
|
|
def cocoa_plant(self, blockid, data):
|
|
orientation = data & 3
|
|
# rotation
|
|
if self.rotation == 1:
|
|
if orientation == 0: orientation = 1
|
|
elif orientation == 1: orientation = 2
|
|
elif orientation == 2: orientation = 3
|
|
elif orientation == 3: orientation = 0
|
|
elif self.rotation == 2:
|
|
if orientation == 0: orientation = 2
|
|
elif orientation == 1: orientation = 3
|
|
elif orientation == 2: orientation = 0
|
|
elif orientation == 3: orientation = 1
|
|
elif self.rotation == 3:
|
|
if orientation == 0: orientation = 3
|
|
elif orientation == 1: orientation = 0
|
|
elif orientation == 2: orientation = 1
|
|
elif orientation == 3: orientation = 2
|
|
|
|
size = data & 12
|
|
if size == 8: # big
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cocoa_stage2.png")
|
|
c_left = (0,3)
|
|
c_right = (8,3)
|
|
c_top = (5,2)
|
|
elif size == 4: # normal
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cocoa_stage1.png")
|
|
c_left = (-2,2)
|
|
c_right = (8,2)
|
|
c_top = (5,2)
|
|
elif size == 0: # small
|
|
t = self.load_image_texture("assets/minecraft/textures/block/cocoa_stage0.png")
|
|
c_left = (-3,2)
|
|
c_right = (6,2)
|
|
c_top = (5,2)
|
|
|
|
# let's get every texture piece necessary to do this
|
|
stalk = t.copy()
|
|
ImageDraw.Draw(stalk).rectangle((0,0,11,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(stalk).rectangle((12,4,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
top = t.copy() # warning! changes with plant size
|
|
ImageDraw.Draw(top).rectangle((0,7,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(top).rectangle((7,0,16,6),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
side = t.copy() # warning! changes with plant size
|
|
ImageDraw.Draw(side).rectangle((0,0,6,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(side).rectangle((0,0,16,3),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(side).rectangle((0,14,16,16),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
# first compose the block of the cocoa plant
|
|
block = Image.new("RGBA", (24,24), self.bgcolor)
|
|
tmp = self.transform_image_side(side).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over (block, tmp, c_right,tmp) # right side
|
|
tmp = tmp.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over (block, tmp, c_left,tmp) # left side
|
|
tmp = self.transform_image_top(top)
|
|
alpha_over(block, tmp, c_top,tmp)
|
|
if size == 0:
|
|
# fix a pixel hole
|
|
block.putpixel((6,9), block.getpixel((6,10)))
|
|
|
|
# compose the cocoa plant
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
if orientation in (2,3): # south and west
|
|
tmp = self.transform_image_side(stalk).transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, block,(-1,-2), block)
|
|
alpha_over(img, tmp, (4,-2), tmp)
|
|
if orientation == 3:
|
|
img = img.transpose(Image.FLIP_LEFT_RIGHT)
|
|
elif orientation in (0,1): # north and east
|
|
tmp = self.transform_image_side(stalk.transpose(Image.FLIP_LEFT_RIGHT))
|
|
alpha_over(img, block,(-1,5), block)
|
|
alpha_over(img, tmp, (2,12), tmp)
|
|
if orientation == 0:
|
|
img = img.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
return img
|
|
|
|
# command block
|
|
@material(blockid=[137,210,211], solid=True, nodata=True)
|
|
def command_block(self, blockid, data):
|
|
if blockid == 210:
|
|
front = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_front.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_side.png")
|
|
back = self.load_image_texture("assets/minecraft/textures/block/repeating_command_block_back.png")
|
|
elif blockid == 211:
|
|
front = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_front.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_side.png")
|
|
back = self.load_image_texture("assets/minecraft/textures/block/chain_command_block_back.png")
|
|
else:
|
|
front = self.load_image_texture("assets/minecraft/textures/block/command_block_front.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/command_block_side.png")
|
|
back = self.load_image_texture("assets/minecraft/textures/block/command_block_back.png")
|
|
return self.build_full_block(side, side, back, front, side)
|
|
|
|
# beacon block
|
|
# at the moment of writing this, it seems the beacon block doens't use
|
|
# the data values
|
|
@material(blockid=138, transparent=True, nodata = True)
|
|
def beacon(self, blockid, data):
|
|
# generate the three pieces of the block
|
|
t = self.load_image_texture("assets/minecraft/textures/block/glass.png")
|
|
glass = self.build_block(t,t)
|
|
t = self.load_image_texture("assets/minecraft/textures/block/obsidian.png")
|
|
obsidian = self.build_full_block((t,12),None, None, t, t)
|
|
obsidian = obsidian.resize((20,20), Image.ANTIALIAS)
|
|
t = self.load_image_texture("assets/minecraft/textures/block/beacon.png")
|
|
crystal = self.build_block(t,t)
|
|
crystal = crystal.resize((16,16),Image.ANTIALIAS)
|
|
|
|
# compose the block
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, obsidian, (2, 4), obsidian)
|
|
alpha_over(img, crystal, (4,3), crystal)
|
|
alpha_over(img, glass, (0,0), glass)
|
|
|
|
return img
|
|
|
|
# cobblestone and mossy cobblestone walls, chorus plants, mossy stone brick walls
|
|
# one additional bit of data value added for mossy and cobblestone
|
|
@material(blockid=[199]+list(range(1792, 1812 + 1)), data=list(range(32)), transparent=True, nospawn=True)
|
|
def cobblestone_wall(self, blockid, data):
|
|
walls_id_to_tex = {
|
|
199: "assets/minecraft/textures/block/chorus_plant.png", # chorus plants
|
|
1792: "assets/minecraft/textures/block/andesite.png",
|
|
1793: "assets/minecraft/textures/block/bricks.png",
|
|
1794: "assets/minecraft/textures/block/cobblestone.png",
|
|
1795: "assets/minecraft/textures/block/diorite.png",
|
|
1796: "assets/minecraft/textures/block/end_stone_bricks.png",
|
|
1797: "assets/minecraft/textures/block/granite.png",
|
|
1798: "assets/minecraft/textures/block/mossy_cobblestone.png",
|
|
1799: "assets/minecraft/textures/block/mossy_stone_bricks.png",
|
|
1800: "assets/minecraft/textures/block/nether_bricks.png",
|
|
1801: "assets/minecraft/textures/block/prismarine.png",
|
|
1802: "assets/minecraft/textures/block/red_nether_bricks.png",
|
|
1803: "assets/minecraft/textures/block/red_sandstone.png",
|
|
1804: "assets/minecraft/textures/block/sandstone.png",
|
|
1805: "assets/minecraft/textures/block/stone_bricks.png",
|
|
1806: "assets/minecraft/textures/block/blackstone.png",
|
|
1807: "assets/minecraft/textures/block/polished_blackstone.png",
|
|
1808: "assets/minecraft/textures/block/polished_blackstone_bricks.png",
|
|
1809: "assets/minecraft/textures/block/cobbled_deepslate.png",
|
|
1810: "assets/minecraft/textures/block/polished_deepslate.png",
|
|
1811: "assets/minecraft/textures/block/deepslate_bricks.png",
|
|
1812: "assets/minecraft/textures/block/deepslate_tiles.png",
|
|
}
|
|
t = self.load_image_texture(walls_id_to_tex[blockid]).copy()
|
|
|
|
wall_pole_top = t.copy()
|
|
wall_pole_side = t.copy()
|
|
wall_side_top = t.copy()
|
|
wall_side = t.copy()
|
|
# _full is used for walls without pole
|
|
wall_side_top_full = t.copy()
|
|
wall_side_full = t.copy()
|
|
|
|
# generate the textures of the wall
|
|
ImageDraw.Draw(wall_pole_top).rectangle((0,0,3,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_pole_top).rectangle((12,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_pole_top).rectangle((0,0,15,3),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_pole_top).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
ImageDraw.Draw(wall_pole_side).rectangle((0,0,3,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_pole_side).rectangle((12,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
# Create the sides and the top of the pole
|
|
wall_pole_side = self.transform_image_side(wall_pole_side)
|
|
wall_pole_other_side = wall_pole_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
wall_pole_top = self.transform_image_top(wall_pole_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 = wall_pole_side.split()[3]
|
|
wall_pole_side = ImageEnhance.Brightness(wall_pole_side).enhance(0.8)
|
|
wall_pole_side.putalpha(sidealpha)
|
|
othersidealpha = wall_pole_other_side.split()[3]
|
|
wall_pole_other_side = ImageEnhance.Brightness(wall_pole_other_side).enhance(0.7)
|
|
wall_pole_other_side.putalpha(othersidealpha)
|
|
|
|
# Compose the wall pole
|
|
wall_pole = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(wall_pole,wall_pole_side, (3,4),wall_pole_side)
|
|
alpha_over(wall_pole,wall_pole_other_side, (9,4),wall_pole_other_side)
|
|
alpha_over(wall_pole,wall_pole_top, (0,0),wall_pole_top)
|
|
|
|
# create the sides and the top of a wall attached to a pole
|
|
ImageDraw.Draw(wall_side).rectangle((0,0,15,2),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side).rectangle((0,0,11,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side_top).rectangle((0,0,11,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side_top).rectangle((0,0,15,4),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side_top).rectangle((0,11,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
# full version, without pole
|
|
ImageDraw.Draw(wall_side_full).rectangle((0,0,15,2),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side_top_full).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
ImageDraw.Draw(wall_side_top_full).rectangle((0,4,15,15),outline=(0,0,0,0),fill=(0,0,0,0))
|
|
|
|
# compose the sides of a wall atached to a pole
|
|
tmp = Image.new("RGBA", (24,24), self.bgcolor)
|
|
wall_side = self.transform_image_side(wall_side)
|
|
wall_side_top = self.transform_image_top(wall_side_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 = wall_side.split()[3]
|
|
wall_side = ImageEnhance.Brightness(wall_side).enhance(0.7)
|
|
wall_side.putalpha(sidealpha)
|
|
|
|
alpha_over(tmp,wall_side, (0,0),wall_side)
|
|
alpha_over(tmp,wall_side_top, (-5,3),wall_side_top)
|
|
wall_side = tmp
|
|
wall_other_side = wall_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# compose the sides of the full wall
|
|
tmp = Image.new("RGBA", (24,24), self.bgcolor)
|
|
wall_side_full = self.transform_image_side(wall_side_full)
|
|
wall_side_top_full = self.transform_image_top(wall_side_top_full.rotate(90))
|
|
|
|
# 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 = wall_side_full.split()[3]
|
|
wall_side_full = ImageEnhance.Brightness(wall_side_full).enhance(0.7)
|
|
wall_side_full.putalpha(sidealpha)
|
|
|
|
alpha_over(tmp,wall_side_full, (4,0),wall_side_full)
|
|
alpha_over(tmp,wall_side_top_full, (3,-4),wall_side_top_full)
|
|
wall_side_full = tmp
|
|
wall_other_side_full = wall_side_full.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Create img to compose the wall
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
|
|
# Position wall imgs around the wall bit stick
|
|
pos_top_left = (-5,-2)
|
|
pos_bottom_left = (-8,4)
|
|
pos_top_right = (5,-3)
|
|
pos_bottom_right = (7,4)
|
|
|
|
# +x axis points top right direction
|
|
# +y axis points bottom right direction
|
|
# There are two special cases for wall without pole.
|
|
# Normal case:
|
|
# First compose the walls in the back of the image,
|
|
# then the pole and then the walls in the front.
|
|
if (data == 0b1010) or (data == 0b11010):
|
|
alpha_over(img, wall_other_side_full,(0,2), wall_other_side_full)
|
|
elif (data == 0b0101) or (data == 0b10101):
|
|
alpha_over(img, wall_side_full,(0,2), wall_side_full)
|
|
else:
|
|
if (data & 0b0001) == 1:
|
|
alpha_over(img,wall_side, pos_top_left,wall_side) # top left
|
|
if (data & 0b1000) == 8:
|
|
alpha_over(img,wall_other_side, pos_top_right,wall_other_side) # top right
|
|
|
|
alpha_over(img,wall_pole,(0,0),wall_pole)
|
|
|
|
if (data & 0b0010) == 2:
|
|
alpha_over(img,wall_other_side, pos_bottom_left,wall_other_side) # bottom left
|
|
if (data & 0b0100) == 4:
|
|
alpha_over(img,wall_side, pos_bottom_right,wall_side) # bottom right
|
|
|
|
return img
|
|
|
|
# carrots, potatoes
|
|
@material(blockid=[141,142], data=list(range(8)), transparent=True, nospawn=True)
|
|
def crops4(self, blockid, data):
|
|
# carrots and potatoes have 8 data, but only 4 visual stages
|
|
stage = {0:0,
|
|
1:0,
|
|
2:1,
|
|
3:1,
|
|
4:2,
|
|
5:2,
|
|
6:2,
|
|
7:3}[data]
|
|
if blockid == 141: # carrots
|
|
raw_crop = self.load_image_texture("assets/minecraft/textures/block/carrots_stage%d.png" % stage)
|
|
else: # potatoes
|
|
raw_crop = self.load_image_texture("assets/minecraft/textures/block/potatoes_stage%d.png" % stage)
|
|
crop1 = self.transform_image_top(raw_crop)
|
|
crop2 = self.transform_image_side(raw_crop)
|
|
crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, crop1, (0,12), crop1)
|
|
alpha_over(img, crop2, (6,3), crop2)
|
|
alpha_over(img, crop3, (6,3), crop3)
|
|
return img
|
|
|
|
|
|
# anvils
|
|
@material(blockid=145, data=list(range(12)), transparent=True, nospawn=True)
|
|
def anvil(self, blockid, data):
|
|
# anvils only have two orientations, invert it for rotations 1 and 3
|
|
orientation = data & 0x1
|
|
if self.rotation in (1, 3):
|
|
if orientation == 1:
|
|
orientation = 0
|
|
else:
|
|
orientation = 1
|
|
|
|
# get the correct textures
|
|
# the bits 0x4 and 0x8 determine how damaged is the anvil
|
|
if (data & 0xc) == 0: # non damaged anvil
|
|
top = self.load_image_texture("assets/minecraft/textures/block/anvil_top.png")
|
|
elif (data & 0xc) == 0x4: # slightly damaged
|
|
top = self.load_image_texture("assets/minecraft/textures/block/chipped_anvil_top.png")
|
|
elif (data & 0xc) == 0x8: # very damaged
|
|
top = self.load_image_texture("assets/minecraft/textures/block/damaged_anvil_top.png")
|
|
# everything else use this texture
|
|
big_side = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy()
|
|
small_side = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy()
|
|
base = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy()
|
|
small_base = self.load_image_texture("assets/minecraft/textures/block/anvil.png").copy()
|
|
|
|
# cut needed patterns
|
|
ImageDraw.Draw(big_side).rectangle((0, 8, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_side).rectangle((0, 0, 2, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_side).rectangle((13, 0, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_side).rectangle((0, 8, 15, 15), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
ImageDraw.Draw(base).rectangle((0, 0, 15, 15), outline=(0, 0, 0, 0))
|
|
ImageDraw.Draw(base).rectangle((1, 1, 14, 14), outline=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_base).rectangle((0, 0, 15, 15), outline=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_base).rectangle((1, 1, 14, 14), outline=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_base).rectangle((2, 2, 13, 13), outline=(0, 0, 0, 0))
|
|
ImageDraw.Draw(small_base).rectangle((3, 3, 12, 12), outline=(0, 0, 0, 0))
|
|
|
|
# check orientation and compose the anvil
|
|
if orientation == 1: # bottom-left top-right
|
|
top = top.rotate(90)
|
|
left_side = small_side
|
|
left_pos = (1, 6)
|
|
right_side = big_side
|
|
right_pos = (10, 5)
|
|
else: # top-left bottom-right
|
|
right_side = small_side
|
|
right_pos = (12, 6)
|
|
left_side = big_side
|
|
left_pos = (3, 5)
|
|
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
|
|
# darken sides
|
|
alpha = big_side.split()[3]
|
|
big_side = ImageEnhance.Brightness(big_side).enhance(0.8)
|
|
big_side.putalpha(alpha)
|
|
alpha = small_side.split()[3]
|
|
small_side = ImageEnhance.Brightness(small_side).enhance(0.9)
|
|
small_side.putalpha(alpha)
|
|
alpha = base.split()[3]
|
|
base_d = ImageEnhance.Brightness(base).enhance(0.8)
|
|
base_d.putalpha(alpha)
|
|
|
|
# compose
|
|
base = self.transform_image_top(base)
|
|
base_d = self.transform_image_top(base_d)
|
|
small_base = self.transform_image_top(small_base)
|
|
top = self.transform_image_top(top)
|
|
|
|
alpha_over(img, base_d, (0, 12), base_d)
|
|
alpha_over(img, base_d, (0, 11), base_d)
|
|
alpha_over(img, base_d, (0, 10), base_d)
|
|
alpha_over(img, small_base, (0, 10), small_base)
|
|
|
|
alpha_over(img, top, (0, 1), top) # Fix gap between block edges
|
|
alpha_over(img, top, (0, 0), top)
|
|
|
|
left_side = self.transform_image_side(left_side)
|
|
right_side = self.transform_image_side(right_side).transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
alpha_over(img, left_side, left_pos, left_side)
|
|
alpha_over(img, right_side, right_pos, right_side)
|
|
|
|
return img
|
|
|
|
|
|
# block of redstone
|
|
block(blockid=152, top_image="assets/minecraft/textures/block/redstone_block.png")
|
|
|
|
# nether quartz ore
|
|
block(blockid=153, top_image="assets/minecraft/textures/block/nether_quartz_ore.png")
|
|
|
|
# block of quartz
|
|
@material(blockid=155, data=list(range(5)), solid=True)
|
|
def quartz_block(self, blockid, data):
|
|
|
|
if data in (0,1): # normal and chiseled quartz block
|
|
if data == 0:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/quartz_block_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/quartz_block_side.png")
|
|
else:
|
|
top = self.load_image_texture("assets/minecraft/textures/block/chiseled_quartz_block_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/chiseled_quartz_block.png")
|
|
return self.build_block(top, side)
|
|
|
|
# pillar quartz block with orientation
|
|
top = self.load_image_texture("assets/minecraft/textures/block/quartz_pillar_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/quartz_pillar.png").copy()
|
|
if data == 2: # vertical
|
|
return self.build_block(top, side)
|
|
elif data == 3: # north-south oriented
|
|
if self.rotation in (0,2):
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
return self.build_full_block(side, None, None, side.rotate(90), top)
|
|
|
|
elif data == 4: # east-west oriented
|
|
if self.rotation in (0,2):
|
|
return self.build_full_block(side, None, None, side.rotate(90), top)
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
|
|
# hopper
|
|
@material(blockid=154, data=list(range(4)), transparent=True)
|
|
def hopper(self, blockid, data):
|
|
#build the top
|
|
side = self.load_image_texture("assets/minecraft/textures/block/hopper_outside.png")
|
|
top = self.load_image_texture("assets/minecraft/textures/block/hopper_top.png")
|
|
bottom = self.load_image_texture("assets/minecraft/textures/block/hopper_inside.png")
|
|
hop_top = self.build_full_block((top,10), side, side, side, side, side)
|
|
|
|
#build a solid block for mid/top
|
|
hop_mid = self.build_full_block((top,5), side, side, side, side, side)
|
|
hop_bot = self.build_block(side,side)
|
|
|
|
hop_mid = hop_mid.resize((17,17),Image.ANTIALIAS)
|
|
hop_bot = hop_bot.resize((10,10),Image.ANTIALIAS)
|
|
|
|
#compose the final block
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, hop_bot, (7,14), hop_bot)
|
|
alpha_over(img, hop_mid, (3,3), hop_mid)
|
|
alpha_over(img, hop_top, (0,-6), hop_top)
|
|
|
|
return img
|
|
|
|
# slime block
|
|
block(blockid=165, top_image="assets/minecraft/textures/block/slime_block.png")
|
|
|
|
# prismarine block
|
|
@material(blockid=168, data=list(range(3)), solid=True)
|
|
def prismarine_block(self, blockid, data):
|
|
|
|
if data == 0: # prismarine
|
|
t = self.load_image_texture("assets/minecraft/textures/block/prismarine.png")
|
|
elif data == 1: # prismarine bricks
|
|
t = self.load_image_texture("assets/minecraft/textures/block/prismarine_bricks.png")
|
|
elif data == 2: # dark prismarine
|
|
t = self.load_image_texture("assets/minecraft/textures/block/dark_prismarine.png")
|
|
|
|
img = self.build_block(t, t)
|
|
|
|
return img
|
|
|
|
# sea lantern
|
|
block(blockid=169, top_image="assets/minecraft/textures/block/sea_lantern.png")
|
|
|
|
# hay block
|
|
@material(blockid=170, data=list(range(9)), solid=True)
|
|
def hayblock(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/hay_block_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/hay_block_side.png")
|
|
|
|
if self.rotation == 1:
|
|
if data == 4: data = 8
|
|
elif data == 8: data = 4
|
|
elif self.rotation == 3:
|
|
if data == 4: data = 8
|
|
elif data == 8: data = 4
|
|
|
|
# choose orientation and paste textures
|
|
if data == 4: # east-west orientation
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
elif data == 8: # north-south orientation
|
|
return self.build_full_block(side, None, None, side.rotate(90), top)
|
|
else:
|
|
return self.build_block(top, side)
|
|
|
|
|
|
# carpet - wool block that's small?
|
|
@material(blockid=171, data=list(range(17)), transparent=True)
|
|
def carpet(self, blockid, data):
|
|
if data < 16:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_wool.png" % color_map[data])
|
|
elif data == 16:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/moss_block.png")
|
|
|
|
return self.build_full_block((texture,15),texture,texture,texture,texture)
|
|
|
|
#clay block
|
|
block(blockid=172, top_image="assets/minecraft/textures/block/terracotta.png")
|
|
|
|
#stained hardened clay
|
|
@material(blockid=159, data=list(range(16)), solid=True)
|
|
def stained_clay(self, blockid, data):
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_terracotta.png" % color_map[data])
|
|
|
|
return self.build_block(texture,texture)
|
|
|
|
#coal block
|
|
block(blockid=173, top_image="assets/minecraft/textures/block/coal_block.png")
|
|
|
|
# packed ice block
|
|
block(blockid=174, top_image="assets/minecraft/textures/block/packed_ice.png")
|
|
|
|
#blue ice
|
|
block(blockid=11312, top_image="assets/minecraft/textures/block/blue_ice.png")
|
|
|
|
#smooth stones
|
|
block(blockid=11313, top_image="assets/minecraft/textures/block/smooth_stone.png") # stone
|
|
block(blockid=11314, top_image="assets/minecraft/textures/block/sandstone_top.png") # sandstone
|
|
block(blockid=11315, top_image="assets/minecraft/textures/block/red_sandstone_top.png") # red sandstone
|
|
|
|
#coral blocks
|
|
block(blockid=11316, top_image="assets/minecraft/textures/block/brain_coral_block.png")
|
|
block(blockid=11317, top_image="assets/minecraft/textures/block/bubble_coral_block.png")
|
|
block(blockid=11318, top_image="assets/minecraft/textures/block/fire_coral_block.png")
|
|
block(blockid=11319, top_image="assets/minecraft/textures/block/horn_coral_block.png")
|
|
block(blockid=11320, top_image="assets/minecraft/textures/block/tube_coral_block.png")
|
|
|
|
#dead coral blocks
|
|
block(blockid=11321, top_image="assets/minecraft/textures/block/dead_brain_coral_block.png")
|
|
block(blockid=11322, top_image="assets/minecraft/textures/block/dead_bubble_coral_block.png")
|
|
block(blockid=11323, top_image="assets/minecraft/textures/block/dead_fire_coral_block.png")
|
|
block(blockid=11324, top_image="assets/minecraft/textures/block/dead_horn_coral_block.png")
|
|
block(blockid=11325, top_image="assets/minecraft/textures/block/dead_tube_coral_block.png")
|
|
|
|
@material(blockid=175, data=list(range(16)), transparent=True)
|
|
def flower(self, blockid, data):
|
|
double_plant_map = ["sunflower", "lilac", "tall_grass", "large_fern", "rose_bush", "peony", "peony", "peony"]
|
|
plant = double_plant_map[data & 0x7]
|
|
|
|
if data & 0x8:
|
|
part = "top"
|
|
else:
|
|
part = "bottom"
|
|
|
|
png = "assets/minecraft/textures/block/%s_%s.png" % (plant,part)
|
|
texture = self.load_image_texture(png)
|
|
img = self.build_billboard(texture)
|
|
|
|
#sunflower top
|
|
if data == 8:
|
|
bloom_tex = self.load_image_texture("assets/minecraft/textures/block/sunflower_front.png")
|
|
alpha_over(img, bloom_tex.resize((14, 11), Image.ANTIALIAS), (5,5))
|
|
|
|
return img
|
|
|
|
# chorus flower
|
|
@material(blockid=200, data=list(range(6)), solid=True)
|
|
def chorus_flower(self, blockid, data):
|
|
# aged 5, dead
|
|
if data == 5:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/chorus_flower_dead.png")
|
|
else:
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/chorus_flower.png")
|
|
|
|
return self.build_block(texture,texture)
|
|
|
|
# purpur block
|
|
block(blockid=201, top_image="assets/minecraft/textures/block/purpur_block.png")
|
|
|
|
# purpur pillar
|
|
@material(blockid=202, data=list(range(3)), solid=True)
|
|
def purpur_pillar(self, blockid, data):
|
|
top=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar_top.png")
|
|
side=self.load_image_texture("assets/minecraft/textures/block/purpur_pillar.png")
|
|
return self.build_axis_block(top, side, data)
|
|
|
|
# end brick
|
|
block(blockid=206, top_image="assets/minecraft/textures/block/end_stone_bricks.png")
|
|
|
|
# frosted ice
|
|
@material(blockid=212, data=list(range(4)), solid=True)
|
|
def frosted_ice(self, blockid, data):
|
|
img = self.load_image_texture("assets/minecraft/textures/block/frosted_ice_%d.png" % data)
|
|
return self.build_block(img, img)
|
|
|
|
# magma block
|
|
block(blockid=213, top_image="assets/minecraft/textures/block/magma.png")
|
|
|
|
# nether wart block
|
|
block(blockid=214, top_image="assets/minecraft/textures/block/nether_wart_block.png")
|
|
# warped wart block
|
|
block(blockid=1010, top_image="assets/minecraft/textures/block/warped_wart_block.png")
|
|
|
|
# red nether brick
|
|
block(blockid=215, top_image="assets/minecraft/textures/block/red_nether_bricks.png")
|
|
|
|
@material(blockid=216, data=list(range(12)), solid=True)
|
|
def boneblock(self, blockid, data):
|
|
# extract orientation
|
|
boneblock_orientation = data & 12
|
|
if self.rotation == 1:
|
|
if boneblock_orientation == 4: boneblock_orientation = 8
|
|
elif boneblock_orientation == 8: boneblock_orientation = 4
|
|
elif self.rotation == 3:
|
|
if boneblock_orientation == 4: boneblock_orientation = 8
|
|
elif boneblock_orientation == 8: boneblock_orientation = 4
|
|
|
|
top = self.load_image_texture("assets/minecraft/textures/block/bone_block_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/bone_block_side.png")
|
|
|
|
# choose orientation and paste textures
|
|
if boneblock_orientation == 0:
|
|
return self.build_block(top, side)
|
|
elif boneblock_orientation == 4: # east-west orientation
|
|
return self.build_full_block(side.rotate(90), None, None, top, side.rotate(90))
|
|
elif boneblock_orientation == 8: # north-south orientation
|
|
return self.build_full_block(side, None, None, side.rotate(270), top)
|
|
|
|
|
|
# observer
|
|
@material(blockid=218, data=[0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13], solid=True, nospawn=True)
|
|
def observer(self, blockid, data):
|
|
# Do rotation
|
|
if self.rotation in [1, 2, 3] and (data & 0b111) in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = (data & 0b1000) | rotation_map[self.rotation][data & 0b111]
|
|
|
|
front = self.load_image_texture("assets/minecraft/textures/block/observer_front.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/observer_side.png")
|
|
top = self.load_image_texture("assets/minecraft/textures/block/observer_top.png")
|
|
file_name_back = "observer_back_on" if data & 0b1000 else "observer_back"
|
|
back = self.load_image_texture("assets/minecraft/textures/block/%s.png" % file_name_back)
|
|
|
|
if data & 0b0111 == 0: # Down
|
|
img = self.build_full_block(back, None, None, side.rotate(90), top)
|
|
elif data & 0b0111 == 1: # Up
|
|
img = self.build_full_block(front.rotate(180), None, None, side.rotate(90), top.rotate(180))
|
|
elif data & 0b0111 == 2: # East
|
|
img = self.build_full_block(top.rotate(180), None, None, side, back)
|
|
elif data & 0b0111 == 3: # West
|
|
img = self.build_full_block(top, None, None, side, front)
|
|
elif data & 0b0111 == 4: # North
|
|
img = self.build_full_block(top.rotate(270), None, None, front, side)
|
|
elif data & 0b0111 == 5: # South
|
|
img = self.build_full_block(top.rotate(90), None, None, back, side)
|
|
|
|
return img
|
|
|
|
|
|
# shulker box
|
|
@material(blockid=list(range(219, 235)) + [257], data=list(range(6)), solid=True, nospawn=True)
|
|
def shulker_box(self, blockid, data):
|
|
# Do rotation
|
|
if self.rotation in [1, 2, 3] and data in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = rotation_map[self.rotation][data]
|
|
|
|
if blockid == 257:
|
|
# Uncolored shulker box
|
|
file_name = "shulker.png"
|
|
else:
|
|
file_name = "shulker_%s.png" % color_map[blockid - 219]
|
|
|
|
shulker_t = self.load_image("assets/minecraft/textures/entity/shulker/%s" % file_name).copy()
|
|
w, h = shulker_t.size
|
|
res = w // 4
|
|
# Cut out the parts of the shulker texture we need for the box
|
|
top = shulker_t.crop((res, 0, res * 2, res))
|
|
bottom = shulker_t.crop((res * 2, int(res * 1.75), res * 3, int(res * 2.75)))
|
|
side_top = shulker_t.crop((0, res, res, int(res * 1.75)))
|
|
side_bottom = shulker_t.crop((0, int(res * 2.75), res, int(res * 3.25)))
|
|
side = Image.new('RGBA', (res, res))
|
|
side.paste(side_top, (0, 0), side_top)
|
|
side.paste(side_bottom, (0, res // 2), side_bottom)
|
|
|
|
if data == 0: # down
|
|
side = side.rotate(180)
|
|
img = self.build_full_block(bottom, None, None, side, side)
|
|
elif data == 1: # up
|
|
img = self.build_full_block(top, None, None, side, side)
|
|
elif data == 2: # east
|
|
img = self.build_full_block(side, None, None, side.rotate(90), bottom)
|
|
elif data == 3: # west
|
|
img = self.build_full_block(side.rotate(180), None, None, side.rotate(270), top)
|
|
elif data == 4: # north
|
|
img = self.build_full_block(side.rotate(90), None, None, top, side.rotate(270))
|
|
elif data == 5: # south
|
|
img = self.build_full_block(side.rotate(270), None, None, bottom, side.rotate(90))
|
|
|
|
return img
|
|
|
|
|
|
# structure block
|
|
@material(blockid=255, data=list(range(4)), solid=True)
|
|
def structure_block(self, blockid, data):
|
|
if data == 0:
|
|
img = self.load_image_texture("assets/minecraft/textures/block/structure_block_save.png")
|
|
elif data == 1:
|
|
img = self.load_image_texture("assets/minecraft/textures/block/structure_block_load.png")
|
|
elif data == 2:
|
|
img = self.load_image_texture("assets/minecraft/textures/block/structure_block_corner.png")
|
|
elif data == 3:
|
|
img = self.load_image_texture("assets/minecraft/textures/block/structure_block_data.png")
|
|
return self.build_block(img, img)
|
|
|
|
|
|
# Jigsaw block
|
|
@material(blockid=256, data=list(range(6)), solid=True)
|
|
def jigsaw_block(self, blockid, data):
|
|
# Do rotation
|
|
if self.rotation in [1, 2, 3] and data in [2, 3, 4, 5]:
|
|
rotation_map = {1: {2: 5, 3: 4, 4: 2, 5: 3},
|
|
2: {2: 3, 3: 2, 4: 5, 5: 4},
|
|
3: {2: 4, 3: 5, 4: 3, 5: 2}}
|
|
data = rotation_map[self.rotation][data]
|
|
|
|
top = self.load_image_texture("assets/minecraft/textures/block/jigsaw_top.png")
|
|
bottom = self.load_image_texture("assets/minecraft/textures/block/jigsaw_bottom.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/jigsaw_side.png")
|
|
|
|
if data == 0: # Down
|
|
img = self.build_full_block(bottom.rotate(self.rotation * 90), None, None,
|
|
side.rotate(180), side.rotate(180))
|
|
elif data == 1: # Up
|
|
img = self.build_full_block(top.rotate(self.rotation * 90), None, None, side, side)
|
|
elif data == 2: # North
|
|
img = self.build_full_block(side, None, None, side.rotate(90), bottom.rotate(180))
|
|
elif data == 3: # South
|
|
img = self.build_full_block(side.rotate(180), None, None, side.rotate(270), top.rotate(270))
|
|
elif data == 4: # West
|
|
img = self.build_full_block(side.rotate(90), None, None, top.rotate(180), side.rotate(270))
|
|
elif data == 5: # East
|
|
img = self.build_full_block(side.rotate(270), None, None, bottom.rotate(180),
|
|
side.rotate(90))
|
|
|
|
return img
|
|
|
|
|
|
# beetroots(207), berry bushes (11505)
|
|
@material(blockid=[207, 11505], data=list(range(4)), transparent=True, nospawn=True)
|
|
def crops(self, blockid, data):
|
|
|
|
crops_id_to_tex = {
|
|
207: "assets/minecraft/textures/block/beetroots_stage%d.png",
|
|
11505: "assets/minecraft/textures/block/sweet_berry_bush_stage%d.png",
|
|
}
|
|
|
|
raw_crop = self.load_image_texture(crops_id_to_tex[blockid] % data)
|
|
crop1 = self.transform_image_top(raw_crop)
|
|
crop2 = self.transform_image_side(raw_crop)
|
|
crop3 = crop2.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
alpha_over(img, crop1, (0,12), crop1)
|
|
alpha_over(img, crop2, (6,3), crop2)
|
|
alpha_over(img, crop3, (6,3), crop3)
|
|
return img
|
|
|
|
# Concrete
|
|
@material(blockid=251, data=list(range(16)), solid=True)
|
|
def concrete(self, blockid, data):
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete.png" % color_map[data])
|
|
return self.build_block(texture, texture)
|
|
|
|
# Concrete Powder
|
|
@material(blockid=252, data=list(range(16)), solid=True)
|
|
def concrete(self, blockid, data):
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_concrete_powder.png" % color_map[data])
|
|
return self.build_block(texture, texture)
|
|
|
|
|
|
# Glazed Terracotta
|
|
@material(blockid=list(range(235, 251)), data=list(range(4)), solid=True)
|
|
def glazed_terracotta(self, blockid, data):
|
|
# Do rotation
|
|
data = (self.rotation + data) % 4
|
|
|
|
texture = self.load_image_texture("assets/minecraft/textures/block/%s_glazed_terracotta.png" %
|
|
color_map[blockid - 235]).copy()
|
|
texture_side4 = texture.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
if data == 0: # South
|
|
return self.build_full_block(texture, None, None, texture, texture_side4.rotate(270))
|
|
elif data == 1: # West
|
|
return self.build_full_block(texture.rotate(270), None, None, texture.rotate(90),
|
|
texture_side4.rotate(180))
|
|
elif data == 2: # North
|
|
return self.build_full_block(texture.rotate(180), None, None, texture.rotate(180),
|
|
texture_side4.rotate(90))
|
|
elif data == 3: # East
|
|
return self.build_full_block(texture.rotate(90), None, None, texture.rotate(270),
|
|
texture_side4)
|
|
|
|
|
|
# dried kelp block
|
|
@material(blockid=11331, data=[0], solid=True)
|
|
def sandstone(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/dried_kelp_top.png")
|
|
return self.build_block(top, self.load_image_texture("assets/minecraft/textures/block/dried_kelp_side.png"))
|
|
|
|
# scaffolding
|
|
block(blockid=11414, top_image="assets/minecraft/textures/block/scaffolding_top.png", side_image="assets/minecraft/textures/block/scaffolding_side.png", solid=False, transparent=True)
|
|
|
|
# beehive and bee_nest
|
|
@material(blockid=[11501, 11502], data=list(range(8)), solid=True)
|
|
def beehivenest(self, blockid, data):
|
|
if blockid == 11501: #beehive
|
|
t_top = self.load_image("assets/minecraft/textures/block/beehive_end.png")
|
|
t_side = self.load_image("assets/minecraft/textures/block/beehive_side.png")
|
|
t_front = self.load_image("assets/minecraft/textures/block/beehive_front.png")
|
|
t_front_honey = self.load_image("assets/minecraft/textures/block/beehive_front_honey.png")
|
|
elif blockid == 11502: #bee_nest
|
|
t_top = self.load_image("assets/minecraft/textures/block/bee_nest_top.png")
|
|
t_side = self.load_image("assets/minecraft/textures/block/bee_nest_side.png")
|
|
t_front = self.load_image("assets/minecraft/textures/block/bee_nest_front.png")
|
|
t_front_honey = self.load_image("assets/minecraft/textures/block/bee_nest_front_honey.png")
|
|
|
|
if data >= 4:
|
|
front = t_front_honey
|
|
else:
|
|
front = t_front
|
|
|
|
if self.rotation == 0: # rendering north upper-left
|
|
if data == 0 or data == 4: # south
|
|
return self.build_full_block(t_top, t_side, t_side, t_side, front)
|
|
elif data == 1 or data == 5: # west
|
|
return self.build_full_block(t_top, t_side, t_side, front, t_side)
|
|
elif data == 2 or data == 6: # north
|
|
return self.build_full_block(t_top, t_side, front, t_side, t_side)
|
|
elif data == 3 or data == 7: # east
|
|
return self.build_full_block(t_top, front, t_side, t_side, t_side)
|
|
|
|
elif self.rotation == 1: # north upper-right
|
|
if data == 0 or data == 4: # south
|
|
return self.build_full_block(t_top, t_side, t_side, front, t_side)
|
|
elif data == 1 or data == 5: # west
|
|
return self.build_full_block(t_top, t_side, front, t_side, t_side)
|
|
elif data == 2 or data == 6: # north
|
|
return self.build_full_block(t_top, front, t_side, t_side, t_side)
|
|
elif data == 3 or data == 7: # east
|
|
return self.build_full_block(t_top, t_side, t_side, t_side, front)
|
|
|
|
elif self.rotation == 2: # north lower-right
|
|
if data == 0 or data == 4: # south
|
|
return self.build_full_block(t_top, t_side, front, t_side, t_side)
|
|
elif data == 1 or data == 5: # west
|
|
return self.build_full_block(t_top, front, t_side, t_side, t_side)
|
|
elif data == 2 or data == 6: # north
|
|
return self.build_full_block(t_top, t_side, t_side, t_side, front)
|
|
elif data == 3 or data == 7: # east
|
|
return self.build_full_block(t_top, t_side, t_side, front, t_side)
|
|
|
|
elif self.rotation == 3: # north lower-left
|
|
if data == 0 or data == 4: # south
|
|
return self.build_full_block(t_top, front, t_side, t_side, t_side)
|
|
elif data == 1 or data == 5: # west
|
|
return self.build_full_block(t_top, t_side, t_side, t_side, front)
|
|
elif data == 2 or data == 6: # north
|
|
return self.build_full_block(t_top, t_side, t_side, front, t_side)
|
|
elif data == 3 or data == 7: # east
|
|
return self.build_full_block(t_top, t_side, front, t_side, t_side)
|
|
|
|
# honeycomb_block
|
|
block(blockid=11503, top_image="assets/minecraft/textures/block/honeycomb_block.png")
|
|
|
|
# honey_block
|
|
block(blockid=11504, top_image="assets/minecraft/textures/block/honey_block_top.png", side_image="assets/minecraft/textures/block/honey_block_side.png")
|
|
|
|
|
|
# Barrel
|
|
@material(blockid=11418, data=list(range(12)), solid=True)
|
|
def barrel(self, blockid, data):
|
|
t_bottom = self.load_image("assets/minecraft/textures/block/barrel_bottom.png")
|
|
t_side = self.load_image("assets/minecraft/textures/block/barrel_side.png")
|
|
|
|
if data & 0x01:
|
|
t_top = self.load_image("assets/minecraft/textures/block/barrel_top_open.png")
|
|
else:
|
|
t_top = self.load_image("assets/minecraft/textures/block/barrel_top.png")
|
|
data = data >> 1
|
|
|
|
if data == 0: # up
|
|
return self.build_full_block(t_top, None, None, t_side, t_side)
|
|
elif data == 1: # down
|
|
t_side = t_side.rotate(180)
|
|
return self.build_full_block(t_bottom, None, None, t_side, t_side)
|
|
elif data == 2: # south
|
|
return self.build_full_block(t_side.rotate(180), None, None, t_side.rotate(270), t_top)
|
|
elif data == 3: # east
|
|
return self.build_full_block(t_side.rotate(270), None, None, t_bottom, t_side.rotate(90))
|
|
elif data == 4: # north
|
|
return self.build_full_block(t_side, None, None, t_side.rotate(90), t_bottom)
|
|
else: # west
|
|
return self.build_full_block(t_side.rotate(90), None, None, t_top, t_side.rotate(270))
|
|
|
|
|
|
# Campfire (11506) and soul campfire (1003)
|
|
@material(blockid=[11506, 1003], data=list(range(8)), solid=True, transparent=True, nospawn=True)
|
|
def campfire(self, blockid, data):
|
|
# Do rotation, mask to not clobber lit data
|
|
data = data & 0b100 | ((self.rotation + (data & 0b11)) % 4)
|
|
block_name = "campfire" if blockid == 11506 else "soul_campfire"
|
|
|
|
# Load textures
|
|
# Fire & lit log textures contain multiple tiles, since both are
|
|
# 16px wide rely on load_image_texture() to crop appropriately
|
|
fire_raw_t = self.load_image_texture("assets/minecraft/textures/block/" + block_name
|
|
+ "_fire.png")
|
|
log_raw_t = self.load_image_texture("assets/minecraft/textures/block/campfire_log.png")
|
|
log_lit_raw_t = self.load_image_texture("assets/minecraft/textures/block/" + block_name
|
|
+ "_log_lit.png")
|
|
|
|
def create_tile(img_src, coord_crop, coord_paste, rot):
|
|
# Takes an image, crops a region, optionally rotates the
|
|
# texture, then finally pastes it onto a 16x16 image
|
|
img_out = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
img_in = img_src.crop(coord_crop)
|
|
if rot != 0:
|
|
img_in = img_in.rotate(rot, expand=True)
|
|
img_out.paste(img_in, coord_paste)
|
|
return img_out
|
|
|
|
# Generate bottom
|
|
bottom_t = log_lit_raw_t if data & 0b100 else log_raw_t
|
|
bottom_t = create_tile(bottom_t, (0, 8, 16, 14), (0, 5), 0)
|
|
bottom_t = self.transform_image_top(bottom_t)
|
|
|
|
# Generate two variants of a log: one with a lit side, one without
|
|
log_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
log_end_t = create_tile(log_raw_t, (0, 4, 4, 8), (12, 6), 0)
|
|
log_side_t = create_tile(log_raw_t, (0, 0, 16, 4), (0, 6), 0)
|
|
log_side_lit_t = create_tile(log_lit_raw_t, (0, 0, 16, 4), (0, 6), 0)
|
|
|
|
log_end_t = self.transform_image_side(log_end_t)
|
|
log_top_t = self.transform_image_top(log_side_t)
|
|
log_side_t = self.transform_image_side(log_side_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
log_side_lit_t = self.transform_image_side(log_side_lit_t).transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
alpha_over(log_t, log_top_t, (-2, 2), log_top_t) # Fix some holes at the edges
|
|
alpha_over(log_t, log_top_t, (-2, 1), log_top_t)
|
|
log_lit_t = log_t.copy()
|
|
|
|
# Unlit log
|
|
alpha_over(log_t, log_side_t, (5, 0), log_side_t)
|
|
alpha_over(log_t, log_end_t, (-7, 0), log_end_t)
|
|
|
|
# Lit log. For unlit fires, just reference the unlit log texture
|
|
if data & 0b100:
|
|
alpha_over(log_lit_t, log_side_lit_t, (5, 0), log_side_lit_t)
|
|
alpha_over(log_lit_t, log_end_t, (-7, 0), log_end_t)
|
|
else:
|
|
log_lit_t = log_t
|
|
|
|
# Log parts. Because fire needs to be in the middle of the logs,
|
|
# split the logs into two parts: Those appearing behind the fire
|
|
# and those appearing in front of the fire
|
|
logs_back_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
logs_front_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
|
|
# Back logs
|
|
alpha_over(logs_back_t, log_lit_t, (-1, 7), log_lit_t)
|
|
log_tmp_t = logs_back_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(logs_back_t, log_tmp_t, (1, -3), log_tmp_t)
|
|
|
|
# Front logs
|
|
alpha_over(logs_front_t, log_t, (7, 10), log_t)
|
|
# Due to the awkward drawing order, take a small part of the back
|
|
# logs that need to be drawn on top of the front logs despite
|
|
# the front logs being drawn last
|
|
ImageDraw.Draw(log_tmp_t).rectangle((0, 0, 18, 24), outline=(0, 0, 0, 0), fill=(0, 0, 0, 0))
|
|
alpha_over(logs_front_t, log_tmp_t, (1, -3), log_tmp_t)
|
|
log_tmp_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(log_tmp_t, log_lit_t, (7, 10), log_lit_t)
|
|
log_tmp_t = log_tmp_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(logs_front_t, log_tmp_t, (1, -3), log_tmp_t)
|
|
|
|
# Compose final image
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(img, bottom_t, (0, 12), bottom_t)
|
|
alpha_over(img, logs_back_t, (0, 0), logs_back_t)
|
|
if data & 0b100:
|
|
fire_t = fire_raw_t.copy()
|
|
if data & 0b11 in [0, 2]: # North, South
|
|
fire_t = fire_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, fire_t, (4, 4), fire_t)
|
|
alpha_over(img, logs_front_t, (0, 0), logs_front_t)
|
|
if data & 0b11 in [0, 2]: # North, South
|
|
img = img.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
return img
|
|
|
|
|
|
# Bell
|
|
@material(blockid=11507, data=list(range(16)), solid=True, transparent=True, nospawn=True)
|
|
def bell(self, blockid, data):
|
|
# Do rotation, mask to not clobber attachment data
|
|
data = data & 0b1100 | ((self.rotation + (data & 0b11)) % 4)
|
|
|
|
# Load textures
|
|
bell_raw_t = self.load_image("assets/minecraft/textures/entity/bell/bell_body.png")
|
|
bar_raw_t = self.load_image_texture("assets/minecraft/textures/block/dark_oak_planks.png")
|
|
post_raw_t = self.load_image_texture("assets/minecraft/textures/block/stone.png")
|
|
|
|
def create_tile(img_src, coord_crop, coord_paste, rot):
|
|
# Takes an image, crops a region, optionally rotates the
|
|
# texture, then finally pastes it onto a 16x16 image
|
|
img_out = Image.new("RGBA", (16, 16), self.bgcolor)
|
|
img_in = img_src.crop(coord_crop)
|
|
if rot != 0:
|
|
img_in = img_in.rotate(rot, expand=True)
|
|
img_out.paste(img_in, coord_paste)
|
|
return img_out
|
|
|
|
# 0 = floor, 1 = ceiling, 2 = single wall, 3 = double wall
|
|
bell_type = (data & 0b1100) >> 2
|
|
# Should the bar/post texture be flipped? Yes if either:
|
|
# - Attached to floor and East or West facing
|
|
# - Not attached to floor and North or South facing
|
|
flip_part = ((bell_type == 0 and data & 0b11 in [1, 3]) or
|
|
(bell_type != 0 and data & 0b11 in [0, 2]))
|
|
|
|
# Generate bell
|
|
# Bell side textures varies based on self.rotation
|
|
bell_sides_idx = [(0 - self.rotation) % 4, (3 - self.rotation) % 4]
|
|
# Upper sides
|
|
bell_coord = [x * 6 for x in bell_sides_idx]
|
|
bell_ul_t = create_tile(bell_raw_t, (bell_coord[0], 6, bell_coord[0] + 6, 13), (5, 4), 180)
|
|
bell_ur_t = create_tile(bell_raw_t, (bell_coord[1], 6, bell_coord[1] + 6, 13), (5, 4), 180)
|
|
bell_ul_t = self.transform_image_side(bell_ul_t)
|
|
bell_ur_t = self.transform_image_side(bell_ur_t.transpose(Image.FLIP_LEFT_RIGHT))
|
|
bell_ur_t = bell_ur_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Lower sides
|
|
bell_coord = [x * 8 for x in bell_sides_idx]
|
|
bell_ll_t = create_tile(bell_raw_t, (bell_coord[0], 21, bell_coord[0] + 8, 23), (4, 11), 180)
|
|
bell_lr_t = create_tile(bell_raw_t, (bell_coord[1], 21, bell_coord[1] + 8, 23), (4, 11), 180)
|
|
bell_ll_t = self.transform_image_side(bell_ll_t)
|
|
bell_lr_t = self.transform_image_side(bell_lr_t.transpose(Image.FLIP_LEFT_RIGHT))
|
|
bell_lr_t = bell_lr_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
# Upper top
|
|
top_rot = (180 + self.rotation * 90) % 360
|
|
bell_ut_t = create_tile(bell_raw_t, (6, 0, 12, 6), (5, 5), top_rot)
|
|
bell_ut_t = self.transform_image_top(bell_ut_t)
|
|
# Lower top
|
|
bell_lt_t = create_tile(bell_raw_t, (8, 13, 16, 21), (4, 4), top_rot)
|
|
bell_lt_t = self.transform_image_top(bell_lt_t)
|
|
|
|
bell_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(bell_t, bell_lt_t, (0, 8), bell_lt_t)
|
|
alpha_over(bell_t, bell_ll_t, (3, 4), bell_ll_t)
|
|
alpha_over(bell_t, bell_lr_t, (9, 4), bell_lr_t)
|
|
alpha_over(bell_t, bell_ut_t, (0, 3), bell_ut_t)
|
|
alpha_over(bell_t, bell_ul_t, (4, 4), bell_ul_t)
|
|
alpha_over(bell_t, bell_ur_t, (8, 4), bell_ur_t)
|
|
|
|
# Generate bar
|
|
if bell_type == 1: # Ceiling
|
|
# bar_coord: Left Right Top
|
|
bar_coord = [(4, 2, 6, 5), (6, 2, 8, 5), (1, 3, 3, 5)]
|
|
bar_tile_pos = [(7, 1), (7, 1), (7, 7)]
|
|
bar_over_pos = [(6, 3), (7, 2), (0, 0)]
|
|
else: # Floor, single wall, double wall
|
|
# Note: For a single wall bell, the position of the bar
|
|
# varies based on facing
|
|
if bell_type == 2 and data & 0b11 in [2, 3]: # Single wall, North/East facing
|
|
bar_x_sw = 3
|
|
bar_l_pos_sw = (6, 7)
|
|
else:
|
|
bar_x_sw = 0
|
|
bar_l_pos_sw = (4, 8)
|
|
bar_x = [2, None, bar_x_sw, 0][bell_type]
|
|
bar_len = [12, None, 13, 16][bell_type]
|
|
bar_l_pos = [(6, 7), None, bar_l_pos_sw, (4, 8)][bell_type]
|
|
bar_long_coord = (bar_x, 3, bar_x + bar_len, 5)
|
|
|
|
bar_coord = [(5, 4, 7, 6), bar_long_coord, bar_long_coord]
|
|
bar_tile_pos = [(2, 1), (bar_x, 1), (bar_x, 7)]
|
|
bar_over_pos = [bar_l_pos, (7, 3), (0, 1)]
|
|
|
|
bar_l_t = create_tile(bar_raw_t, bar_coord[0], bar_tile_pos[0], 0)
|
|
bar_r_t = create_tile(bar_raw_t, bar_coord[1], bar_tile_pos[1], 0)
|
|
bar_t_t = create_tile(bar_raw_t, bar_coord[2], bar_tile_pos[2], 0)
|
|
bar_l_t = self.transform_image_side(bar_l_t)
|
|
bar_r_t = self.transform_image_side(bar_r_t.transpose(Image.FLIP_LEFT_RIGHT))
|
|
bar_r_t = bar_r_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
bar_t_t = self.transform_image_top(bar_t_t)
|
|
|
|
bar_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(bar_t, bar_t_t, bar_over_pos[2], bar_t_t)
|
|
alpha_over(bar_t, bar_l_t, bar_over_pos[0], bar_l_t)
|
|
alpha_over(bar_t, bar_r_t, bar_over_pos[1], bar_r_t)
|
|
if flip_part:
|
|
bar_t = bar_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
# Generate post, only applies to floor attached bell
|
|
if bell_type == 0:
|
|
post_l_t = create_tile(post_raw_t, (0, 1, 4, 16), (6, 1), 0)
|
|
post_r_t = create_tile(post_raw_t, (0, 1, 2, 16), (14, 1), 0)
|
|
post_t_t = create_tile(post_raw_t, (0, 0, 2, 4), (14, 6), 0)
|
|
post_l_t = self.transform_image_side(post_l_t)
|
|
post_r_t = self.transform_image_side(post_r_t.transpose(Image.FLIP_LEFT_RIGHT))
|
|
post_r_t = post_r_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
post_t_t = self.transform_image_top(post_t_t)
|
|
|
|
post_back_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
post_front_t = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
alpha_over(post_back_t, post_t_t, (0, 1), post_t_t)
|
|
alpha_over(post_back_t, post_l_t, (10, 0), post_l_t)
|
|
alpha_over(post_back_t, post_r_t, (7, 3), post_r_t)
|
|
alpha_over(post_back_t, post_r_t, (6, 3), post_r_t) # Fix some holes
|
|
alpha_over(post_front_t, post_back_t, (-10, 5), post_back_t)
|
|
if flip_part:
|
|
post_back_t = post_back_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
post_front_t = post_front_t.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
if bell_type == 0:
|
|
alpha_over(img, post_back_t, (0, 0), post_back_t)
|
|
alpha_over(img, bell_t, (0, 0), bell_t)
|
|
alpha_over(img, bar_t, (0, 0), bar_t)
|
|
if bell_type == 0:
|
|
alpha_over(img, post_front_t, (0, 0), post_front_t)
|
|
|
|
return img
|
|
|
|
|
|
# Ancient Debris
|
|
block(blockid=[1000], top_image="assets/minecraft/textures/block/ancient_debris_top.png",
|
|
side_image="assets/minecraft/textures/block/ancient_debris_side.png")
|
|
|
|
|
|
# Basalt
|
|
@material(blockid=[1001, 1002], data=list(range(3)), solid=True)
|
|
def basalt(self, blockid, data):
|
|
block_name = "polished_basalt" if blockid == 1002 else "basalt"
|
|
top = self.load_image_texture("assets/minecraft/textures/block/" + block_name + "_top.png")
|
|
side = self.load_image_texture("assets/minecraft/textures/block/" + block_name + "_side.png")
|
|
return self.build_axis_block(top, side, data)
|
|
|
|
|
|
# Blackstone block
|
|
block(blockid=[1004], top_image="assets/minecraft/textures/block/blackstone_top.png",
|
|
side_image="assets/minecraft/textures/block/blackstone.png")
|
|
|
|
|
|
# Chain
|
|
@material(blockid=11419, data=list(range(3)), solid=True, transparent=True, nospawn=True)
|
|
def chain(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/chain.png")
|
|
sidetex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
mask = tex.crop((0, 0, 6, 16))
|
|
alpha_over(sidetex, mask, (5, 0), mask)
|
|
|
|
if data == 0: # y
|
|
return self.build_sprite(sidetex)
|
|
else:
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
sidetex = sidetex.rotate(90)
|
|
side = self.transform_image_side(sidetex)
|
|
otherside = self.transform_image_top(sidetex)
|
|
|
|
def draw_x():
|
|
_side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, _side, (6,3), _side)
|
|
alpha_over(img, otherside, (3,3), otherside)
|
|
|
|
def draw_z():
|
|
_otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, side, (6,3), side)
|
|
alpha_over(img, _otherside, (0,6), _otherside)
|
|
|
|
draw_funcs = [draw_x, draw_z]
|
|
|
|
if data == 1: # x
|
|
draw_funcs[self.rotation % len(draw_funcs)]()
|
|
|
|
elif data == 2: # z
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
|
|
return img
|
|
|
|
|
|
# Respawn anchor
|
|
@material(blockid=1037, data=list(range(5)), solid=True)
|
|
def respawn_anchor(self, blockid, data):
|
|
top = self.load_image_texture("assets/minecraft/textures/block/respawn_anchor_top_off.png" if data ==
|
|
0 else "assets/minecraft/textures/block/respawn_anchor_top.png")
|
|
side = self.load_image_texture(
|
|
"assets/minecraft/textures/block/respawn_anchor_side%s.png" % (data))
|
|
|
|
return self.build_block(top, side)
|
|
|
|
|
|
# Netherite
|
|
block(blockid=[1005], top_image="assets/minecraft/textures/block/netherite_block.png")
|
|
# soul soil
|
|
block(blockid=1020, top_image="assets/minecraft/textures/block/soul_soil.png")
|
|
# nether gold ore
|
|
block(blockid=1021, top_image="assets/minecraft/textures/block/nether_gold_ore.png")
|
|
# Solid Nether stone blocks
|
|
block(blockid=1022, top_image="assets/minecraft/textures/block/polished_blackstone.png")
|
|
block(blockid=1023, top_image="assets/minecraft/textures/block/chiseled_polished_blackstone.png")
|
|
block(blockid=1024, top_image="assets/minecraft/textures/block/gilded_blackstone.png")
|
|
block(blockid=1025, top_image="assets/minecraft/textures/block/cracked_polished_blackstone_bricks.png")
|
|
block(blockid=1026, top_image="assets/minecraft/textures/block/polished_blackstone_bricks.png")
|
|
|
|
block(blockid=1035, top_image="assets/minecraft/textures/block/crying_obsidian.png")
|
|
block(blockid=1036, top_image="assets/minecraft/textures/block/lodestone_top.png", side_image="assets/minecraft/textures/block/lodestone_side.png")
|
|
block(blockid=1041, top_image="assets/minecraft/textures/block/quartz_bricks.png")
|
|
|
|
block(blockid=1042, top_image="assets/minecraft/textures/block/amethyst_block.png")
|
|
block(blockid=1043, top_image="assets/minecraft/textures/block/raw_iron_block.png")
|
|
block(blockid=1044, top_image="assets/minecraft/textures/block/raw_gold_block.png")
|
|
block(blockid=1045, top_image="assets/minecraft/textures/block/budding_amethyst.png")
|
|
|
|
# You have entered the COPPER ZONE
|
|
block(blockid=[1046, 1050], top_image="assets/minecraft/textures/block/copper_block.png")
|
|
block(blockid=[1047, 1051], top_image="assets/minecraft/textures/block/exposed_copper.png")
|
|
block(blockid=[1048, 1052], top_image="assets/minecraft/textures/block/weathered_copper.png")
|
|
block(blockid=[1049, 1053], top_image="assets/minecraft/textures/block/oxidized_copper.png")
|
|
# Cut variant
|
|
block(blockid=[1054, 1058], top_image="assets/minecraft/textures/block/cut_copper.png")
|
|
block(blockid=[1055, 1059], top_image="assets/minecraft/textures/block/exposed_cut_copper.png")
|
|
block(blockid=[1056, 1060], top_image="assets/minecraft/textures/block/weathered_cut_copper.png")
|
|
block(blockid=[1057, 1061], top_image="assets/minecraft/textures/block/oxidized_cut_copper.png")
|
|
|
|
block(blockid=1062, top_image="assets/minecraft/textures/block/raw_copper_block.png")
|
|
block(blockid=1063, top_image="assets/minecraft/textures/block/copper_ore.png")
|
|
# You are now leaving the COPPER ZONE
|
|
|
|
block(blockid=1080, top_image="assets/minecraft/textures/block/moss_block.png")
|
|
block(blockid=1081, top_image="assets/minecraft/textures/block/calcite.png")
|
|
block(blockid=1082, top_image="assets/minecraft/textures/block/rooted_dirt.png")
|
|
|
|
|
|
# deepslate
|
|
@material(blockid=1083, data=list(range(3)), solid=True)
|
|
def deepslate(self, blockid, data):
|
|
top=self.load_image_texture("assets/minecraft/textures/block/deepslate_top.png")
|
|
side=self.load_image_texture("assets/minecraft/textures/block/deepslate.png")
|
|
return self.build_axis_block(top, side, data)
|
|
|
|
|
|
block(blockid=1084, top_image="assets/minecraft/textures/block/cobbled_deepslate.png")
|
|
block(blockid=1085, top_image="assets/minecraft/textures/block/polished_deepslate.png")
|
|
block(blockid=1086, top_image="assets/minecraft/textures/block/deepslate_coal_ore.png")
|
|
block(blockid=1087, top_image="assets/minecraft/textures/block/deepslate_iron_ore.png")
|
|
block(blockid=1088, top_image="assets/minecraft/textures/block/deepslate_copper_ore.png")
|
|
block(blockid=1089, top_image="assets/minecraft/textures/block/deepslate_gold_ore.png")
|
|
block(blockid=1090, top_image="assets/minecraft/textures/block/deepslate_emerald_ore.png")
|
|
block(blockid=1091, top_image="assets/minecraft/textures/block/deepslate_lapis_ore.png")
|
|
block(blockid=1092, top_image="assets/minecraft/textures/block/deepslate_diamond_ore.png")
|
|
block(blockid=1093, top_image="assets/minecraft/textures/block/deepslate_redstone_ore.png")
|
|
block(blockid=1094, top_image="assets/minecraft/textures/block/deepslate_bricks.png")
|
|
block(blockid=1095, top_image="assets/minecraft/textures/block/cracked_deepslate_bricks.png")
|
|
block(blockid=1096, top_image="assets/minecraft/textures/block/deepslate_tiles.png")
|
|
block(blockid=1097, top_image="assets/minecraft/textures/block/cracked_deepslate_tiles.png")
|
|
block(blockid=1098, top_image="assets/minecraft/textures/block/chiseled_deepslate.png")
|
|
|
|
block(blockid=1107, top_image="assets/minecraft/textures/block/dripstone_block.png")
|
|
block(blockid=1108, top_image="assets/minecraft/textures/block/smooth_basalt.png")
|
|
block(blockid=1109, top_image="assets/minecraft/textures/block/tuff.png")
|
|
|
|
|
|
@material(blockid=1110, data=list(range(16)), transparent=True)
|
|
def pointed_dripstone(self, blockid, data):
|
|
up_down = "down" if data & 0b1000 else "up"
|
|
if (data & 4) == 4: # base
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_base.png" % (up_down))
|
|
elif (data & 3) == 3: # frustum
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_frustum.png" % (up_down))
|
|
elif (data & 2) == 2: # middle
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_middle.png" % (up_down))
|
|
elif (data & 1) == 1: # tip_merge
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_tip_merge.png" % (up_down))
|
|
else: # 0 - tip
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/pointed_dripstone_%s_tip.png" % (up_down))
|
|
return self.build_sprite(tex)
|
|
|
|
|
|
block(blockid=1111, top_image="assets/minecraft/textures/block/powder_snow.png")
|
|
|
|
|
|
@material(blockid=1112, data=0, transparent=True)
|
|
def hangings_roots(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/hanging_roots.png")
|
|
return self.build_sprite(tex)
|
|
|
|
|
|
@material(blockid=[1113, 1114, 1115], data=list(range(6)), transparent=True)
|
|
def amethyst_bud(self, blockid, data):
|
|
if blockid == 1113:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/small_amethyst_bud.png")
|
|
elif blockid == 1114:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/medium_amethyst_bud.png")
|
|
elif blockid == 1115:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/large_amethyst_bud.png")
|
|
|
|
def draw_north():
|
|
rotated = tex.rotate(90)
|
|
side = self.transform_image_side(rotated)
|
|
otherside = self.transform_image_top(rotated)
|
|
otherside = otherside.transpose(Image.FLIP_TOP_BOTTOM)
|
|
alpha_over(img, side, (6, 3), side)
|
|
alpha_over(img, otherside, (0, 6), otherside)
|
|
|
|
def draw_south():
|
|
rotated = tex.rotate(-90)
|
|
side = self.transform_image_side(rotated)
|
|
otherside = self.transform_image_top(rotated)
|
|
otherside = otherside.transpose(Image.FLIP_TOP_BOTTOM)
|
|
alpha_over(img, side, (6, 3), side)
|
|
alpha_over(img, otherside, (0, 6), otherside)
|
|
|
|
def draw_west():
|
|
rotated = tex.rotate(-90)
|
|
side = self.transform_image_side(rotated)
|
|
side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
otherside = self.transform_image_top(rotated)
|
|
otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
otherside = otherside.transpose(Image.FLIP_TOP_BOTTOM)
|
|
alpha_over(img, side, (6, 3), side)
|
|
alpha_over(img, otherside, (0, 6), otherside)
|
|
|
|
def draw_east():
|
|
rotated = tex.rotate(90)
|
|
side = self.transform_image_side(rotated)
|
|
side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
otherside = self.transform_image_top(rotated)
|
|
otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
otherside = otherside.transpose(Image.FLIP_TOP_BOTTOM)
|
|
alpha_over(img, side, (6, 3), side)
|
|
alpha_over(img, otherside, (0, 6), otherside)
|
|
|
|
draw_funcs = [draw_east, draw_south, draw_west, draw_north]
|
|
|
|
if data == 0: # down
|
|
tex = tex.transpose(Image.FLIP_TOP_BOTTOM)
|
|
return self.build_sprite(tex)
|
|
elif data == 1: # up
|
|
return self.build_sprite(tex)
|
|
elif data == 5: # north
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
draw_funcs[(self.rotation + 3) % len(draw_funcs)]()
|
|
return img
|
|
elif data == 3: # south
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
return img
|
|
elif data == 4: # west
|
|
img = Image.new("RGBA", (24,24), self.bgcolor)
|
|
draw_funcs[(self.rotation + 2) % len(draw_funcs)]()
|
|
return img
|
|
elif data == 2: # east
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
draw_funcs[(self.rotation + 0) % len(draw_funcs)]()
|
|
return img
|
|
|
|
return self.build_sprite(tex)
|
|
|
|
|
|
@material(blockid=[1116, 1117], data=list(range(2)), transparent=True)
|
|
def cave_vines(self, blockid, data):
|
|
if blockid == 1116:
|
|
if data == 1:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/cave_vines_plant_lit.png")
|
|
else:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/cave_vines_plant.png")
|
|
elif blockid == 1117:
|
|
if data == 1:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/cave_vines_lit.png")
|
|
else:
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/cave_vines.png")
|
|
return self.build_sprite(tex)
|
|
|
|
|
|
@material(blockid=1118, data=list(range(6)), transparent=True, solid=True)
|
|
def lightning_rod(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/lightning_rod.png")
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
|
|
mask = tex.crop((0, 4, 2, 16))
|
|
sidetex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(sidetex, mask, (14, 4), mask)
|
|
|
|
mask = tex.crop((0, 0, 4, 4))
|
|
toptex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(toptex, mask, (12, 0), mask)
|
|
|
|
mask = tex.crop((0, 4, 2, 6))
|
|
side_toptex = Image.new(tex.mode, tex.size, self.bgcolor)
|
|
alpha_over(side_toptex, mask, (12, 0), mask)
|
|
|
|
def draw_east():
|
|
toptex_rotated = toptex.rotate(90)
|
|
top_side = self.transform_image_side(toptex_rotated)
|
|
top_side = top_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
top_otherside = self.transform_image_top(toptex)
|
|
top_otherside = top_otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
top_top = self.transform_image_side(toptex)
|
|
|
|
# top
|
|
alpha_over(img, top_otherside, (6, 6), top_otherside)
|
|
# side
|
|
alpha_over(img, top_side, (8, 7), top_side)
|
|
alpha_over(img, top_top, (6, 2), top_top)
|
|
|
|
roated_side = sidetex.rotate(90)
|
|
side = self.transform_image_side(roated_side)
|
|
side = side.transpose(Image.FLIP_TOP_BOTTOM)
|
|
otherside = self.transform_image_top(sidetex)
|
|
otherside = otherside.transpose(Image.FLIP_TOP_BOTTOM)
|
|
side_top = self.transform_image_side(side_toptex)
|
|
|
|
alpha_over(img, otherside, (-7, 4), otherside)
|
|
alpha_over(img, side, (5, -1), side)
|
|
alpha_over(img, side_top, (-2, 9), side_top)
|
|
|
|
def draw_south():
|
|
roated_side = sidetex.rotate(90)
|
|
side = self.transform_image_side(roated_side)
|
|
otherside = self.transform_image_top(sidetex)
|
|
|
|
alpha_over(img, side, (3, 6), side)
|
|
alpha_over(img, otherside, (-8, 6), otherside)
|
|
|
|
toptex_rotated = toptex.rotate(90)
|
|
top_side = self.transform_image_side(toptex_rotated)
|
|
top_otherside = self.transform_image_top(toptex)
|
|
top_top = self.transform_image_side(toptex)
|
|
top_top = top_top.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
alpha_over(img, top_side, (15, 12), top_side)
|
|
alpha_over(img, top_otherside, (5, 10), top_otherside)
|
|
alpha_over(img, top_top, (17, 7), top_top)
|
|
|
|
def draw_west():
|
|
roated_side = sidetex.rotate(90)
|
|
side = self.transform_image_side(roated_side)
|
|
side = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
otherside = self.transform_image_top(sidetex)
|
|
otherside = otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
|
|
alpha_over(img, side, (10, 6), side)
|
|
alpha_over(img, otherside, (8, 6), otherside)
|
|
|
|
toptex_rotated = toptex.rotate(90)
|
|
top_side = self.transform_image_side(toptex_rotated)
|
|
top_side = top_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
top_otherside = self.transform_image_top(toptex)
|
|
top_otherside = top_otherside.transpose(Image.FLIP_LEFT_RIGHT)
|
|
top_top = self.transform_image_side(toptex)
|
|
|
|
# top
|
|
alpha_over(img, top_otherside, (-3, 10), top_otherside)
|
|
# side
|
|
alpha_over(img, top_side, (0, 11), top_side)
|
|
alpha_over(img, top_top, (-3, 7), top_top)
|
|
|
|
def draw_north():
|
|
roated_side = sidetex.rotate(90)
|
|
side = self.transform_image_side(roated_side)
|
|
otherside = self.transform_image_top(sidetex)
|
|
|
|
alpha_over(img, side, (4, 7), side)
|
|
alpha_over(img, otherside, (-6, 7), otherside)
|
|
|
|
toptex_rotated = toptex.rotate(90)
|
|
top_side = self.transform_image_side(toptex_rotated)
|
|
top_otherside = self.transform_image_top(toptex)
|
|
top_top = self.transform_image_side(toptex)
|
|
top_top = top_top.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, top_otherside, (-4, 6), top_otherside)
|
|
alpha_over(img, top_side, (5, 7), top_side)
|
|
alpha_over(img, top_top, (8, 3), top_top)
|
|
|
|
draw_funcs = [draw_east, draw_south, draw_west, draw_north]
|
|
|
|
if data == 1: # up
|
|
side = self.transform_image_side(sidetex)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, side, (0, 6 - 4), side)
|
|
alpha_over(img, otherside, (12, 6 - 4), otherside)
|
|
|
|
top_top = self.transform_image_top(toptex)
|
|
top_side = self.transform_image_side(toptex)
|
|
top_otherside = top_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, top_side, (0, 6 - 4), top_side)
|
|
alpha_over(img, top_otherside, (12, 6 - 4), top_otherside)
|
|
alpha_over(img, top_top, (0, 5), top_top)
|
|
elif data == 0: # down
|
|
toptex_flipped = toptex.transpose(Image.FLIP_TOP_BOTTOM)
|
|
top_top = self.transform_image_top(toptex)
|
|
top_side = self.transform_image_side(toptex_flipped)
|
|
top_otherside = top_side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, top_side, (0, 6 - 4), top_side)
|
|
alpha_over(img, top_otherside, (12, 6 - 4), top_otherside)
|
|
alpha_over(img, top_top, (0, 14), top_top)
|
|
|
|
flipped = sidetex.transpose(Image.FLIP_TOP_BOTTOM)
|
|
side_top = self.transform_image_top(side_toptex)
|
|
side = self.transform_image_side(flipped)
|
|
otherside = side.transpose(Image.FLIP_LEFT_RIGHT)
|
|
alpha_over(img, side, (0, 6 - 4), side)
|
|
alpha_over(img, otherside, (12, 6 - 4), otherside)
|
|
alpha_over(img, side_top, (2, 6), side_top)
|
|
elif data == 3: # south
|
|
draw_funcs[(self.rotation + 1) % len(draw_funcs)]()
|
|
elif data == 4: # west
|
|
draw_funcs[(self.rotation + 2) % len(draw_funcs)]()
|
|
elif data == 2: # east
|
|
draw_funcs[(self.rotation + 0) % len(draw_funcs)]()
|
|
elif data == 5: # north
|
|
draw_funcs[(self.rotation + 3) % len(draw_funcs)]()
|
|
|
|
return img
|
|
|
|
|
|
@material(blockid=1119, data=list(range(1 << 6)), transparent=True)
|
|
def glow_lichen(self, blockid, data):
|
|
tex = self.load_image_texture("assets/minecraft/textures/block/glow_lichen.png")
|
|
|
|
bottom = tex if data & 1 << 0 else None
|
|
top = tex if data & 1 << 1 else None
|
|
east = tex if data & 1 << 2 else None
|
|
south = tex if data & 1 << 3 else None
|
|
west = tex if data & 1 << 4 else None
|
|
north = tex if data & 1 << 5 else None
|
|
|
|
if self.rotation == 0:
|
|
return self.build_full_block(top, north, east, west, south, bottom)
|
|
elif self.rotation == 1:
|
|
return self.build_full_block(top, west, north, south, east, bottom)
|
|
elif self.rotation == 2:
|
|
return self.build_full_block(top, south, west, east, north, bottom)
|
|
else: # self.rotation == 3:
|
|
return self.build_full_block(top, east, south, north, west, bottom)
|
|
|
|
|
|
@material(blockid=1120, data=list(range(1)), transparent=True)
|
|
def spore_blossom(self, blockid, data):
|
|
leaf = self.load_image_texture("assets/minecraft/textures/block/spore_blossom.png")
|
|
base = self.load_image_texture("assets/minecraft/textures/block/spore_blossom_base.png")
|
|
img = Image.new("RGBA", (24, 24), self.bgcolor)
|
|
|
|
side_leaf = self.transform_image_top(leaf)
|
|
alpha_over(img, side_leaf, (-6, -5), side_leaf)
|
|
|
|
roated_leaf = leaf.rotate(90)
|
|
side_leaf = self.transform_image_top(roated_leaf)
|
|
alpha_over(img, side_leaf, (-7, 4), side_leaf)
|
|
|
|
roated_leaf = roated_leaf.rotate(90)
|
|
side_leaf = self.transform_image_top(roated_leaf)
|
|
alpha_over(img, side_leaf, (5, 4), side_leaf)
|
|
|
|
roated_leaf = roated_leaf.rotate(90)
|
|
side_leaf = self.transform_image_top(roated_leaf)
|
|
alpha_over(img, side_leaf, (5, -5), side_leaf)
|
|
|
|
base_top = self.transform_image_top(base)
|
|
alpha_over(img, base_top, (0, 0), base_top)
|
|
return img
|
|
|
|
|
|
block(blockid=1121, top_image="assets/minecraft/textures/block/mud.png")
|
|
block(blockid=1122, top_image="assets/minecraft/textures/block/packed_mud.png")
|
|
block(blockid=1123, top_image="assets/minecraft/textures/block/mud_bricks.png")
|
|
block(blockid=1125, top_image="assets/minecraft/textures/block/mangrove_roots_top.png",
|
|
side_image="assets/minecraft/textures/block/mangrove_roots_side.png")
|
|
block(blockid=1127, top_image="assets/minecraft/textures/block/muddy_mangrove_roots_top.png",
|
|
side_image="assets/minecraft/textures/block/muddy_mangrove_roots_side.png")
|