@botharness/pixel-avatar 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,6 +21,7 @@ const svg = pixelAvatarSvg(recipe); // 32×32 viewBox, crisp edges, no ids or sc
21
21
  - `AVATAR_SPECIES`, `AVATAR_SPECIES_SWATCHES`, `AVATAR_PIECE_COLORS`, `withSpecies(recipe, species)`: Avatar Species (`human`, `goblin`) on the same rig, as asset version 2. `withSpecies` keeps every choice, splits side hair into `sideHair` (left) and `rightSideHair`, and accepts optional `leftSideHairColor`/`rightSideHairColor`, where left and right are as seen on screen. Consumers built before version 2 reject these recipes with `isPixelAvatarRecipe` and can show a saved snapshot instead.
22
22
  - `AVATAR_PARTS_V2`, `AVATAR_EXTRA_PARTS`, `hiddenChoices(recipe)`: version 2 adds elf, dwarf, orc and flower species, armor/robe/tunic/cloak outfits, helmet/hood headwear, an optional `beard`, and a flower's `petals` and `flowerBase`. `hiddenChoices` names saved choices the current species or headwear keeps but does not draw, so an editor can say so instead of discarding them.
23
23
  - `PixelCustomPart`, `withHeadpiece(recipe, part)`, `customPartId`, `isPixelCustomPart`: a Human-drawn Custom Part in the `headpiece` slot (the top 32×16 of the tile). Its back layer is drawn behind the hair and its front layer over it, and both follow the head through turns. Each cell is `[x, y, color, tone]`, where `color` is an appearance color slot (`hairColor`, `skinColor`, `eyeColor`, `shirtColor`), so the part recolors with the Avatar, or a fixed `#rrggbb`, and `tone` is a step from −2 to 2 on the rig's shade ramp (`partToneColor`). A recipe wearing a part embeds its own copy as asset version 3. `customPartId` is the SHA-256 of the canonical content; names, authors and origins belong to the consumer's library.
24
+ - `HAIR_PART_SLOTS`, `hairPieceStart(recipe, slot)`, `withCustomPart(recipe, slot, part)`, `wornPart`: drawn hair pieces. `bangs`, `leftSideHair`, `rightSideHair` and `backHair` each take a Custom Part (front layer only) that replaces the built-in piece while it stays saved underneath. In a hair part, a `hairColor` cell at tone 0 is live hair: it is shaded with the rest of the hair exactly like the built-in piece, so `hairPieceStart` flattens the worn built-in piece into such cells and an unchanged copy renders identically. Other cells show their own color. Drawn hair follows turns and is hidden under a helmet or hood and on a flower, like built-in hair.
24
25
  - `emptyPartLayer`, `paintPartLayer`, `fillPartLayer`, `mirrorPartX`, `partLayer`, `partCells`, `createCustomPart`: drawing on dense layers, with pencil and eraser strokes, 4-connected flood fill and mirroring across the Avatar centerline.
25
26
  - `faceCells`, `pixelSymbolCells`, `symbolArtCells`: pixels for [`@botharness/pixel-morph`](../morph).
26
27
  - `pixelAvatarSvg(recipe, { turns, classPrefix })`: optional pre-rendered head turns, and rig layer classes (`<prefix>-body`, `-head`, `-face`, `-gaze`, `-blink`) for CSS animation.
package/dist/index.d.ts CHANGED
@@ -72,9 +72,7 @@ type PixelAvatarRecipeV1 = Meta & {
72
72
  } & { [P in AvatarPart]: (typeof AVATAR_PARTS)[P][number] } & { [P in AvatarHairPart]?: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]?: number } & {
73
73
  species?: never;
74
74
  rightSideHair?: never;
75
- } & { [P in AvatarPieceColor | AvatarExtraPart]?: never } & {
76
- headpiece?: never;
77
- };
75
+ } & { [P in AvatarPieceColor | AvatarExtraPart]?: never } & { [P in CustomPartKey]?: never };
78
76
  /**
79
77
  * Asset version 2: a species, the full split hair and geometry, a separate right side hair
80
78
  * (`sideHair` is then the left side) and optional per-side hair colors.
@@ -84,14 +82,23 @@ type PixelAvatarRecipeV2 = Meta & {
84
82
  } & { [P in AvatarPart]: (typeof AVATAR_PARTS_V2)[P][number] } & { [P in AvatarHairPart]: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]: number } & {
85
83
  species: AvatarSpecies;
86
84
  rightSideHair: (typeof AVATAR_HAIR_PARTS)['sideHair'][number];
87
- } & { [P in AvatarPieceColor]?: string } & { [P in AvatarExtraPart]?: (typeof AVATAR_EXTRA_PARTS)[P][number] } & {
88
- headpiece?: never;
85
+ } & { [P in AvatarPieceColor]?: string } & { [P in AvatarExtraPart]?: (typeof AVATAR_EXTRA_PARTS)[P][number] } & { [P in CustomPartKey]?: never };
86
+ /** The recipe key that embeds the Custom Part worn in each slot. */
87
+ declare const CUSTOM_PART_KEYS: {
88
+ readonly headpiece: "headpiece";
89
+ readonly bangs: "bangsPart";
90
+ readonly leftSideHair: "leftSideHairPart";
91
+ readonly rightSideHair: "rightSideHairPart";
92
+ readonly backHair: "backHairPart";
89
93
  };
