import species from "../species.js" /** * Palette color names * @type {Record} */ export const PALETTE = { THEME_HIGHLIGHT: "theme-highlight", TRANSPARENT: "transparent", OUTLINE: "outline", BORDER: "border", FOOT: "foot", BEAK: "beak", EYE: "eye", FACE: "face", HOOD: "hood", EYEBROW: "eyebrow", NOSE: "nose", CHEEK: "cheek", SCRUFF: "scruff", COLLAR: "collar", BELLY: "belly", UNDERBELLY: "underbelly", WING: "wing", WING_EDGE: "wing-edge", HEART: "heart", HEART_BORDER: "heart-border", HEART_SHINE: "heart-shine", FEATHER_SPINE: "feather-spine", }; /** * Mapping of sprite sheet colors to palette colors * @type {Record} */ export const SPRITE_SHEET_COLOR_MAP = { "transparent": PALETTE.TRANSPARENT, "#fff000": PALETTE.THEME_HIGHLIGHT, "#ffffff": PALETTE.BORDER, "#000000": PALETTE.OUTLINE, "#010a19": PALETTE.BEAK, "#190301": PALETTE.EYE, "#af8e75": PALETTE.FOOT, "#639bff": PALETTE.FACE, "#99e550": PALETTE.HOOD, "#ff5573": PALETTE.EYEBROW, "#d95763": PALETTE.NOSE, "#ff67a9": PALETTE.CHEEK, "#c5e550": PALETTE.SCRUFF, "#ffe955": PALETTE.COLLAR, "#f8b143": PALETTE.BELLY, "#ec8637": PALETTE.UNDERBELLY, "#578ae6": PALETTE.WING, "#326ed9": PALETTE.WING_EDGE, "#c82e2e": PALETTE.HEART, "#501a1a": PALETTE.HEART_BORDER, "#ff6b6b": PALETTE.HEART_SHINE, "#373737": PALETTE.FEATHER_SPINE, }; export class BirdType { /** * @param {string} name * @param {string} description * @param {Record} colors * @param {string[]} [tags] */ constructor(name, description, colors, tags = []) { this.name = name; this.description = description; const defaultColors = { [PALETTE.TRANSPARENT]: "transparent", [PALETTE.OUTLINE]: "#000000", [PALETTE.BORDER]: "#ffffff", [PALETTE.BEAK]: "#000000", [PALETTE.EYE]: "#000000", [PALETTE.HEART]: "#c82e2e", [PALETTE.HEART_BORDER]: "#501a1a", [PALETTE.HEART_SHINE]: "#ff6b6b", [PALETTE.FEATHER_SPINE]: "#373737", [PALETTE.HOOD]: colors.face, [PALETTE.EYEBROW]: colors.face, [PALETTE.NOSE]: colors.face, [PALETTE.CHEEK]: colors.face, [PALETTE.SCRUFF]: colors.face, [PALETTE.COLLAR]: colors.face, }; /** @type {Record} */ this.colors = { ...defaultColors, ...colors, [PALETTE.THEME_HIGHLIGHT]: colors[PALETTE.THEME_HIGHLIGHT] ?? colors.hood ?? colors.face }; this.tags = tags; } } /** * Load a sprite sheet image and convert it to a 2D array of palette color names * @param {string} src URL or data URI of the sprite sheet image * @param {boolean} [templateColors] Whether to map pixel colors to palette names * @returns {Promise} */ export function loadSpriteSheetPixels(src, templateColors = true) { return new Promise((resolve, reject) => { const img = new Image(); img.src = src; img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); if (!ctx) { reject(new Error('Failed to get canvas context')); return; } ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, img.width, img.height); const pixels = imageData.data; const hexArray = []; for (let y = 0; y < img.height; y++) { const row = []; for (let x = 0; x < img.width; x++) { const index = (y * img.width + x) * 4; const r = pixels[index]; const g = pixels[index + 1]; const b = pixels[index + 2]; const a = pixels[index + 3]; if (a === 0) { row.push(PALETTE.TRANSPARENT); continue; } const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; if (!templateColors) { row.push(hex); continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { row.push(hex); continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } hexArray.push(row); } resolve(hexArray); }; img.onerror = (err) => { reject(err); }; }); } /** @type {Record} */ export const SPECIES = Object.fromEntries( Object.entries(species).map(([id, data]) => [ id, new BirdType(data.name, data.description, data.colors, data.tags ?? []), ]), );