Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save scmx/794ed63d026d2cecbc2e18ee88514c96 to your computer and use it in GitHub Desktop.
Save scmx/794ed63d026d2cecbc2e18ee88514c96 to your computer and use it in GitHub Desktop.
Node.js script for detecting inconsistent character images in small-burg-towns-pack Smallburg base pack

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),
      ])
  )
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment