import type { CompanionBones, Eye, Hat, Species } from './types.js' import { axolotl, blob, cactus, capybara, cat, chonk, dragon, duck, ghost, goose, mushroom, octopus, owl, penguin, rabbit, robot, snail, turtle, } from './types.js' // Each sprite is 5 lines tall, 12 wide (after {E}→1char substitution). // Multiple frames per species for idle fidget animation. // Line 0 is the hat slot — must be blank in frames 0-1; frame 2 may use it. const BODIES: Record = { [duck]: [ [ ' ', ' __ ', ' <({E} )___ ', ' ( ._> ', ' `--´ ', ], [ ' ', ' __ ', ' <({E} )___ ', ' ( ._> ', ' `--´~ ', ], [ ' ', ' __ ', ' <({E} )___ ', ' ( .__> ', ' `--´ ', ], ], [goose]: [ [ ' ', ' ({E}> ', ' || ', ' _(__)_ ', ' ^^^^ ', ], [ ' ', ' ({E}> ', ' || ', ' _(__)_ ', ' ^^^^ ', ], [ ' ', ' ({E}>> ', ' || ', ' _(__)_ ', ' ^^^^ ', ], ], [blob]: [ [ ' ', ' .----. ', ' ( {E} {E} ) ', ' ( ) ', ' `----´ ', ], [ ' ', ' .------. ', ' ( {E} {E} ) ', ' ( ) ', ' `------´ ', ], [ ' ', ' .--. ', ' ({E} {E}) ', ' ( ) ', ' `--´ ', ], ], [cat]: [ [ ' ', ' /\\_/\\ ', ' ( {E} {E}) ', ' ( ω ) ', ' (")_(") ', ], [ ' ', ' /\\_/\\ ', ' ( {E} {E}) ', ' ( ω ) ', ' (")_(")~ ', ], [ ' ', ' /\\-/\\ ', ' ( {E} {E}) ', ' ( ω ) ', ' (")_(") ', ], ], [dragon]: [ [ ' ', ' /^\\ /^\\ ', ' < {E} {E} > ', ' ( ~~ ) ', ' `-vvvv-´ ', ], [ ' ', ' /^\\ /^\\ ', ' < {E} {E} > ', ' ( ) ', ' `-vvvv-´ ', ], [ ' ~ ~ ', ' /^\\ /^\\ ', ' < {E} {E} > ', ' ( ~~ ) ', ' `-vvvv-´ ', ], ], [octopus]: [ [ ' ', ' .----. ', ' ( {E} {E} ) ', ' (______) ', ' /\\/\\/\\/\\ ', ], [ ' ', ' .----. ', ' ( {E} {E} ) ', ' (______) ', ' \\/\\/\\/\\/ ', ], [ ' o ', ' .----. ', ' ( {E} {E} ) ', ' (______) ', ' /\\/\\/\\/\\ ', ], ], [owl]: [ [ ' ', ' /\\ /\\ ', ' (({E})({E})) ', ' ( >< ) ', ' `----´ ', ], [ ' ', ' /\\ /\\ ', ' (({E})({E})) ', ' ( >< ) ', ' .----. ', ], [ ' ', ' /\\ /\\ ', ' (({E})(-)) ', ' ( >< ) ', ' `----´ ', ], ], [penguin]: [ [ ' ', ' .---. ', ' ({E}>{E}) ', ' /( )\\ ', ' `---´ ', ], [ ' ', ' .---. ', ' ({E}>{E}) ', ' |( )| ', ' `---´ ', ], [ ' .---. ', ' ({E}>{E}) ', ' /( )\\ ', ' `---´ ', ' ~ ~ ', ], ], [turtle]: [ [ ' ', ' _,--._ ', ' ( {E} {E} ) ', ' /[______]\\ ', ' `` `` ', ], [ ' ', ' _,--._ ', ' ( {E} {E} ) ', ' /[______]\\ ', ' `` `` ', ], [ ' ', ' _,--._ ', ' ( {E} {E} ) ', ' /[======]\\ ', ' `` `` ', ], ], [snail]: [ [ ' ', ' {E} .--. ', ' \\ ( @ ) ', ' \\_`--´ ', ' ~~~~~~~ ', ], [ ' ', ' {E} .--. ', ' | ( @ ) ', ' \\_`--´ ', ' ~~~~~~~ ', ], [ ' ', ' {E} .--. ', ' \\ ( @ ) ', ' \\_`--´ ', ' ~~~~~~ ', ], ], [ghost]: [ [ ' ', ' .----. ', ' / {E} {E} \\ ', ' | | ', ' ~`~``~`~ ', ], [ ' ', ' .----. ', ' / {E} {E} \\ ', ' | | ', ' `~`~~`~` ', ], [ ' ~ ~ ', ' .----. ', ' / {E} {E} \\ ', ' | | ', ' ~~`~~`~~ ', ], ], [axolotl]: [ [ ' ', '}~(______)~{', '}~({E} .. {E})~{', ' ( .--. ) ', ' (_/ \\_) ', ], [ ' ', '~}(______){~', '~}({E} .. {E}){~', ' ( .--. ) ', ' (_/ \\_) ', ], [ ' ', '}~(______)~{', '}~({E} .. {E})~{', ' ( -- ) ', ' ~_/ \\_~ ', ], ], [capybara]: [ [ ' ', ' n______n ', ' ( {E} {E} ) ', ' ( oo ) ', ' `------´ ', ], [ ' ', ' n______n ', ' ( {E} {E} ) ', ' ( Oo ) ', ' `------´ ', ], [ ' ~ ~ ', ' u______n ', ' ( {E} {E} ) ', ' ( oo ) ', ' `------´ ', ], ], [cactus]: [ [ ' ', ' n ____ n ', ' | |{E} {E}| | ', ' |_| |_| ', ' | | ', ], [ ' ', ' ____ ', ' n |{E} {E}| n ', ' |_| |_| ', ' | | ', ], [ ' n n ', ' | ____ | ', ' | |{E} {E}| | ', ' |_| |_| ', ' | | ', ], ], [robot]: [ [ ' ', ' .[||]. ', ' [ {E} {E} ] ', ' [ ==== ] ', ' `------´ ', ], [ ' ', ' .[||]. ', ' [ {E} {E} ] ', ' [ -==- ] ', ' `------´ ', ], [ ' * ', ' .[||]. ', ' [ {E} {E} ] ', ' [ ==== ] ', ' `------´ ', ], ], [rabbit]: [ [ ' ', ' (\\__/) ', ' ( {E} {E} ) ', ' =( .. )= ', ' (")__(") ', ], [ ' ', ' (|__/) ', ' ( {E} {E} ) ', ' =( .. )= ', ' (")__(") ', ], [ ' ', ' (\\__/) ', ' ( {E} {E} ) ', ' =( . . )= ', ' (")__(") ', ], ], [mushroom]: [ [ ' ', ' .-o-OO-o-. ', '(__________)', ' |{E} {E}| ', ' |____| ', ], [ ' ', ' .-O-oo-O-. ', '(__________)', ' |{E} {E}| ', ' |____| ', ], [ ' . o . ', ' .-o-OO-o-. ', '(__________)', ' |{E} {E}| ', ' |____| ', ], ], [chonk]: [ [ ' ', ' /\\ /\\ ', ' ( {E} {E} ) ', ' ( .. ) ', ' `------´ ', ], [ ' ', ' /\\ /| ', ' ( {E} {E} ) ', ' ( .. ) ', ' `------´ ', ], [ ' ', ' /\\ /\\ ', ' ( {E} {E} ) ', ' ( .. ) ', ' `------´~ ', ], ], } const HAT_LINES: Record = { none: '', crown: ' \\^^^/ ', tophat: ' [___] ', propeller: ' -+- ', halo: ' ( ) ', wizard: ' /^\\ ', beanie: ' (___) ', tinyduck: ' ,> ', } export function renderSprite(bones: CompanionBones, frame = 0): string[] { const frames = BODIES[bones.species] const body = frames[frame % frames.length]!.map(line => line.replaceAll('{E}', bones.eye), ) const lines = [...body] // Only replace with hat if line 0 is empty (some fidget frames use it for smoke etc) if (bones.hat !== 'none' && !lines[0]!.trim()) { lines[0] = HAT_LINES[bones.hat] } // Drop blank hat slot — wastes a row in the Card and ambient sprite when // there's no hat and the frame isn't using it for smoke/antenna/etc. // Only safe when ALL frames have blank line 0; otherwise heights oscillate. if (!lines[0]!.trim() && frames.every(f => !f[0]!.trim())) lines.shift() return lines } export function spriteFrameCount(species: Species): number { return BODIES[species].length } export function renderFace(bones: CompanionBones): string { const eye: Eye = bones.eye switch (bones.species) { case duck: case goose: return `(${eye}>` case blob: return `(${eye}${eye})` case cat: return `=${eye}ω${eye}=` case dragon: return `<${eye}~${eye}>` case octopus: return `~(${eye}${eye})~` case owl: return `(${eye})(${eye})` case penguin: return `(${eye}>)` case turtle: return `[${eye}_${eye}]` case snail: return `${eye}(@)` case ghost: return `/${eye}${eye}\\` case axolotl: return `}${eye}.${eye}{` case capybara: return `(${eye}oo${eye})` case cactus: return `|${eye} ${eye}|` case robot: return `[${eye}${eye}]` case rabbit: return `(${eye}..${eye})` case mushroom: return `|${eye} ${eye}|` case chonk: return `(${eye}.${eye})` } }