Added all 386 follower sprites.
Allow NPC pokemon objects via a custom script header.
1
.gitignore
vendored
|
@ -34,4 +34,5 @@ porymap.project.cfg
|
|||
*.a
|
||||
.fuse_hidden*
|
||||
.ccls-cache/*
|
||||
overworld/
|
||||
*.sna
|
||||
|
|
133
extract_sprites.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
#!/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(index, path):
|
||||
joinp = os.path.join
|
||||
name = f'{index:03d}.png'
|
||||
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, f'{index_to_name[index]}.png')
|
||||
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):
|
||||
# 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) 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:
|
||||
f0 = open('graphics_info.h', 'w', buffering=1)
|
||||
f1 = open('pic_tables.h', 'w', buffering=1)
|
||||
f2 = open('event_graphics.h', 'w', buffering=1)
|
||||
f3 = open('spritesheet_extra.mk', 'w', buffering=1)
|
||||
for index in range(1, 386+1):
|
||||
stack_sprite(index, 'overworld')
|
||||
try:
|
||||
species = index_to_name[index]
|
||||
path = os.path.join('overworld', f'{species}.png')
|
||||
output_path = os.path.join('graphics', 'object_events', 'pics', 'pokemon', f'{species}.png')
|
||||
paletteify(path, output_path)
|
||||
except Exception as e:
|
||||
print(e.__class__.__name__, e, file=sys.stderr)
|
||||
continue
|
||||
d = 32 if species not in {'steelix', 'wailord', 'kyogre', 'groudon', 'rayquaza', 'lugia', 'ho_oh'} else 64
|
||||
line = f'[SPECIES_{species.upper()}] = {{0xFFFF, OBJ_EVENT_PAL_TAG_DYNAMIC, OBJ_EVENT_PAL_TAG_NONE, {d*16}, {d}, {d}, 2, SHADOW_SIZE_M, FALSE, FALSE, TRACKS_FOOT, &gObjectEventBaseOam_{d}x{d}, gObjectEventSpriteOamTables_{d}x{d}, gObjectEventImageAnimTable_Following, gObjectEventPicTable_{species.capitalize()}, gDummySpriteAffineAnimTable}},'
|
||||
f0.write(line + '\n')
|
||||
lines = [f'const struct SpriteFrameImage gObjectEventPicTable_{species.capitalize()}[] = {{']
|
||||
lines += [f' overworld_frame(gObjectEventPic_{species.capitalize()}, 4, 4, {frame}),' for frame in range(6)]
|
||||
lines.append('};')
|
||||
f1.write('\n'.join(lines) + '\n')
|
||||
line = f'const u32 gObjectEventPic_{species.capitalize()}[] = INCBIN_U32("graphics/object_events/pics/pokemon/{species}.4bpp");'
|
||||
f2.write(line + '\n')
|
||||
lines = [f'$(OBJEVENTGFXDIR)/pokemon/{species}.4bpp: %.4bpp: %.png\n',
|
||||
f'\t$(GFX) $< $@ -mwidth {int(d/8)} -mheight {int(d/8)}\n\n']
|
||||
f3.write(''.join(lines))
|
||||
f0.close()
|
||||
f1.close()
|
||||
f2.close()
|
||||
f3.close()
|
BIN
graphics/object_events/pics/pokemon/abra.png
Normal file
After Width: | Height: | Size: 485 B |
BIN
graphics/object_events/pics/pokemon/absol.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
graphics/object_events/pics/pokemon/aerodactyl.png
Normal file
After Width: | Height: | Size: 961 B |
BIN
graphics/object_events/pics/pokemon/aggron.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
graphics/object_events/pics/pokemon/aipom.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
graphics/object_events/pics/pokemon/alakazam.png
Normal file
After Width: | Height: | Size: 750 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1 KiB |
BIN
graphics/object_events/pics/pokemon/ampharos.png
Normal file
After Width: | Height: | Size: 696 B |
BIN
graphics/object_events/pics/pokemon/anorith.png
Normal file
After Width: | Height: | Size: 722 B |
BIN
graphics/object_events/pics/pokemon/arbok.png
Normal file
After Width: | Height: | Size: 708 B |
BIN
graphics/object_events/pics/pokemon/arcanine.png
Normal file
After Width: | Height: | Size: 961 B |
BIN
graphics/object_events/pics/pokemon/ariados.png
Normal file
After Width: | Height: | Size: 952 B |
BIN
graphics/object_events/pics/pokemon/armaldo.png
Normal file
After Width: | Height: | Size: 908 B |
BIN
graphics/object_events/pics/pokemon/aron.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
graphics/object_events/pics/pokemon/articuno.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 690 B |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 520 B |
BIN
graphics/object_events/pics/pokemon/bagon.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
graphics/object_events/pics/pokemon/baltoy.png
Normal file
After Width: | Height: | Size: 388 B |
BIN
graphics/object_events/pics/pokemon/barboach.png
Normal file
After Width: | Height: | Size: 513 B |
BIN
graphics/object_events/pics/pokemon/bayleef.png
Normal file
After Width: | Height: | Size: 692 B |
BIN
graphics/object_events/pics/pokemon/beautifly.png
Normal file
After Width: | Height: | Size: 795 B |
BIN
graphics/object_events/pics/pokemon/beedrill.png
Normal file
After Width: | Height: | Size: 785 B |
BIN
graphics/object_events/pics/pokemon/beldum.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
graphics/object_events/pics/pokemon/bellossom.png
Normal file
After Width: | Height: | Size: 633 B |
BIN
graphics/object_events/pics/pokemon/bellsprout.png
Normal file
After Width: | Height: | Size: 396 B |
BIN
graphics/object_events/pics/pokemon/blastoise.png
Normal file
After Width: | Height: | Size: 834 B |
BIN
graphics/object_events/pics/pokemon/blaziken.png
Normal file
After Width: | Height: | Size: 885 B |
BIN
graphics/object_events/pics/pokemon/blissey.png
Normal file
After Width: | Height: | Size: 594 B |
BIN
graphics/object_events/pics/pokemon/breloom.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
graphics/object_events/pics/pokemon/bulbasaur.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
graphics/object_events/pics/pokemon/butterfree.png
Normal file
After Width: | Height: | Size: 808 B |
BIN
graphics/object_events/pics/pokemon/cacnea.png
Normal file
After Width: | Height: | Size: 509 B |
BIN
graphics/object_events/pics/pokemon/cacturne.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
graphics/object_events/pics/pokemon/camerupt.png
Normal file
After Width: | Height: | Size: 869 B |
BIN
graphics/object_events/pics/pokemon/carvanha.png
Normal file
After Width: | Height: | Size: 749 B |
BIN
graphics/object_events/pics/pokemon/cascoon.png
Normal file
After Width: | Height: | Size: 392 B |
BIN
graphics/object_events/pics/pokemon/caterpie.png
Normal file
After Width: | Height: | Size: 562 B |
BIN
graphics/object_events/pics/pokemon/celebi.png
Normal file
After Width: | Height: | Size: 542 B |
BIN
graphics/object_events/pics/pokemon/chansey.png
Normal file
After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 948 B |
BIN
graphics/object_events/pics/pokemon/charmander.png
Normal file
After Width: | Height: | Size: 506 B |
BIN
graphics/object_events/pics/pokemon/charmeleon.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
graphics/object_events/pics/pokemon/chikorita.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
graphics/object_events/pics/pokemon/chimecho.png
Normal file
After Width: | Height: | Size: 326 B |
BIN
graphics/object_events/pics/pokemon/chinchou.png
Normal file
After Width: | Height: | Size: 667 B |
BIN
graphics/object_events/pics/pokemon/clamperl.png
Normal file
After Width: | Height: | Size: 492 B |
BIN
graphics/object_events/pics/pokemon/claydol.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
graphics/object_events/pics/pokemon/clefable.png
Normal file
After Width: | Height: | Size: 626 B |
BIN
graphics/object_events/pics/pokemon/clefairy.png
Normal file
After Width: | Height: | Size: 456 B |
BIN
graphics/object_events/pics/pokemon/cleffa.png
Normal file
After Width: | Height: | Size: 415 B |
BIN
graphics/object_events/pics/pokemon/cloyster.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
graphics/object_events/pics/pokemon/combusken.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
graphics/object_events/pics/pokemon/corphish.png
Normal file
After Width: | Height: | Size: 785 B |
BIN
graphics/object_events/pics/pokemon/corsola.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
graphics/object_events/pics/pokemon/cradily.png
Normal file
After Width: | Height: | Size: 931 B |
BIN
graphics/object_events/pics/pokemon/crawdaunt.png
Normal file
After Width: | Height: | Size: 863 B |
BIN
graphics/object_events/pics/pokemon/crobat.png
Normal file
After Width: | Height: | Size: 958 B |
BIN
graphics/object_events/pics/pokemon/croconaw.png
Normal file
After Width: | Height: | Size: 778 B |
BIN
graphics/object_events/pics/pokemon/cubone.png
Normal file
After Width: | Height: | Size: 550 B |
BIN
graphics/object_events/pics/pokemon/cyndaquil.png
Normal file
After Width: | Height: | Size: 654 B |
BIN
graphics/object_events/pics/pokemon/delcatty.png
Normal file
After Width: | Height: | Size: 757 B |
BIN
graphics/object_events/pics/pokemon/delibird.png
Normal file
After Width: | Height: | Size: 565 B |
Before Width: | Height: | Size: 547 B After Width: | Height: | Size: 728 B |
BIN
graphics/object_events/pics/pokemon/dewgong.png
Normal file
After Width: | Height: | Size: 750 B |
BIN
graphics/object_events/pics/pokemon/diglett.png
Normal file
After Width: | Height: | Size: 419 B |
BIN
graphics/object_events/pics/pokemon/ditto.png
Normal file
After Width: | Height: | Size: 333 B |
BIN
graphics/object_events/pics/pokemon/dodrio.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
graphics/object_events/pics/pokemon/doduo.png
Normal file
After Width: | Height: | Size: 515 B |
BIN
graphics/object_events/pics/pokemon/donphan.png
Normal file
After Width: | Height: | Size: 658 B |
BIN
graphics/object_events/pics/pokemon/dragonair.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
graphics/object_events/pics/pokemon/dragonite.png
Normal file
After Width: | Height: | Size: 786 B |
BIN
graphics/object_events/pics/pokemon/dratini.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
graphics/object_events/pics/pokemon/drowzee.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
graphics/object_events/pics/pokemon/dugtrio.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
graphics/object_events/pics/pokemon/dunsparce.png
Normal file
After Width: | Height: | Size: 711 B |
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 825 B |
BIN
graphics/object_events/pics/pokemon/duskull.png
Normal file
After Width: | Height: | Size: 474 B |
BIN
graphics/object_events/pics/pokemon/dustox.png
Normal file
After Width: | Height: | Size: 734 B |
BIN
graphics/object_events/pics/pokemon/eevee.png
Normal file
After Width: | Height: | Size: 493 B |
BIN
graphics/object_events/pics/pokemon/ekans.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
graphics/object_events/pics/pokemon/electabuzz.png
Normal file
After Width: | Height: | Size: 580 B |
BIN
graphics/object_events/pics/pokemon/electrike.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
graphics/object_events/pics/pokemon/electrode.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
graphics/object_events/pics/pokemon/elekid.png
Normal file
After Width: | Height: | Size: 515 B |
BIN
graphics/object_events/pics/pokemon/entei.png
Normal file
After Width: | Height: | Size: 1,013 B |
BIN
graphics/object_events/pics/pokemon/espeon.png
Normal file
After Width: | Height: | Size: 739 B |
BIN
graphics/object_events/pics/pokemon/exeggcute.png
Normal file
After Width: | Height: | Size: 824 B |
BIN
graphics/object_events/pics/pokemon/exeggutor.png
Normal file
After Width: | Height: | Size: 787 B |
BIN
graphics/object_events/pics/pokemon/exploud.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
graphics/object_events/pics/pokemon/fearow.png
Normal file
After Width: | Height: | Size: 1,021 B |
BIN
graphics/object_events/pics/pokemon/feebas.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
graphics/object_events/pics/pokemon/feraligatr.png
Normal file
After Width: | Height: | Size: 980 B |
BIN
graphics/object_events/pics/pokemon/flaaffy.png
Normal file
After Width: | Height: | Size: 741 B |
BIN
graphics/object_events/pics/pokemon/flareon.png
Normal file
After Width: | Height: | Size: 744 B |
BIN
graphics/object_events/pics/pokemon/flygon.png
Normal file
After Width: | Height: | Size: 933 B |
BIN
graphics/object_events/pics/pokemon/forretress.png
Normal file
After Width: | Height: | Size: 523 B |
BIN
graphics/object_events/pics/pokemon/furret.png
Normal file
After Width: | Height: | Size: 805 B |