#!/usr/bin/env python3 """ Extract sprites from HGSS follower spritesheets. """ import os.path import subprocess import sys from glob import glob import png SPRITESHEETS = [('gen1.png', 15, 11, 1)] output_dir = 'sprites' index_to_name = {} with open('names.txt', 'r') as f: for line in f: index, name = line.split(' ')[:2] name = name.strip() index_to_name[int(index)] = name.lower() name_to_index = {v: k for k, v in index_to_name.items()} PKMN_GRAPHICS = os.path.join('graphics', 'pokemon') def extract_sprites(spritesheet): path, width, height, offset = spritesheet for y in range(height): for x in range(width): if x == 3 and y == 0 or x == 10 and y == 1: continue output_path = os.path.join(output_dir, f'{offset:03d}.png') subprocess.run(['convert', '-extract', f'64x128+{x*(64+1)}+{y*(128+1)}', path, output_path], check=True) offset += 1 def stack_sprite(name, path): joinp = os.path.join frames = [joinp(path, 'down', name), joinp(path, 'down', 'frame2', name), joinp(path, 'up', name), joinp(path, 'up', 'frame2', name), joinp(path, 'left', name), joinp(path, 'left', 'frame2', name)] output = joinp(path, name) subprocess.run(['convert'] + frames + ['+append', output], check=True) print(f'Stacked {output}') def canonicalize_names(): for path in glob('overworld/**/*.png', recursive=True): head, tail = os.path.split(path) name, ext = os.path.splitext(tail) try: num = int(name) except ValueError: continue new_name = f'{num:03d}' new_path = os.path.join(head, new_name+ext) os.rename(path, new_path) print(path, '->', new_path) def closest_color(c, palette): min_d = float('inf') best = 0 r1, g1, b1 = c for i, (r2, g2, b2) in enumerate(palette[1:], 1): # Color diff from https://stackoverflow.com/questions/1847092/given-an-rgb-value-what-would-be-the-best-way-to-find-the-closest-match-in-the-d d = ((r2-r1)*0.30)**2 + ((g2-g1)*0.59)**2 + ((b2-b1)*0.11)**2 if d < min_d: min_d = d best = i return best def apply_palette(palette_file, input_file, output_file): # Apply one file's palette to another plt = png.Reader(palette_file) plt.read() target_palette = tuple(c[:3] for c in plt.palette()) inp = png.Reader(input_file) w, h, rows, _ = inp.read() src_palette = tuple(c[:3] for c in inp.palette()) with open(output_file, 'wb') as f: new_rows = [] for row in rows: new_rows.append([closest_color(src_palette[c], target_palette) if c else 0 for c in row]) w = png.Writer(width=w, height=h, bitdepth=4, palette=target_palette) w.write(f, new_rows) def paletteify(path, output_path=None): output_path = output_path or path joinp = os.path.join _, tail = os.path.split(path) species, _ = os.path.splitext(tail) front = png.Reader(joinp(PKMN_GRAPHICS, species, 'anim_front.png')) front.read() target_palette = tuple(c[:3] for c in front.palette()) r, g, b = target_palette[0] color = f'rgb({r},{g},{b})' # Strip alpha color subprocess.run(['convert', path, '-background', color, '-alpha', 'remove', output_path], check=True) apply_palette(joinp(PKMN_GRAPHICS, species, 'anim_front.png'), output_path, output_path) # Sprites from https://veekun.com/dex/downloads if __name__ == '__main__': args = sys.argv[1:] if args: paletteify(args[0]) else: for path in sorted(glob('overworld/*.png')): _, tail = os.path.split(path) name, _ = os.path.splitext(tail) output_path = os.path.join('graphics/object_events/pics/pokemon', f'{name}.png') try: paletteify(path, output_path) except Exception as e: print(name, e.__class__.__name__, e, file=sys.stderr)