Skip to content

Commit 471a90a

Browse files
authored
Add missing Pokémon sprites (#196)
* add script to check missing sprites * add missing front, front shiny sprites * add gen 6 back * add missing gen 8 sprites * add missing gen 6 shiny back * ruff format * fix: optimize sprite renaming with species-form fallback and cleaner suffixes * fixing silvally sprites * add missing alcremie sprites * add missing pikachu region cap * final addition * fix: front sceptile mega * rerun gen9 and update all sprites from latest * fix Xerneas * rename to correct pokemon id, remove some duplicate images * rename part 2 * fix xerneas shiny * revert ampharos mega * fix Zarude-dada back * fix shiny rotom * fix: rapidash, zorua color update * chore: add contributing document * fix: consolidate missing sprite checks into single report * fix: use pathlib, get csv data from upstream, add more comment for explaination * add sort forms to make it consistent later * fix: do not copy if no suffix found to prevent overritting default pokemon id * fix: palafin * revert to ogerpon mask * fix: clarify the logic for renaming to id-formname format
1 parent dc262ce commit 471a90a

934 files changed

Lines changed: 1255 additions & 273 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ desktop.ini
1111
# Versioning
1212
.svn/
1313
.git/
14+
15+
smogon/
16+
scripts/downloads/

CONTRIBUTING_SPRITES.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Contributing Sprites
2+
3+
## Overview
4+
5+
This repository provides helper scripts and documentation to manually maintain the default sprite collection located in `sprites/pokemon/`. Our goal is to provide a complete set of "Gen 5 style" (Black & White) sprites for the entire National Dex.
6+
7+
This initial version of the maintenance workflow focuses specifically on:
8+
9+
1. **Auditing** the `sprites/pokemon/` folder for missing assets.
10+
2. **Synchronizing** with Smogon community spreadsheets for Pokémon with **IDs 650+** (National Dex entries beyond the official Gen 5 games).
11+
12+
## Missing Sprites Tracking and Smogon Synchronization
13+
14+
### 1. Identifying Missing Sprites
15+
16+
**Script:** `scripts/check_missing_sprites.py`
17+
18+
This script identifies which Pokémon or forms are missing from the `sprites/pokemon/` directory.
19+
20+
* **Data Sources:** It references `pokemon.csv` and `pokemon_forms.csv` from the PokéAPI database.
21+
* **Logic:** It checks for the existence of four primary assets for every entry:
22+
* Front (Default & Shiny)
23+
* Back (Default & Shiny)
24+
* **Output:** Missing entries are logged in `missing_sprites.csv` to track our progress toward a 100% complete National Dex.
25+
26+
### 2. Synchronizing with Smogon
27+
28+
**Script:** `scripts/smogon_download.py`
29+
30+
Since official Gen 5 sprites do not exist for newer Pokémon, we source community-made assets from Smogon. This script automates the download and renaming process.
31+
32+
#### Filename Mapping
33+
34+
Smogon uses a shorthand naming system. The script (utilizing logic from `renameSmogon.sh`) translates these into the PokéAPI structure:
35+
36+
| Suffix | Sprite Type | Example |
37+
| --- | --- | --- |
38+
| *(None)* | Front Default | `100.png` |
39+
| `s` | Front Shiny | `100s.png` |
40+
| `b` | Back Default | `100b.png` |
41+
| `sb` | Back Shiny | `100sb.png` |
42+
| `g` | Gigantamax | `100g.png` |
43+
| `_1` | Variant/Form | `100_1.png` |
44+
45+
### 3. Mandatory Manual Verification
46+
47+
The Smogon source data is community-maintained and contains known inconsistencies. **The scripts do not handle these automatically.** Contributors must manually review and correct the following:
48+
49+
### Known Data Quirks
50+
51+
* **Orientation Swaps:** Some filenames are reversed in the source.
52+
* *Example:* For **Blastoise**, `009_2.png` is often the **Back** sprite despite being labeled as a front variant.
53+
* **Action:** Verify the image visually and ensure it is placed in the correct directory.
54+
55+
* **Duplicate Variant IDs:** Different forms may share the same numerical suffix in Smogon spreadsheets.
56+
* *Example:* Both **Hoenn Cap** and **Partner Cap** Pikachu may use `_8`.
57+
* **Action:** Cross-reference with the spreadsheet context and manually rename files to match their unique PokéAPI form IDs.
58+
59+
* **Form Mapping:** Ensure variants (e.g., `_1`, `_2`) are correctly mapped using `forms.json`. If a new form is added, you must update `forms.json` manually.
60+
61+
### How to Use
62+
63+
1. **Check for gaps:** `python scripts/check_missing_sprites.py`
64+
2. **Sync Smogon assets:** `python scripts/smogon_download.py`
65+
3. **Manual Fixes:** Review the downloaded files against the "Known Issues" above and correct names/folders manually before submitting a PR.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ sprites
7676
7777
### `pokemon`
7878

79+
Interested in helping us complete the default Gen 5 style sprite collection? See [CONTRIBUTING_SPRITES.md](./CONTRIBUTING_SPRITES.md) for details on our maintenance scripts for National Dex 650+.
80+
7981
#### `other`
8082

8183
##### `dream-world`

scripts/check_missing_sprites.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import pandas as pd
2+
from pathlib import Path
3+
4+
# CONFIGURATION
5+
GITHUB_BASE_URL = "https://raw.githubusercontent.com/PokeAPI/pokeapi/master/data/v2/csv"
6+
POKEMON_CSV_URL = f"{GITHUB_BASE_URL}/pokemon.csv"
7+
FORMS_CSV_URL = f"{GITHUB_BASE_URL}/pokemon_forms.csv"
8+
VG_CSV_URL = f"{GITHUB_BASE_URL}/version_groups.csv"
9+
10+
# Local Sprite directories relative to this script
11+
# .parent.parent refers to /Parent/ (going up from /Parent/sprites/scripts/)
12+
SCRIPT_DIR = Path(__file__).resolve().parent
13+
BASE_PATH = SCRIPT_DIR.parent / "sprites" / "pokemon"
14+
15+
PATHS = {
16+
"Front": BASE_PATH,
17+
"Front Shiny": BASE_PATH / "shiny",
18+
"Back": BASE_PATH / "back",
19+
"Back Shiny": BASE_PATH / "back" / "shiny",
20+
}
21+
22+
23+
def check_sprites():
24+
# 1. Load Data from GitHub
25+
print("🌐 Fetching latest Pokémon data from GitHub...")
26+
try:
27+
df_pokemon = pd.read_csv(POKEMON_CSV_URL)
28+
df_forms = pd.read_csv(FORMS_CSV_URL)
29+
df_vg = pd.read_csv(VG_CSV_URL)
30+
except Exception as e:
31+
print(f"❌ Error fetching data: {e}")
32+
return
33+
34+
# 2. Merge Data
35+
print("🔍 Processing data and identifying versions...")
36+
37+
# Get generation info via forms
38+
df_forms_subset = df_forms[df_forms["is_default"] == 1][
39+
["pokemon_id", "introduced_in_version_group_id"]
40+
]
41+
42+
df_merged = df_pokemon.merge(
43+
df_forms_subset, left_on="id", right_on="pokemon_id", how="left"
44+
)
45+
df_merged = df_merged.merge(
46+
df_vg[["id", "generation_id"]],
47+
left_on="introduced_in_version_group_id",
48+
right_on="id",
49+
how="left",
50+
)
51+
52+
pokemon_entries = (
53+
df_merged[["id_x", "species_id", "identifier", "generation_id"]]
54+
.rename(columns={"id_x": "pokemon_id", "generation_id": "generation"})
55+
.to_dict("records")
56+
)
57+
58+
# 3. Check Local Files
59+
print(f"🧪 Scanning local folders for {len(pokemon_entries)} Pokémon...")
60+
61+
all_missing = []
62+
63+
for pokemon in pokemon_entries:
64+
p_id = pokemon["pokemon_id"]
65+
s_id = pokemon["species_id"]
66+
name = pokemon["identifier"]
67+
gen = (
68+
int(pokemon["generation"])
69+
if pd.notnull(pokemon["generation"])
70+
else "Unknown"
71+
)
72+
filename = f"{p_id}.png"
73+
74+
for label, folder in PATHS.items():
75+
file_path = folder / filename
76+
77+
if not file_path.exists():
78+
all_missing.append(
79+
{
80+
"pokemon_id": p_id,
81+
"identifier": name,
82+
"species_id": s_id,
83+
"sprite_type": label.lower().replace(" ", "_"),
84+
"generation": gen,
85+
}
86+
)
87+
88+
# 4. Save and Report
89+
if all_missing:
90+
df_missing = pd.DataFrame(all_missing)
91+
df_missing = df_missing.sort_values(by=["pokemon_id", "sprite_type"])
92+
93+
output_path = SCRIPT_DIR / "missing_sprites.csv"
94+
df_missing.to_csv(output_path, index=False)
95+
96+
print("\n" + "=" * 50)
97+
print(f"✅ REPORT GENERATED: {output_path.name}")
98+
print(f"❌ Total Missing Assets: {len(df_missing)}")
99+
print("=" * 50)
100+
print(df_missing.head(10).to_string(index=False))
101+
if len(df_missing) > 10:
102+
print(f"... and {len(df_missing) - 10} more.")
103+
else:
104+
print("\n✅ All assets present! Your collection is complete.")
105+
106+
107+
if __name__ == "__main__":
108+
check_sprites()

0 commit comments

Comments
 (0)