Lend the borrowed icons to the sheet and forumsprites too
The alias table sat in smogon.build.ts, so only xyicons/ got it. Two other sets publish the same directory and had the same holes: the smogdex sheet, where the dex asks for a sprite- class per species it renders, and forumsprites. Neither had a class or a file for a totem, a Gourgeist size or Mimikyu-Busted. So the table belongs beside the names, not beside one set that uses them. It moves to data/lib as ICON_ALIASES with an iconNames() to apply it, which the sheet calls in place of smogonNames() and the two deploys reach through a NameOpts on publishedNames(). Keyed by the name holding the icon now, since that is the direction all three consumers read it in. It stays out of smogonNames(): src/models has real art for every one of these, and in xy/ the lending name would be claimed by the base sprite and shadow the forme's own. Greninja-Bond goes the other way, into SPECIES_ALIASES, because it is not an icon shortage. Battle Bond's form looks like any other Greninja, and the tree agrees: all seven -obond files under src/, in gen5, pmd and previews/gen9, are byte for byte their base, and no model was ever drawn. xy/ was serving the gen 5 rendition of a Greninja; it now serves the animation, and the seven duplicates go. The sheet gains 22 classes, forumsprites 22 files. xy/ changes one file, greninja-bond.gif, which is now greninja.gif. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@@ -69,7 +69,7 @@ deploy(async ctx => {
|
||||
await itemspritecopy(manifest, f, {dir: `${ASSETS}/forumsprites`});
|
||||
}
|
||||
for (let f of forumG6) {
|
||||
await spritecopy(manifest, f, {dir: `${ASSETS}/forumsprites`}, true);
|
||||
await spritecopy(manifest, f, {dir: `${ASSETS}/forumsprites`}, {allowUnknown: true, icons: true});
|
||||
}
|
||||
manifest.write('__meta/forumsprites/manifest.json');
|
||||
});
|
||||
|
||||
@@ -84,16 +84,54 @@ export const ITEM_ALIASES: Record<string, string[]> = {
|
||||
// The second name a sprite answers to, where one picture publishes twice.
|
||||
// Meowstic is a disagreement: PS's is the male, baseForme M with Meowstic-F the
|
||||
// alt forme, while the dex splits the pair evenly and calls that entry
|
||||
// Meowstic-M. Toxtricity is a shortage: the games drew one Gigantamax
|
||||
// Toxtricity and not two, which is why PS's own icon sheet gives Amped and
|
||||
// Low-Key a single slot and its animations no low-key gmax at all. Keyed and
|
||||
// valued in published smogon form, because only that side asks; PS wants
|
||||
// `meowstic` and `toxtricitygmax`, which is what the filenames already say.
|
||||
// Meowstic-M. The other two are one picture wearing two names. The games drew
|
||||
// one Gigantamax Toxtricity and not two, which is why PS's own icon sheet gives
|
||||
// Amped and Low-Key a single slot and its animations no low-key gmax at all.
|
||||
// Greninja-Bond is Battle Bond's form and looks like any other Greninja, unlike
|
||||
// Greninja-Ash, which is drawn: the gen 5 renditions of the two are byte for
|
||||
// byte the base sprite, and nothing ever drew a model. Keyed and valued in
|
||||
// published smogon form, because only that side asks; PS wants `meowstic`,
|
||||
// `toxtricitygmax` and `greninja`, which is what the filenames already say.
|
||||
export const SPECIES_ALIASES: Record<string, string[]> = {
|
||||
greninja: ['greninja-bond'],
|
||||
meowstic: ['meowstic-m'],
|
||||
'toxtricity-gmax': ['toxtricity-low-key-gmax'],
|
||||
};
|
||||
|
||||
// Formes the gen 6 icon set has no art for, because the games drew them none.
|
||||
// PS's client says the same thing in its own sheet, where each of these sits at
|
||||
// the slot of the forme it shares, under "alt forms with duplicate icons".
|
||||
// Keyed by the name that has the icon, in published smogon form.
|
||||
//
|
||||
// The icon sets alone borrow this way. src/models has a real animation for
|
||||
// every one of these, so smogonNames() must not know about them: in xy/ the
|
||||
// borrowing name would be claimed by the base sprite and shadow the forme's
|
||||
// own art.
|
||||
export const ICON_ALIASES: Record<string, string[]> = {
|
||||
araquanid: ['araquanid-totem'],
|
||||
gourgeist: ['gourgeist-large', 'gourgeist-small', 'gourgeist-super'],
|
||||
gumshoos: ['gumshoos-totem'],
|
||||
'kommo-o': ['kommo-o-totem'],
|
||||
lurantis: ['lurantis-totem'],
|
||||
'marowak-alola': ['marowak-alola-totem'],
|
||||
mimikyu: ['mimikyu-busted', 'mimikyu-busted-totem', 'mimikyu-totem'],
|
||||
pichu: ['pichu-spiky-eared'],
|
||||
pumpkaboo: ['pumpkaboo-large', 'pumpkaboo-small', 'pumpkaboo-super'],
|
||||
'raticate-alola': ['raticate-alola-totem'],
|
||||
ribombee: ['ribombee-totem'],
|
||||
rockruff: ['rockruff-dusk'],
|
||||
salazzle: ['salazzle-totem'],
|
||||
togedemaru: ['togedemaru-totem'],
|
||||
vikavolt: ['vikavolt-totem'],
|
||||
};
|
||||
|
||||
// Every name a gen 6 icon answers to: its own, and any forme with no icon of
|
||||
// its own that borrows it.
|
||||
export function iconNames(sn: SpriteFilename): string[] {
|
||||
let names = smogonNames(sn);
|
||||
return [...names, ...names.flatMap(n => ICON_ALIASES[n] ?? [])];
|
||||
}
|
||||
|
||||
// Every name a sprite answers to on the smogon side: its own, and any alias.
|
||||
export function smogonNames(sn: SpriteFilename): string[] {
|
||||
let name = publishedName(sn, smogon);
|
||||
|
||||
@@ -69,7 +69,15 @@ function extOf(f: Sprite, ext?: string): string {
|
||||
// copies, since the mapping isn't one name per filename in either direction:
|
||||
// Meowstic answers to two, and the forme slots the games gave one sprite (the
|
||||
// six Minior meteors, Zygarde's Power Construct pair) answer to the same one.
|
||||
export function publishedNames(f: Sprite, allowUnknown = false): string[] {
|
||||
export type NameOpts = {
|
||||
// forumsprites publishes Unknown; no other set does.
|
||||
allowUnknown?: boolean,
|
||||
// The gen 6 icon sets publish the formes they have no icon for under a
|
||||
// borrowed one. See ICON_ALIASES.
|
||||
icons?: boolean,
|
||||
};
|
||||
|
||||
export function publishedNames(f: Sprite, opts: NameOpts = {}): string[] {
|
||||
let sn = spritedata.parseFilename(f.name);
|
||||
|
||||
// Skip asymmetrical for now
|
||||
@@ -79,19 +87,19 @@ export function publishedNames(f: Sprite, allowUnknown = false): string[] {
|
||||
|
||||
if (sn.kind === 'x') {
|
||||
// Skip these, we don't use Unknown/Substitute
|
||||
if (!allowUnknown || sn.name !== 'unknown') {
|
||||
if (!opts.allowUnknown || sn.name !== 'unknown') {
|
||||
return [];
|
||||
}
|
||||
} else if (sn.kind !== 's') {
|
||||
throw new Error(`Not a specie sprite: ${f.name}`);
|
||||
}
|
||||
|
||||
return spritedata.smogonNames(sn);
|
||||
return opts.icons ? spritedata.iconNames(sn) : spritedata.smogonNames(sn);
|
||||
}
|
||||
|
||||
export async function spritecopy(manifest: Manifest, f: Sprite, dest: Dest,
|
||||
allowUnknown = false): Promise<void> {
|
||||
for (let name of publishedNames(f, allowUnknown)) {
|
||||
opts: NameOpts = {}): Promise<void> {
|
||||
for (let name of publishedNames(f, opts)) {
|
||||
await manifest.copy(f, dest, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,53 +74,18 @@ let xyIcons = forEachRule('src/minisprites/pokemon/gen6/*.png', {
|
||||
cmds: [trimimg(), compresspng({config: 'MINISPRITE'})],
|
||||
}, '%b');
|
||||
|
||||
// The games drew these formes no icon of their own, so gen 6 has none and
|
||||
// nothing upstream does either: PS ships icons as one dexnum-indexed sheet, and
|
||||
// its own copy of this list sits commented out in ps-pokemon.sheet.mjs under
|
||||
// "alt forms with duplicate icons". Serve each the icon it shares. Only this
|
||||
// set needs them; xy/ has real art for all 22.
|
||||
let xyIconAliases: Record<string, string> = {
|
||||
'araquanid-totem': 'araquanid',
|
||||
'gourgeist-large': 'gourgeist',
|
||||
'gourgeist-small': 'gourgeist',
|
||||
'gourgeist-super': 'gourgeist',
|
||||
'greninja-bond': 'greninja',
|
||||
'gumshoos-totem': 'gumshoos',
|
||||
'kommo-o-totem': 'kommo-o',
|
||||
'lurantis-totem': 'lurantis',
|
||||
'marowak-alola-totem': 'marowak-alola',
|
||||
'mimikyu-busted': 'mimikyu',
|
||||
'mimikyu-busted-totem': 'mimikyu',
|
||||
'mimikyu-totem': 'mimikyu',
|
||||
'pichu-spiky-eared': 'pichu',
|
||||
'pumpkaboo-large': 'pumpkaboo',
|
||||
'pumpkaboo-small': 'pumpkaboo',
|
||||
'pumpkaboo-super': 'pumpkaboo',
|
||||
'raticate-alola-totem': 'raticate-alola',
|
||||
'ribombee-totem': 'ribombee',
|
||||
'rockruff-dusk': 'rockruff',
|
||||
'salazzle-totem': 'salazzle',
|
||||
'togedemaru-totem': 'togedemaru',
|
||||
'vikavolt-totem': 'vikavolt',
|
||||
};
|
||||
|
||||
deploy(ctx => {
|
||||
// icons: the gen 6 set has no art for some formes and lends them another's,
|
||||
// which the smogdex sheet and forumsprites do off the same directory.
|
||||
let byName = new Map<string, Sprite>();
|
||||
for (let f of xyIcons) {
|
||||
for (let name of publishedNames(f)) {
|
||||
for (let name of publishedNames(f, {icons: true})) {
|
||||
if (byName.has(name)) {
|
||||
throw new Error(`Two icons published as ${name}`);
|
||||
}
|
||||
byName.set(name, f);
|
||||
}
|
||||
}
|
||||
for (let [name, from] of Object.entries(xyIconAliases)) {
|
||||
let f = byName.get(from);
|
||||
if (f === undefined) {
|
||||
throw new Error(`No ${from} icon to publish as ${name}`);
|
||||
}
|
||||
if (byName.has(name)) {
|
||||
throw new Error(`${name} has an icon of its own now; drop the alias`);
|
||||
}
|
||||
byName.set(name, f);
|
||||
}
|
||||
for (let [name, f] of byName) {
|
||||
smogonSpritecopy(ctx, f, 'xyicons', [name]);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 551 B |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 798 B |
|
Before Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 945 B |
|
Before Width: | Height: | Size: 945 B |
@@ -33,7 +33,7 @@ for (let [filename, sprite] of Object.entries(result.coordinates)) {
|
||||
continue;
|
||||
}
|
||||
// TODO would like to use psid here, mess with it later.
|
||||
for (let name of spritedata.smogonNames(parsed)) {
|
||||
for (let name of spritedata.iconNames(parsed)) {
|
||||
sprites.set(name, sprite);
|
||||
}
|
||||
}
|
||||
|
||||