|
@@ -0,0 +1,165 @@
|
|
|
+import io
|
|
|
+
|
|
|
+from PIL import Image
|
|
|
+from bs4 import BeautifulSoup
|
|
|
+from colorspacious import cspace_convert
|
|
|
+import requests
|
|
|
+import numpy as np
|
|
|
+
|
|
|
+import ingest
|
|
|
+
|
|
|
+base = "https://play.pokemonshowdown.com/sprites/ani/"
|
|
|
+back_base = "https://play.pokemonshowdown.com/sprites/ani-back/"
|
|
|
+
|
|
|
+# removing all forms of a pokemon, and also pokestars
|
|
|
+start_with_filters = [
|
|
|
+ # no significant visual changes
|
|
|
+ "arceus-", "silvally-", "genesect-", "pumpkaboo-", "gourgeist-", "unown-", "giratina-",
|
|
|
+ # cannot start the battle in alternate form
|
|
|
+ "castform-", "cherrim-", "aegislash-", "xerneas-", "wishiwashi-", "eiscue-", "mimikyu-",
|
|
|
+ "cramorant-", "morepeko-",
|
|
|
+ # weird event thing
|
|
|
+ "greninja-", "eevee-", "pikachu-", "zarude-", "magearna-",
|
|
|
+ # pokestars
|
|
|
+ "pokestar",
|
|
|
+]
|
|
|
+
|
|
|
+# removing all forms of a type
|
|
|
+end_with_filters = [
|
|
|
+ "-mega.gif", "-megax.gif", "-megay.gif", "-primal.gif", "-ultra.gif",
|
|
|
+ "-gmax.gif", "-eternamax.gif", "-totem.gif", "-f.gif", "-b.gif",
|
|
|
+]
|
|
|
+
|
|
|
+# removing pokemon entirely
|
|
|
+full_filters = [
|
|
|
+ # darmanitan zen forms (cannot start in zen)
|
|
|
+ "darmanitan-galarzen.gif", "darmanitan-zen.gif",
|
|
|
+ # minior core forms (cannot start in anything but -meteor, renamed below)
|
|
|
+ "minior.gif", "minior-blue.gif", "minior-green.gif", "minior-indigo.gif",
|
|
|
+ "minior-orange.gif", "minior-violet.gif", "minior-yellow.gif",
|
|
|
+ # because it is a create-a-pokemon
|
|
|
+ "astrolotl.gif", "aurumoth.gif", "caribolt.gif", "cawmodore.gif", "chromera.gif", "crucibelle.gif",
|
|
|
+ "equilibra.gif", "fidgit.gif", "jumbao.gif", "justyke.gif", "kerfluffle.gif", "kitsunoh.gif",
|
|
|
+ "krilowatt.gif", "malaconda.gif", "miasmaw.gif", "mollux.gif", "naviathan.gif", "necturna.gif",
|
|
|
+ "pajantom.gif", "plasmanta.gif", "pluffle.gif", "protowatt.gif", "scratchet.gif", "smogecko.gif",
|
|
|
+ "smoguana.gif", "smokomodo.gif", "snaelstrom.gif", "stratagem.gif", "tomohawk.gif", "volkraken.gif", "voodoom.gif",
|
|
|
+ # typos/duplicates
|
|
|
+ "buffalant.gif", "klinklang-back.gif", "krikretot.gif", "pumpkabo-super.gif", "magcargo%20.gif",
|
|
|
+ "ratatta-a.gif", "ratatta-alola.gif", "raticate-a.gif",
|
|
|
+ "rotom-h.gif", "rotom-m.gif", "rotom-s.gif", "rotom-w.gif",
|
|
|
+ # not a pokemon
|
|
|
+ "substitute.gif",
|
|
|
+]
|
|
|
+
|
|
|
+# force certain pokemon to stay
|
|
|
+force_keep = [ "meowstic-f.gif" ]
|
|
|
+
|
|
|
+# rename certain pokemon after the fact
|
|
|
+rename = {
|
|
|
+ # dash consistency
|
|
|
+ "nidoranm": "nidoran-m",
|
|
|
+ "nidoranf": "nidoran-f",
|
|
|
+ "porygonz": "porygon-z",
|
|
|
+ "tapubulu": "tapu-bulu",
|
|
|
+ "tapufini": "tapu-fini",
|
|
|
+ "tapukoko": "tapu-koko",
|
|
|
+ "tapulele": "tapu-lele",
|
|
|
+ "hooh": "ho-oh",
|
|
|
+ "mimejr": "mime-jr",
|
|
|
+ "mrmime": "mr-mime",
|
|
|
+ "mrmime-galar": "mr-mime-galar",
|
|
|
+ "mrrime": "mr-rime",
|
|
|
+ "jangmoo": "jangmo-o",
|
|
|
+ "hakamoo": "hakamo-o",
|
|
|
+ "kommoo": "kommo-o",
|
|
|
+ "typenull": "type-null",
|
|
|
+ "oricorio-pompom": "oricorio-pom-pom",
|
|
|
+ "necrozma-duskmane": "necrozma-dusk-mane",
|
|
|
+ "necrozma-dawnwings": "necrozma-dawn-wings",
|
|
|
+ "toxtricity-lowkey": "toxtricity-low-key",
|
|
|
+
|
|
|
+ # rename forms
|
|
|
+ "shellos": "shellos-west",
|
|
|
+ "shaymin": "shaymin-land",
|
|
|
+ "meloetta": "meloetta-aria",
|
|
|
+ "keldeo": "keldeo-ordinary",
|
|
|
+ "hoopa": "hoopa-confined",
|
|
|
+ "burmy": "burmy-plant",
|
|
|
+ "wormadam": "wormadam-plant",
|
|
|
+ "deerling": "deerling-spring",
|
|
|
+ "sawsbuck": "sawsbuck-spring",
|
|
|
+ "vivillon": "vivillon-meadow",
|
|
|
+ "basculin": "basculin-redstriped",
|
|
|
+ "meowstic": "meowstic-male",
|
|
|
+ "meowstic-f": "meowstic-female",
|
|
|
+ "flabebe": "flabebe-red",
|
|
|
+ "floette": "floette-red",
|
|
|
+ "florges": "florges-red",
|
|
|
+ "minior-meteor": "minior",
|
|
|
+ "sinistea": "sinistea-phony",
|
|
|
+ "polteageist": "polteageist-phony",
|
|
|
+ "gastrodon": "gastrodon-west",
|
|
|
+ "furfrou": "furfrou-natural",
|
|
|
+ "wishiwashi": "wishiwashi-school",
|
|
|
+ "tornadus": "tornadus-incarnate",
|
|
|
+ "landorus": "landorus-incarnate",
|
|
|
+ "thundurus": "thundurus-incarnate",
|
|
|
+ "calyrex-ice": "calyrex-ice-rider",
|
|
|
+ "calyrex-shadow": "calyrex-shadow-rider",
|
|
|
+ "urshifu-rapidstrike": "urshifu-rapid-strike",
|
|
|
+ "zacian": "zacian-hero",
|
|
|
+ "zamazenta": "zamazenta-hero",
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+def get_all_pokemon() -> list[str]:
|
|
|
+ soup = BeautifulSoup(requests.get(back_base).text, "html.parser")
|
|
|
+ gifs = [href for a in soup.find_all("a") if (href := a.get("href")).endswith("gif")]
|
|
|
+ return [
|
|
|
+ g[:-4]
|
|
|
+ for g in gifs
|
|
|
+ if g in force_keep or (
|
|
|
+ g not in full_filters
|
|
|
+ and not any(g.startswith(f) for f in start_with_filters)
|
|
|
+ and not any(g.endswith(f) for f in end_with_filters)
|
|
|
+ )
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def load_image(base: str, name: str) -> Image:
|
|
|
+ return Image.open(io.BytesIO(requests.get(base + name + ".gif").content))
|
|
|
+
|
|
|
+
|
|
|
+def get_all_pixels(im: Image) -> list[tuple[int, int, int]]:
|
|
|
+ rgb_pixels = []
|
|
|
+ for fr in range(getattr(im, "n_frames", 1)):
|
|
|
+ im.seek(fr)
|
|
|
+ rgb_pixels += [
|
|
|
+ (r, g, b)
|
|
|
+ for r, g, b, a in im.convert("RGBA").getdata()
|
|
|
+ if not ingest.is_outline(r, g, b, a)
|
|
|
+ ]
|
|
|
+ return rgb_pixels
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ pkmn = get_all_pokemon()
|
|
|
+ print("Found", len(pkmn), "sprites...")
|
|
|
+ errors = []
|
|
|
+ with open("database-anim.js", "w") as outfile:
|
|
|
+ outfile.write("const databaseV2 = [\n")
|
|
|
+ for name in pkmn:
|
|
|
+ print("Ingesting", name, "...")
|
|
|
+ try:
|
|
|
+ front = get_all_pixels(load_image(base, name))
|
|
|
+ back = get_all_pixels(load_image(back_base, name))
|
|
|
+ rgb_pixels = np.array(front + back)
|
|
|
+ jab_pixels = cspace_convert(rgb_pixels, "sRGB255", "CAM02-UCS")
|
|
|
+ stats = [len(rgb_pixels), *ingest.all_stats(jab_pixels), *ingest.all_stats(rgb_pixels)]
|
|
|
+ outfile.write(f' [ "{rename.get(name, name)}", {", ".join(str(n) for n in stats)} ],\n')
|
|
|
+ except Exception as e:
|
|
|
+ print(e)
|
|
|
+ errors.append(name)
|
|
|
+ outfile.write("];\n")
|
|
|
+ print("Errors:", errors)
|
|
|
+
|