As of Feb 2020, this project is up-to-date with Gen 8 (Pokémon Sword/Shield). All old images from Gen 7 (Pokémon Ultra Sun/Ultra Moon) are still available for legacy support.
' % (item[0], item[1]) for item in old_images])
}, 'Index', version, commit, '.')
return content
def wrap_docs_page(table_content, gen, gen_dir, curr_page, json_file, is_items_page, version, commit, sprites_counter, new_sprites_only):
'''Wraps a documentation page in a table node and adds styling'''
gen_url = f'{REPO_BASE_URL}/{gen_dir}'
json_url = f'{REPO_BASE_URL}/data/{json_file}'
gen_link = f'{gen_dir}'
json_link = f'data/{json_file}'
main_info = '''
This table lists all inventory item sprites. These items are from the last several games and is up-to-date as of Pokémon Sword/Shield. The sprites are from Gen 3 through 8.
All sprites are 32×32 in size. There are sets of sprites: one with a Sword/Shield style white outline around the sprites, and one without (as all previous games). Both sets contain the same number of sprites.
''' if is_items_page else '''
This table lists all Pokémon box sprites for Gen %(gen)s%(subtype)s, which can be found in the %(gen_link)s directory. The list is up-to-date as of Pokémon Sword/Shield, and some of the sprites are from an earlier generation. All shiny sprites were custom-made and are not found in-game.
All box sprites are 68×56 as of Gen 8; the old Gen 7 sprites have been updated to the new size and contrast. (The original 40×30 sprites from Gen 7 are still available in the legacy sprites directory.)
The data for this list (Pokémon names, forms, etc.) is from the gen-%(gen)s key of the items from %(json_link)s.
%(new_sprites_only)s
''' % {
'gen': gen,
'gen_link': gen_link,
'json_link': json_link,
'new_sprites_only': 'Only items that contain "is_prev_gen_icon": false are shown.' if new_sprites_only else '',
'subtype': ' (new sprites only)' if '-new' in curr_page else '',
}
return wrap_in_html('''
%(title_sprite)sPokéSprite
Database project of box and inventory sprites from the Pokémon core series games
' % (docs_url(item[0]), 'curr' if item[0] == curr_page else '', item[1]) for item in menu]
return ''.join(menu_links)
def get_title_venusaur():
return get_img_node(get_pkm_url(DEX_SPRITE_DIR[8], 'venusaur', True, False, False), None, 'Shiny Venusaur', 'p')
def wrap_in_html(content, title, version, commit, res_dir = '.'):
return '''
PokéSprite - %(title)s
%(content)s
''' % {
'res_dir': res_dir,
'content': content,
'title': title,
'version': version,
'commit': commit
}
def run_cmd(cmd):
return subprocess.check_output(cmd, cwd=BASE_DIR).strip().decode('utf-8')
def write_file(filename, content):
with open(filename, 'wt') as file:
print(content, file=file)
def docs_url(slug):
return f'{DOCS_BASE_URL}/overview/{slug}.html'
def read_repo_state():
'''Returns information about the current state of the repository'''
version = '[unknown]'
try:
package = read_json_file(REPO_PACKAGE)
version = package['version']
except:
pass
# In case this fails (e.g. Git is not installed, or this isn't a repo).
commit = '[unknown]'
try:
count = run_cmd(['git', 'rev-list', 'HEAD', '--count'])
branch = run_cmd(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
hash = run_cmd(['git', 'rev-parse', '--short', '--verify', 'HEAD'])
commit = f'{branch}-{count} [{hash}]'
return (version, commit)
except subprocess.CalledProcessError:
return (version, commit)
except OSError:
return (version, commit)
def read_json_file(file):
'''Reads a single JSON and returns a dict'''
with open(file) as json_file:
return json.load(json_file)
def read_data():
'''Retrieves Pokémon and items JSON data'''
return { 'dex': read_json_file(DEX_JSON), 'itm': read_json_file(ITM_JSON), 'etc': read_json_file(ETC_JSON) }
def get_pkm_form(form_name, form_alias, is_unofficial_icon):
title = []
daggers = []
if form_alias is not None:
alias = f'"{form_alias}"' if form_alias else 'default form'
title.append(f'Alias of {alias}')
daggers.append('†')
if is_unofficial_icon:
title.append('Unofficial icon (see below)')
daggers.append('‡')
if len(title):
title = '; '.join(title)
daggers = ''.join(daggers)
return f'{form_name}{daggers}'
return form_name
def get_pkm_url(base, slug, is_shiny, is_female, is_right):
return ''.join([
base,
'/regular' if not is_shiny else '/shiny',
'/female' if is_female else '',
'/right' if is_right else '',
'/',
slug,
'.png'
])
def get_etc_url(base, slug):
return ''.join([
base,
'/',
slug,
'.png'
])
def get_itm_url(base, ns, group, file):
return ''.join([
base,
'/',
ns,
'/',
group,
'/',
file,
'.png'
])
def get_pkm_gen(is_prev_gen_icon, docs_gen):
prev_gen = str(int(docs_gen) - 1)
if is_prev_gen_icon:
return { 'node': prev_gen, 'expl': f'Icon is from generation {prev_gen}', 'cls': '' }
return ''
def get_pkm_gender(is_female, has_female):
if not has_female:
return ''
node = 'F' if is_female else 'M'
expl = 'Female sprite' if is_female else 'Male sprite'
return { 'node': node, 'expl': expl, 'cls': f' gender-{node.lower()}' }
def get_pkm_unofficial(is_unofficial_icon):
return '✓' if is_unofficial_icon else ''
def get_td_node(td):
# If the column is a list, we'll render several columns.
if isinstance(td, list):
cols = []
non_empty = list(filter(len, td))
first_col_span = len(td) - (len(non_empty) - 1)
cols.append(f'
{td[0]}
')
# The rest of the columns are either plain strings, or a dict containing { node, expl }.
for col in non_empty[1:]:
if isinstance(col, dict):
node = col['node']
expl = col['expl']
cls = col['cls']
cols.append(f'
{node}
')
else:
cols.append(f'
{col}
')
return ''.join(cols)
# Otherwise it's a string, and we'll check if it contains an image.
attr = ' class="image"' if str(td)[:4] == '{td}'
def get_img_node(url, name, form_name, type):
return f''
def reset_counter():
'''Resets the global sprite counter'''
global _n_counter
_n_counter = 0
def get_counter():
'''Increments and returns the global sprite counter'''
global _n_counter
_n_counter += 1
return _n_counter
def determine_form(slug, form_name, form_data):
'''Return two Pokémon form strings: one for display, and one referencing a file'''
# The regular form is indicated by a '$', and can be an alias of another one.
form_value = '' if form_name == '$' else form_name
form_alias = form_data.get('is_alias_of', None)
form_alias = '' if form_alias == '$' else form_alias
form_file = form_alias if form_alias is not None else form_value
form_display = form_value
# Save two slugs: the first one is literally just the Pokémon name plus its form,
# and the other uses the 'is_alias_of' slug and is used for selecting the right file.
form_slug_display = '-'.join(filter(len, [slug, form_display]))
form_slug_file = '-'.join(filter(len, [slug, form_file]))
return (form_slug_file, form_slug_display, form_alias)
def append_pkm(cols, base, slug_display, slug_file, form_name, form_alias, has_female, is_female, is_right, is_unofficial_icon, is_prev_gen_icon, docs_gen):
'''Adds a single Pokémon row'''
cols.append([
get_counter(),
get_img_node(get_pkm_url(base, slug_file, False, is_female, is_right), None, form_name, 'p'),
get_img_node(get_pkm_url(base, slug_file, True, is_female, is_right), None, form_name, 'p'),
[get_pkm_form(form_name, form_alias, is_unofficial_icon), get_pkm_gender(is_female, has_female), get_pkm_gen(is_prev_gen_icon, docs_gen)],
f'{slug_display}'
])
def append_pkm_form(cols, base, slug_display, slug_file, form_name, form_alias, has_female, has_right, add_female, add_right, is_unofficial_icon, is_prev_gen_icon, docs_gen):
'''Adds columns for a single form: at least two, then female sprites, then right-facing sprites'''
append_pkm(cols, base, slug_display, slug_file, form_name, form_alias, has_female, False, False, is_unofficial_icon, is_prev_gen_icon, docs_gen)
if has_female and add_female: append_pkm(cols, base, slug_display, slug_file, form_name, form_alias, has_female, True, False, is_unofficial_icon, is_prev_gen_icon, docs_gen)
if has_right and add_right: append_pkm(cols, base, slug_display, slug_file, form_name, form_alias, has_female, False, True, is_unofficial_icon, is_prev_gen_icon, docs_gen)
def generate_items_table(itm, etc, dirs, curr_page, json_file, version = '[unknown]', commit = '[unknown]'):
'''Generates a documentation table for inventory sprites'''
reset_counter()
new_sprites_only = '-new' in curr_page
base_url = REPO_BASE_URL
sprites_counter = 0
buffer = []
buffer.append('')
buffer.append('
')
buffer.append('')
buffer.append('')
item_dict = {}
for id, item in itm.items():
group, name = item.split('/')
if not item_dict.get(group):
item_dict[group] = []
item_dict[group].append({ 'name': name, 'id': id })
for group, items in item_dict.items():
buffer.append(f'
{group.title()}
')
for item in items:
count = get_counter()
name = item['name']
id = item['id']
imgs = ['
Gen %(gen)s sprite overview table%(subtype)s pokesprite-images v%(version)s %(commit)s
' % { 'subtype': ' (new sprites only)' if new_sprites_only else '', 'gen': gen, 'version': version, 'commit': commit })
buffer.append('
#
Dex
Name
名前/ローマ字
Sprites
Form
Slug
')
buffer.append('')
buffer.append('')
# Loop over each Pokémon and generate rows for each of its forms, one regular and one shiny,
# including gender differences and right-facing sprites.
for idx, pkm in dex.items():
#if int(idx) > 25 and idx != '172' and idx != '593': continue
slug_en = pkm['slug']['eng']
gen_data = pkm[f'gen-{str(gen)}']
# Main columns - contains general information spanned across all rows.
main_cols = [f'#{str(idx)}', pkm['name']['eng'], pkm['name']['jpn'], pkm['name']['jpn_ro']]
# Form columns - form-specific information. A global sprite counter is also prepended.
form_cols = []
if not 'forms' in gen_data:
continue
for form_name, form_data in gen_data['forms'].items():
form_slug_file, form_slug_display, form_alias = determine_form(slug_en, form_name, form_data)
form_name_clean = EMPTY_PLACEHOLDER if form_name == '$' else form_name
is_prev_gen_icon = form_data.get('is_prev_gen_icon', False)
if new_sprites_only and is_prev_gen_icon:
continue
append_pkm_form(
form_cols,
base_url,
form_slug_display,
form_slug_file,
form_name_clean,
form_alias,
form_data.get('has_female', False),
form_data.get('has_right', False),
add_female,
add_right,
form_data.get('is_unofficial_icon', False),
form_data.get('is_prev_gen_icon', False),
gen
)
if not form_cols:
continue
first_col = form_cols[0]
rest_cols = form_cols[1:]
sprites_counter += len(form_cols)
# First row (containing one form and all main cols):
buffer.append('
')
buffer.append(f'
{first_col[0]}
')
for col in main_cols:
buffer.append(f'
{col}
')
for first_row_col in first_col[1:]:
buffer.append(get_td_node(first_row_col))
buffer.append('
')
# All other rows (only form cols, skipping over the main cols):
for row in rest_cols:
buffer.append('
')
for col in row:
buffer.append(get_td_node(col))
buffer.append('
')
# Add the remaining other icons.
if not new_sprites_only:
buffer.append('
†: form is an alias of another form and doesn't have a separate image. ‡: this icon is unofficial (not directly lifted from the games; only applies to non-shiny sprites, as shiny sprites are all unofficial).