diff --git a/.gitignore b/.gitignore index c22d0b2ce4c..9d96ed04a15 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,7 @@ coverage /dependency-graph.svg /.vs + + +# Script outputs +./*.csv \ No newline at end of file diff --git a/scripts/find_sprite_variant_mismatches.py b/scripts/find_sprite_variant_mismatches.py new file mode 100644 index 00000000000..483695fdb66 --- /dev/null +++ b/scripts/find_sprite_variant_mismatches.py @@ -0,0 +1,98 @@ +""" +Validates the contents of the variant's masterlist file and identifies +any mismatched entries for the sprite of the same key between front, back, exp, exp back, and female. + +This will create a csv file that contains all of the entries with mismatches. + +An empty entry means that there was not a mismatch for that version of the sprite (meaning it matches front). +""" + +import sys + +if sys.version_info < (3, 7): + msg = "This script requires Python 3.7+" + raise RuntimeError(msg) + +import json +import os +import csv +from dataclasses import dataclass, field +from typing import Literal as L + +MASTERLIST_PATH = os.path.join( + os.path.dirname(os.path.dirname(__file__)), "public", "images", "pokemon", "variant", "_masterlist.json" +) +DEFAULT_OUTPUT_PATH = "sprite-mismatches.csv" + + +@dataclass(order=True) +class Sprite: + key: str = field(compare=False) + front: list[int] = field(default_factory=list, compare=False) + back: list[int] = field(default_factory=list, compare=False) + female: list[int] = field(default_factory=list, compare=False) + exp: list[int] = field(default_factory=list, compare=False) + expback: list[int] = field(default_factory=list, compare=False) + sortedKey: tuple[int] | tuple[int, str] = field(init=False, repr=False, compare=True) + + def as_row(self) -> tuple[str, list[int] | L[""], list[int] | L[""], list[int] | L[""], list[int] | L[""], list[int] | L[""]]: + """return sprite information as a tuple for csv writing""" + return (self.key, self.front or "", self.back or "", self.exp or "", self.expback or "", self.female or "") + + def is_mismatch(self) -> bool: + """return True if the female, back, or exp sprites do not match the front""" + for val in [self.back, self.exp, self.expback, self.female]: + if val != [] and val != self.front: + return True + return False + + def __post_init__(self): + split = self.key.split("-", maxsplit=1) + self.sortedKey = (int(split[0]), split[1]) if len(split) == 2 else (int(split[0]),) + + +def make_mismatch_sprite_list(path): + with open(path, "r") as f: + masterlist: dict = json.load(f) + + # Go through the keys in "front" and "back" and make sure they match the masterlist + back_data: dict[str, list[int]] = masterlist.pop("back", {}) + exp_data: dict[str, list[int]] = masterlist.pop("exp", {}) + exp_back_data: dict[str, list[int]] = exp_data.get("back", []) + female_data: dict[str, list[int]] = masterlist.pop("female", {}) + + sprites: list[Sprite] = [] + + for key, item in masterlist.items(): + sprite = Sprite( + key, front=item, back=back_data.get(key, []), exp=exp_data.get(key, []), expback=exp_back_data.get(key, []), female=female_data.get(key, []) + ) + if sprite.is_mismatch(): + sprites.append(sprite) + + return sprites + + +def write_mismatch_csv(filename: str, mismatches: list[Sprite]): + with open(filename, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["key", "front", "back", "exp", "expback", "female"]) + for sprite in sorted(mismatches): + writer.writerow(sprite.as_row()) + + +if __name__ == "__main__": + import argparse + + p = argparse.ArgumentParser("find_sprite_variant_mismatches", description=__doc__) + + p.add_argument( + "-o", + "--output", + default=DEFAULT_OUTPUT_PATH, + help=f"The path to a file to save the output file. If not specified, will write to {DEFAULT_OUTPUT_PATH}.", + ) + p.add_argument("--masterlist", default=MASTERLIST_PATH, help=f"The path to the masterlist file to validate. Defaults to {MASTERLIST_PATH}.") + args = p.parse_args() + mismatches = make_mismatch_sprite_list(args.masterlist) + write_mismatch_csv(args.output, mismatches)