Node.js script for detecting inconsistent character images in small-burg-towns-pack Smallburg base pack
is a really cool pixel art pack by almostApixel that you can find on itch.io.
I was playing around with the folder of images and started building a little demo that generates a random character in a random state, with random skin, random hair etc.
But then I noticed that some images didn't load. The file paths I were relying on weren't consistent. If I for example randomized an adult character with a dark brown ponytail, then the image wouldn't work in all 4 states [idle, jump, run, walk].
Easily fixed by manually renaming the files like I want. I could also notify the author about it. What I did was I wrote a script that detects all (character inconsistencies) in the pack and prints out what to fix. This way I can provide a little bit more helpful feedback to the author that I know is probably pretty busy with keeping track of all the images to export whenever a new release is made.
This script could be used by the author to detect future problems, or by any user of the pack to help adjust inconsistencies that might impact their usage of the pack. Or by YOU if you want to do something similar, take a look at the code below.
In the next part you can see the script and the output I got when placing the script inside the
SmallBurg_village_pack_v2.02_free
folder beside the assets
folder.
And then running:
npm i glob # install the glob npm package dependency for finding all nested files in assets folder
./analyze.mjs # run the script, you need Node.js 14+ or so to run it. (alternatively run `node analyze.mjs`)
output.txt
Images in folders that probably shouldn't have a space in them
[
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_black.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_black.png',
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_blonde.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_blonde.png',
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_dark_brown.png',
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_dark_light.png',
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_purple.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_purple.png',
'./assets/character/adult/hairs/idle/pony tail/idle_hairs_ponytail_red.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_red.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_brown_dark.png',
'./assets/character/adult/hairs/walk/pony tail/walk_hairs_ponytail_brown_light.png'
]
Images of character adult that aren't present in all 4 states:
{
adult_clothing_acessories_tie: [ 'idle' ],
adult_clothing_top_shirt_overhalls: [ 'idle' ],
adult_clothing_acessories_tie_red: [ 'jump', 'run', 'walk' ],
adult_clothing_top_overhalls: [ 'jump', 'run', 'walk' ],
adult_hairs_balding_hairs_balding: [ 'idle' ],
adult_hairs_bigbun_brown_dark: [ 'idle', 'jump', 'walk' ],
adult_hairs_long_purple: [ 'idle', 'jump', 'run' ],
'adult_hairs_pony tail_ponytail_black': [ 'idle', 'walk' ],
'adult_hairs_pony tail_ponytail_blonde': [ 'idle', 'walk' ],
'adult_hairs_pony tail_dark_brown': [ 'idle' ],
'adult_hairs_pony tail_dark_light': [ 'idle' ],
'adult_hairs_pony tail_ponytail_purple': [ 'idle', 'walk' ],
'adult_hairs_pony tail_ponytail_red': [ 'idle', 'walk' ],
adult_hairs_short_blonde: [ 'idle', 'jump', 'run' ],
adult_hairs_short_brown_dark: [ 'idle', 'jump', 'run' ],
adult_hairs_small_dark_brown: [ 'idle' ],
adult_hairs_spikey_dark_brown: [ 'idle' ],
adult_hairs_spikey_dark_light: [ 'idle' ],
adult_hairs_balding_gray: [ 'jump', 'run', 'walk' ],
adult_hairs_ponytail_black: [ 'jump', 'run' ],
adult_hairs_ponytail_blonde: [ 'jump', 'run' ],
adult_hairs_ponytail_brown_dark: [ 'jump' ],
adult_hairs_ponytail_brown_light: [ 'jump' ],
adult_hairs_ponytail_purple: [ 'jump', 'run' ],
adult_hairs_ponytail_red: [ 'jump', 'run' ],
adult_hairs_small_brown_dark: [ 'jump', 'run', 'walk' ],
adult_hairs_spikey_brown_dark: [ 'jump', 'run', 'walk' ],
adult_hairs_spikey_brown_light: [ 'jump', 'run', 'walk' ],
adult_hairs_bigbun_dark_brown: [ 'run' ],
adult_hairs_ponytail_bown_dark: [ 'run' ],
adult_hairs_ponytail_bown_light: [ 'run' ],
adult_hairs_long_purples: [ 'walk' ],
'adult_hairs_pony tail_brown_dark': [ 'walk' ],
'adult_hairs_pony tail_brown_light': [ 'walk' ],
adult_hairs_short_brown_blonde: [ 'walk' ],
adult_hairs_short_dark_brown: [ 'walk' ]
}
Images of character child, that aren't present in all 3 states:
{
child_hairs_long_brown_light: [ 'idle', 'walk' ],
child_hairs_short_brown_light: [ 'idle', 'running' ],
child_hairs_spikey_blonde: [ 'idle', 'running' ],
child_hairs_spikey_brown_light: [ 'idle', 'running' ],
child_hairs_long_borwn_light: [ 'running' ],
child_hairs_short_brown_high: [ 'walk' ],
child_hairs_spikey_brown_ligth: [ 'walk' ],
child_hairs_spikey_light: [ 'walk' ]
}
analyze.mjs
#!/usr/bin/env node
import glob from "glob";
// Node.js script for detecting inconsistent character images in small-burg-towns-pack Smallburg base pack
// You can find the asset pack by almostApixel at https://almostapixel.itch.io/small-burg-towns-pack
// https://gist.github.com/scmx/794ed63d026d2cecbc2e18ee88514c96
const characterImages = glob.sync("./assets/character/**/*.png");
const map = new Map();
for (const path of characterImages) {
// categorize
const [, , , age, category, state, subCategoryOrName, nameMaybe] =
path.split("/");
const name = nameMaybe || subCategoryOrName;
const subCategory = nameMaybe ? subCategoryOrName : undefined;
const [, style] = name.match(/([a-z]+_[a-z]+)\.png/);
// what to group by
const key = `${age}_${category}_${
subCategory ? `${subCategory}_` : ""
}${style.replace(new RegExp(`^${subCategory}_`), "")}`;
// group them
if (map.has(key)) {
map.get(key).images[state] = path;
} else {
const images = { idle: null, jump: null, run: null, walk: null };
if (age == "child") delete images.jump;
images[state] = path;
map.set(key, { key, age, category, subCategory, style, name, images });
}
}
console.log(`All character images`)
console.log([...map.values()]);
console.log(`Images in folders that probably shouldn't have a space in them`);
console.log(
[...map.values()]
.flatMap(({ images }) => Object.values(images))
.filter((p) => p?.includes(" "))
);
console.log(`Images of character adult that aren't present in all 4 states:`);
console.log(
Object.fromEntries(
[...map.values()]
.filter(
({ key, images }) =>
key.startsWith("adult") &&
Object.values(images).filter((v) => v).length !== 4
)
.map(({ key, images }) => [
key,
Object.entries(images)
.filter(([, v]) => v)
.map(([k]) => k),
])
)
);
console.log(`Images of character child, that aren't present in all 3 states:`);
console.log(
Object.fromEntries(
[...map.values()]
.filter(
({ key, images }) =>
key.startsWith("child") &&
Object.values(images).filter((v) => v).length !== 3
)
.map(({ key, images }) => [
key,
Object.entries(images)
.filter(([, v]) => v)
.map(([k]) => k),
])
)
);