def chop_terrain(img, cross_img=None): parts = img.chop_grid(TERRAIN_PARTS) parts['empty'] = BLANK_TILE if cross_img is not None: parts.update(cross_img.chop_grid(( ('cross/nw',), ('cross/ne',), ))) else: parts['cross/nw'] = image2.stack((parts['inner/nw'], parts['inner/se'])) parts['cross/ne'] = image2.stack((parts['inner/ne'], parts['inner/sw'])) return parts
def get_top(side): return image2.stack(( black[ds[0]], wall_top[ds[1]], wall_top[ds[2]], ramp['top/%s' % side], ))
def get_front(z, side): front_desc = calc_front_desc(ds) ramp_front = ramp['front/%s/z%d' % (side, z)] if front_desc is not None: return image2.stack((wall_front['%s/z%d' % (front_desc, z)], ramp_front)) else: return ramp_front
def do_cave_entrance(tiles, cave_bottom_dct): wall_top = chop_cave_top(tiles('lpc-cave-walls2.png')) wall_front = chop_cave_front(tiles('lpc-cave-walls2.png')) door_front = chop_cave_entrance(tiles('lpc-cave-walls2.png')) black = chop_black() cave_bottom = cave_bottom_dct['cccc'] bb = BLOCK.child().shape('solid') for code in iter_codes(3): nw, ne, se, sw = code ds = dissect(code, 3) name = ''.join(str(x) for x in code) terrain = ''.join(('g' if x == 1 else 'c') for x in code) def get_front(z, side): front_desc = calc_front_desc(ds) door_tile = door_front['%s/z%d' % (side, z)] if front_desc is not None: base = wall_front['%s/z%d' % (front_desc, z)] if side == 'left': base = base.modify(blank_right) elif side == 'right': base = base.modify(blank_left) return image2.stack((base, door_tile)) else: return door_tile top = image2.stack(( black[ds[0]], wall_top[ds[1]], wall_top[ds[2]], )) if ne == 2 and se == 1: bb.new('cave_entrance/x0/z0/%s/c%s' % (terrain, name)) \ .front(get_front(0, 'left')) \ .bottom(cave_bottom_dct[terrain]) bb.new('cave_entrance/x0/z1/c%s' % name) \ .front(get_front(1, 'left')) \ .top(top) if code == (2, 2, 1, 1): bb.new('cave_entrance/x1/z0/%s/c%s' % (terrain, name)) \ .shape('floor') \ .front(door_front['center/z0']) \ .bottom(cave_bottom_dct[terrain]) bb.new('cave_entrance/x1/z1/c%s' % name) \ .shape('empty') \ .front(door_front['center/z1']) \ .top(top) if nw == 2 and sw == 1: bb.new('cave_entrance/x2/z0/%s/c%s' % (terrain, name)) \ .front(get_front(0, 'right')) \ .bottom(cave_bottom_dct[terrain]) bb.new('cave_entrance/x2/z1/c%s' % name) \ .front(get_front(1, 'right')) \ .top(top)
def get_front(z, side): front_desc = calc_front_desc(ds) door_tile = door_front['%s/z%d' % (side, z)] if front_desc is not None: base = wall_front['%s/z%d' % (front_desc, z)] if side == 'left': base = base.modify(blank_right) elif side == 'right': base = base.modify(blank_left) return image2.stack((base, door_tile)) else: return door_tile
def iter_terrain_floor(layers): letters = [l.name for l in layers] for code in iter_codes(len(layers)): name = ''.join(layers[i].name for i in code) parts = [] for i in range(len(layers)): l = layers[i] if l.is_base: key = describe(tuple(x >= i for x in code)) else: key = describe(tuple(x == i for x in code)) parts.append(l.dct[key]) img = image2.stack(parts) yield FloorInfo(name, img)
def get_image(self): img_size = (self.size[0], self.size[1] + self.size[2]) px_size = tuple(x * TILE_SIZE for x in img_size) # part.base is relative to the 0,0,0 corner, so part.base[1] may be as # low as -self.size[2] * TILE_SIZE. y_off = self.size[2] * TILE_SIZE layers = [] for p in self.parts: bx, by = p.base layers.append(p.img.pad(px_size, offset=(bx, by + y_off))) if len(layers) > 0: result = image2.stack(layers) else: result = image2.Image(size=px_size) return result.raw().raw()
def interior_blocks(basename, img, shape='empty'): parts = img.chop(INTERIOR_PARTS, unit=TILE_SIZE // 2) def variations(k, vs): f = lambda x: (('0', 'inner'), ('1', 'full')) if x == '*' else (('', x),) for ak, av in f(vs[0]): for bk, bv in f(vs[1]): for ck, cv in f(vs[2]): for dk, dv in f(vs[3]): extra_k = ak + bk + ck + dk local_k = k + '/' + extra_k if extra_k else k yield (local_k, (av, bv, cv, dv)) # Build up the map that describes how to build each tile from parts. PART_MAP_FULL = {} def add(base, parts_str): for k, v in variations(base, parts_str.split()): PART_MAP_FULL[k] = v # Naming convention: <edges>/<corners> # # <edges> is [n][s][w][e] depending on which edges are filled. # # <corners> is [01] for each corner with both connected edges filled # (order: nw, sw, se, ne). The digit is 0 if the subtile in that position # is 'inner' (for situations where there is not a tile of the same group in # that direction), and 1 if the subtile is 'full' (there is a tile). # # If <corners> is empty, then the slash is omitted. # 'NW SW SE NE ' add('spot', 'outer outer outer outer') add('n', 'vert outer outer vert ') add('w', 'horiz horiz outer outer') add('s', 'outer vert vert outer') add('e', 'outer outer horiz horiz') add('nw', '* horiz outer vert ') add('sw', 'horiz * vert outer') add('se', 'outer vert * horiz') add('ne', 'vert outer horiz * ') add('ns', 'vert vert vert vert ') add('we', 'horiz horiz horiz horiz') add('nsw', '* * vert vert ') add('swe', 'horiz * * horiz') add('nse', 'vert vert * * ') add('nwe', '* horiz horiz * ') add('nswe', '* * * * ') full_parts = {} HALF = TILE_SIZE // 2 for k, (nw, sw, se, ne) in PART_MAP_FULL.items(): full_parts[k] = image2.stack(( parts['nw/' + nw].pad(TILE_SIZE, offset=(0, 0)), parts['sw/' + sw].pad(TILE_SIZE, offset=(0, HALF)), parts['se/' + se].pad(TILE_SIZE, offset=(HALF, HALF)), parts['ne/' + ne].pad(TILE_SIZE, offset=(HALF, 0)), )).with_unit(TILE_SIZE) b = BLOCK.prefixed(basename) \ .shape(shape) b.new(full_parts.keys()) \ .bottom(full_parts) return b
def init(): tiles = loader('tiles', unit=TILE_SIZE) def mk_layer(letter, img_name, is_base=True): return Layer(letter, chop_terrain(tiles(img_name)), is_base) layer_dct = { 'g': mk_layer('g', 'lpc-grassalt-with-variants.png'), 'm': mk_layer('m', 'lpc-base-tiles/dirt.png'), 'c': mk_layer('c', 'lpc-base-tiles/dirt2.png'), #'s': mk_layer('s', 'TODO'), 'a': mk_layer('a', 'lpc-base-tiles/lavarock.png'), 'w': mk_layer('w', 'lpc-base-tiles/water.png', is_base=False), 'l': mk_layer('l', 'lpc-base-tiles/lava.png', is_base=False), 'p': mk_layer('p', 'lpc-base-tiles/holemid.png', is_base=False), } order = 'mca wlp g s' def collect_layers(letters): return [layer_dct[l] for l in order if l in letters] # Base terrain floor_bb = BLOCK.prefixed('terrain').shape('floor') empty_bb = BLOCK.prefixed('terrain').shape('empty') seen = set() for letters in ('gc', 'gw'): for name, img in iter_terrain_floor(collect_layers(letters)): # Avoid duplicate blocks if name in seen: continue seen.add(name) bb = floor_bb if 'w' not in letters else empty_bb bb.new(name).bottom(img) # Variants for layer in layer_dct.values(): name = layer.name * 4 for i in range(4): key = 'full/v%d' % i if i > 0 else 'full' floor_bb.new('%s/v%d' % (name, i)).bottom(layer.dct[key]) # Hilltop edges grass_top = chop_terrain(tiles('cave-top-grass.png'), tiles('cave-top-grass-cross.png')) for code in iter_codes(2): d = describe(tuple(x == 0 for x in code)) blk = floor_bb.new('gggg/e%s' % ''.join(str(x) for x in code)).bottom(grass_top[d]) if code == (1, 1, 1, 1): blk.shape('empty') # Cave top (for ramp tops inside caves) cave_top = chop_terrain(tiles('lpc-cave-top2.png'), tiles('lpc-cave-top2-cross.png')) for code in iter_codes(2): d = describe(tuple(x == 0 for x in code)) blk = floor_bb.new('cccc/e%s' % ''.join(str(x) for x in code)).bottom(cave_top[d]) if code == (1, 1, 1, 1): blk.shape('empty') # Terrain with cave walls for floor in iter_terrain_floor(collect_layers('gc')): for cave in iter_cave_z0(tiles('lpc-cave-walls2.png')): BLOCK.new('terrain/%s/c%s' % (floor.name, cave.name)) \ .bottom(floor.bottom) \ .front(cave.front) \ .shape('floor' if cave.clear else 'solid') # Cave walls (z1 part) cave_top_dct = chop_cave_top(tiles('lpc-cave-walls2.png')) cave_front_dct = chop_cave_front(tiles('lpc-cave-walls2.png')) black_dct = chop_black() for code in iter_codes(3): ds = dissect(code, 3) name = ''.join(str(x) for x in code) front_desc = calc_front_desc(ds) front = cave_front_dct['%s/z1' % front_desc] if front_desc is not None else None top = image2.stack(( black_dct[ds[0]], cave_top_dct[ds[1]], cave_top_dct[ds[2]], )) clear = name in ('1111', '2222') BLOCK.new('cave_z1/c%s' % name) \ .shape('empty' if clear else 'solid') \ .top(top).front(front) # Ramps cave_bottom_dct = dict(iter_terrain_floor(collect_layers('gc'))) do_cave_ramps(tiles, cave_bottom_dct) do_cave_entrance(tiles, cave_bottom_dct)
def do_cave_ramps(tiles, cave_bottom_dct): ramp = tiles('cave-stair.png').chop_grid(CAVE_RAMP_PARTS) wall_top = chop_cave_top(tiles('lpc-cave-walls2.png')) wall_front = chop_cave_front(tiles('lpc-cave-walls2.png')) black = chop_black() cave_bottom = cave_bottom_dct['cccc'] bb = BLOCK.child().shape('solid') for code in iter_codes(3): nw, ne, se, sw = code ds = dissect(code, 3) name = ''.join(str(x) for x in code) terrain = ''.join(('g' if x == 1 else 'c') for x in code) def get_front(z, side): front_desc = calc_front_desc(ds) ramp_front = ramp['front/%s/z%d' % (side, z)] if front_desc is not None: return image2.stack((wall_front['%s/z%d' % (front_desc, z)], ramp_front)) else: return ramp_front def get_top(side): return image2.stack(( black[ds[0]], wall_top[ds[1]], wall_top[ds[2]], ramp['top/%s' % side], )) if ne == 0 and se != 0: # Front left if terrain != 'cccc': bb.new('ramp/xy01/z0/cccc/c%s' % name).front(get_front(0, 'left')) \ .bottom(cave_bottom) bb.new('ramp/xy01/z0/%s/c%s' % (terrain, name)).front(get_front(0, 'left')) \ .bottom(cave_bottom_dct[terrain]) bb.new('ramp/xy01/z1/c%s' % name).front(get_front(1, 'left')) \ .top(get_top('front/left/%s' % ('corner' if sw != 0 else 'edge'))) if nw == 0 and sw != 0: # Front right if terrain != 'cccc': bb.new('ramp/xy21/z0/cccc/c%s' % name).front(get_front(0, 'right')) \ .bottom(cave_bottom) bb.new('ramp/xy21/z0/%s/c%s' % (terrain, name)).front(get_front(0, 'right')) \ .bottom(cave_bottom_dct[terrain]) bb.new('ramp/xy21/z1/c%s' % name).front(get_front(1, 'right')) \ .top(get_top('front/right/%s' % ('corner' if se != 0 else 'edge'))) def normal_front(z): front_desc = calc_front_desc(ds) if front_desc is not None: return wall_front['%s/z%d' % (front_desc, z)] else: return None if se == 0: # Back right if terrain != 'cccc': bb.new('ramp/xy00/z0/cccc/c%s' % name).front(normal_front(0)) \ .bottom(cave_bottom) bb.new('ramp/xy00/z0/%s/c%s' % (terrain, name)).front(normal_front(0)) \ .bottom(cave_bottom_dct[terrain]) bb.new('ramp/xy00/z1/c%s' % name).front(normal_front(1)) \ .top(get_top('back/left')) if sw == 0: # Back left if terrain != 'cccc': bb.new('ramp/xy20/z0/cccc/c%s' % name).front(normal_front(0)) \ .bottom(cave_bottom) bb.new('ramp/xy20/z0/%s/c%s' % (terrain, name)).front(normal_front(0)) \ .bottom(cave_bottom_dct[terrain]) bb.new('ramp/xy20/z1/c%s' % name).front(normal_front(1)) \ .top(get_top('back/right')) if sw == 0 and se == 0: # Back center if terrain != 'cccc': bb.new('ramp/xy10/z0/cccc/c%s' % name).front(normal_front(0)) \ .bottom(cave_bottom) bb.new('ramp/xy10/z0/%s/c%s' % (terrain, name)).front(normal_front(0)) \ .bottom(cave_bottom_dct[terrain]) bb.new('ramp/xy10/z1/c%s' % name).front(normal_front(1)) \ .top(get_top('back/center/dirt2')) bb_ramp = BLOCK.child().shape('ramp_n') for terrain in ('grass', 'dirt', 'dirt2'): bottom = cave_bottom if terrain != 'grass' else cave_bottom_dct['gggg'] bb_ramp.new('ramp/%s/z0' % terrain) \ .bottom(image2.stack(( bottom, ramp['ramp/%s/3' % terrain], ))) \ .back(ramp['ramp/%s/2' % terrain]) bb_ramp.new('ramp/%s/z1' % terrain) \ .bottom(ramp['ramp/%s/1' % terrain]) \ .back(ramp['ramp/%s/0' % terrain]) BLOCK.new('ramp/%s/cap0' % terrain) \ .shape('floor') \ .bottom(ramp['ramp/%s/cap0' % terrain]) BLOCK.new('ramp/%s/cap1' % terrain) \ .shape('empty') \ .bottom(ramp['ramp/%s/cap1' % terrain])
def add_cross(parts): parts['cross/nw'] = image2.stack((parts['inner/nw'], parts['inner/se'])) parts['cross/ne'] = image2.stack((parts['inner/ne'], parts['inner/sw']))