90
- /** Asset version 3: version 2 with an embedded Custom Part in the headpiece slot. */
91
- type PixelAvatarRecipeV3 = Omit<PixelAvatarRecipeV2, 'assetVersion' | 'headpiece'> & {
94
+ type CustomPartKey = (typeof CUSTOM_PART_KEYS)[PartSlot];
95
+ /**
96
+ * Asset version 3: version 2 wearing at least one embedded Custom Part. A drawn hair piece
97
+ * replaces the built-in piece, which stays saved and returns when the part is taken off.
98
+ */
99
+ type PixelAvatarRecipeV3 = Omit<PixelAvatarRecipeV2, 'assetVersion' | CustomPartKey> & {
92
100
  assetVersion: 3;
93
- headpiece: PixelCustomPart;
94
- };
101
+ } & { [P in CustomPartKey]?: PixelCustomPart };
95
102
  type PixelAvatarRecipe = PixelAvatarRecipeV1 | PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
96
103
  declare const DEFAULT_RECIPE: PixelAvatarRecipeV1;
97
104
  declare function isPixelAvatarRecipe(value: unknown): value is PixelAvatarRecipe;
@@ -112,10 +119,14 @@ declare function withSpecies(recipe: PixelAvatarRecipeV3, species: AvatarSpecies
112
119
  declare function withSpecies(recipe: PixelAvatarRecipeV1 | PixelAvatarRecipeV2, species: AvatarSpecies): PixelAvatarRecipeV2;
113
120
  declare function withSpecies(recipe: PixelAvatarRecipe, species: AvatarSpecies): PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
114
121
  /**
115
- * Returns the recipe wearing a Custom Part in the headpiece slot (asset version 3), or without
116
- * one (asset version 2). The recipe embeds its own copy of the part.
122
+ * Returns the recipe wearing `part` in `slot`, or with that slot's part taken off. The recipe
123
+ * embeds its own copy; it is asset version 3 while it wears any part and version 2 otherwise.
117
124
  */
125
+ declare function withCustomPart(recipe: PixelAvatarRecipe, slot: PartSlot, part: PixelCustomPart | undefined): PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
126
+ /** `withCustomPart` for the headpiece slot. */
118
127
  declare function withHeadpiece(recipe: PixelAvatarRecipe, part: PixelCustomPart | undefined): PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
128
+ /** The Custom Part worn in a slot, if any. */
129
+ declare function wornPart(recipe: PixelAvatarRecipe, slot: PartSlot): PixelCustomPart | undefined;
119
130
  declare function detailedRecipe(recipe: PixelAvatarRecipe): PixelAvatarRecipe;
120
131
  declare const AVATAR_SWATCHES: Record<AvatarColor, readonly string[]>;
121
132
  /**
@@ -141,16 +152,40 @@ declare const AVATAR_PRESETS: readonly PixelAvatarRecipe[];
141
152
  //#region src/part.d.ts
142
153
  /**
143
154
  * Custom Part slots. A headpiece covers the top of the tile, around the hair: its back layer is
144
- * drawn behind the hair and its front layer over it. Cells are in tile coordinates, so the
145
- * Avatar centerline sits between x = 15 and x = 16.
155
+ * drawn behind the hair and its front layer over it. Hair slots replace one built-in hair piece
156
+ * and use only the front layer. Cells are in tile coordinates, so the Avatar centerline sits
157
+ * between x = 15 and x = 16.
146
158
  */
147
159
  declare const PART_SLOTS: {
148
160
  readonly headpiece: {
149
161
  readonly width: 32;
150
162
  readonly height: 16;
151
163
  };
164
+ readonly bangs: {
165
+ readonly width: 32;
166
+ readonly height: 32;
167
+ };
168
+ readonly leftSideHair: {
169
+ readonly width: 32;
170
+ readonly height: 32;
171
+ };
172
+ readonly rightSideHair: {
173
+ readonly width: 32;
174
+ readonly height: 32;
175
+ };
176
+ readonly backHair: {
177
+ readonly width: 32;
178
+ readonly height: 32;
179
+ };
152
180
  };
153
181
  type PartSlot = keyof typeof PART_SLOTS;
182
+ /**
183
+ * Hair slots. In a hair part, a `hairColor` cell at tone 0 is live hair: it is shaded with the
184
+ * rest of the hair, exactly like a built-in piece. Every other cell shows its own color.
185
+ */
186
+ declare const HAIR_PART_SLOTS: readonly ["bangs", "leftSideHair", "rightSideHair", "backHair"];
187
+ type HairPartSlot = (typeof HAIR_PART_SLOTS)[number];
188
+ declare const isHairPartSlot: (slot: PartSlot) => slot is HairPartSlot;
154
189
  /** Tone steps on the rig's shade ramp: two darker, the color itself, two lighter. */
155
190
  declare const PART_TONES: readonly [-2, -1, 0, 1, 2];
156
191
  type PartTone = (typeof PART_TONES)[number];
@@ -215,6 +250,16 @@ declare function pixelFigure(recipe: Recipe, yawDeg: number, options?: PixelFigu
215
250
  head: string;
216
251
  cells: PixelCell$1[];
217
252
  };
253
+ /**
254
+ * The hair piece in `slot` as a Custom Part to start drawing from: the drawn part worn there,
255
+ * or the built-in piece flattened for this recipe in the front pose as live `hairColor` cells.
256
+ * A flattened piece keeps this shape and no longer follows face shape or hair length.
257
+ */
258
+ declare function hairPieceStart(recipe: PixelAvatarRecipe, slot: HairPartSlot): {
259
+ slot: HairPartSlot;
260
+ front: PartCell[];
261
+ back: PartCell[];
262
+ };
218
263
  //#endregion
219
264
  //#region src/random.d.ts
220
265
  declare function seededRandom(seed: string): () => number;
@@ -264,5 +309,5 @@ declare function pixelSymbolCells(symbol: PixelSymbol, color: string): PixelCell
264
309
  */
265
310
  declare function symbolArtCells(rows: readonly string[], color: string): PixelCell$1[];
266
311
  //#endregion
267
- export { AVATAR_COLORS, AVATAR_EXTRA_PARTS, AVATAR_HAIR_PARTS, AVATAR_PARTS, AVATAR_PARTS_V2, AVATAR_PIECE_COLORS, AVATAR_PRESETS, AVATAR_RANGES, AVATAR_SPECIES, AVATAR_SPECIES_SWATCHES, AVATAR_SWATCHES, AVATAR_TURNS, type AvatarColor, type AvatarExtraPart, type AvatarHairPart, type AvatarPart, type AvatarPieceColor, type AvatarRange, type AvatarSpecies, DEFAULT_RECIPE, MAX_PART_FIXED_COLORS, PART_LAYERS, PART_SLOTS, PART_TONES, PIXEL_SYMBOLS, type PartCell, type PartColor, type PartInk, type PartLayer, type PartLayerName, type PartSlot, type PartTone, type PixelAvatarRecipe, type PixelAvatarRecipeV1, type PixelAvatarRecipeV2, type PixelAvatarRecipeV3, type PixelAvatarSvgOptions, type PixelCell, type PixelCustomPart, type PixelFigureOptions, type PixelGrid, type PixelMouthState, type PixelSymbol, canonicalCustomPart, canonicalRecipe, createCustomPart, createSeededRecipe, createSeededRecipeV2, customPartId, detailedRecipe, emptyPartLayer, faceCells, fillPartLayer, hiddenChoices, isPixelAvatarRecipe, isPixelCustomPart, mirrorPartX, paintPartLayer, partCells, partLayer, partToneColor, pixelAvatarSvg, pixelFigure, pixelSymbolCells, pixelTileColor, seededRandom, seededRecipe, seededRecipeV2, symbolArtCells, withHeadpiece, withSpecies };
312
+ export { AVATAR_COLORS, AVATAR_EXTRA_PARTS, AVATAR_HAIR_PARTS, AVATAR_PARTS, AVATAR_PARTS_V2, AVATAR_PIECE_COLORS, AVATAR_PRESETS, AVATAR_RANGES, AVATAR_SPECIES, AVATAR_SPECIES_SWATCHES, AVATAR_SWATCHES, AVATAR_TURNS, type AvatarColor, type AvatarExtraPart, type AvatarHairPart, type AvatarPart, type AvatarPieceColor, type AvatarRange, type AvatarSpecies, CUSTOM_PART_KEYS, type CustomPartKey, DEFAULT_RECIPE, HAIR_PART_SLOTS, type HairPartSlot, MAX_PART_FIXED_COLORS, PART_LAYERS, PART_SLOTS, PART_TONES, PIXEL_SYMBOLS, type PartCell, type PartColor, type PartInk, type PartLayer, type PartLayerName, type PartSlot, type PartTone, type PixelAvatarRecipe, type PixelAvatarRecipeV1, type PixelAvatarRecipeV2, type PixelAvatarRecipeV3, type PixelAvatarSvgOptions, type PixelCell, type PixelCustomPart, type PixelFigureOptions, type PixelGrid, type PixelMouthState, type PixelSymbol, canonicalCustomPart, canonicalRecipe, createCustomPart, createSeededRecipe, createSeededRecipeV2, customPartId, detailedRecipe, emptyPartLayer, faceCells, fillPartLayer, hairPieceStart, hiddenChoices, isHairPartSlot, isPixelAvatarRecipe, isPixelCustomPart, mirrorPartX, paintPartLayer, partCells, partLayer, partToneColor, pixelAvatarSvg, pixelFigure, pixelSymbolCells, pixelTileColor, seededRandom, seededRecipe, seededRecipeV2, symbolArtCells, withCustomPart, withHeadpiece, withSpecies, wornPart };
268
313
  //# sourceMappingURL=index.d.ts.map