@botharness/pixel-avatar 0.3.0 → 0.5.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 +3 -0
- package/dist/index.d.ts +126 -12
- package/dist/index.js +571 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,5 +19,8 @@ const svg = pixelAvatarSvg(recipe); // 32×32 viewBox, crisp edges, no ids or sc
|
|
|
19
19
|
- `isPixelAvatarRecipe`, `canonicalRecipe`: validate and normalise saved recipes.
|
|
20
20
|
- `AVATAR_HAIR_PARTS`, `AVATAR_RANGES`, `detailedRecipe(recipe)`: optional finer controls. A recipe may carry `bangs`, `sideHair`, `backHair`, `spacing`, `height` and `hairLength`, all six or none; `detailedRecipe` derives them from the plain `hair` so an editor can start from what is on screen. Recipes without them render exactly as before.
|
|
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
|
+
- `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
|
+
- `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
|
+
- `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.
|
|
22
25
|
- `faceCells`, `pixelSymbolCells`, `symbolArtCells`: pixels for [`@botharness/pixel-morph`](../morph).
|
|
23
26
|
- `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
|
@@ -34,44 +34,88 @@ type AvatarColor = (typeof AVATAR_COLORS)[number];
|
|
|
34
34
|
* Avatar Species: a base on the pixel bust rig. Species share motion and anchors; each sets its
|
|
35
35
|
* own ears, face details and suggested body colors. A recipe with a species is asset version 2.
|
|
36
36
|
*/
|
|
37
|
-
declare const AVATAR_SPECIES: readonly ["human", "goblin"];
|
|
37
|
+
declare const AVATAR_SPECIES: readonly ["human", "goblin", "elf", "dwarf", "orc", "flower"];
|
|
38
38
|
type AvatarSpecies = (typeof AVATAR_SPECIES)[number];
|
|
39
39
|
/** Optional per-piece hair colors (asset version 2); an absent piece uses `hairColor`. */
|
|
40
40
|
declare const AVATAR_PIECE_COLORS: readonly ["leftSideHairColor", "rightSideHairColor"];
|
|
41
41
|
type AvatarPieceColor = (typeof AVATAR_PIECE_COLORS)[number];
|
|
42
|
-
|
|
42
|
+
/** Part choices that exist only in asset version 2, added after every version 1 choice. */
|
|
43
|
+
declare const AVATAR_PARTS_V2: {
|
|
44
|
+
readonly outfit: readonly ["tee", "shirttie", "hoodie", "turtleneck", "sailor", "blazer", "overalls", "dress", "kimono", "cardigan", "maid", "jacket", "armor", "robe", "tunic", "cloak"];
|
|
45
|
+
readonly accessory: readonly ["none", "beanie", "cap", "headphones", "flower", "bow", "earring", "crown", "halo", "catears", "hairclip", "horns", "beret", "ribbon", "headband", "bunnyears", "horseears", "flowercrown", "witch", "pins", "helmet", "hood"];
|
|
46
|
+
readonly head: readonly ["round", "oval", "square", "long", "heart", "vchin", "chubby", "diamond"];
|
|
47
|
+
readonly pose: readonly ["front", "left", "right"];
|
|
48
|
+
readonly hair: readonly ["crop", "sweep", "spiky", "buzz", "curly", "mohawk", "bob", "long", "bun", "pigtails", "afro", "twintails", "drills", "ponytail", "sidetail", "hime", "odango", "messy", "wavy", "braids", "wolf", "ahoge", "none"];
|
|
49
|
+
readonly eyes: readonly ["round", "dot", "sparkle", "lashes", "sleepy", "happy", "wink", "sharp"];
|
|
50
|
+
readonly brows: readonly ["soft", "thick", "raised", "angry", "worried", "none"];
|
|
51
|
+
readonly nose: readonly ["button", "dot", "line", "none"];
|
|
52
|
+
readonly mouth: readonly ["smile", "grin", "open", "flat", "smirk", "cat", "tongue", "o"];
|
|
53
|
+
readonly cheeks: readonly ["blush", "freckles", "none"];
|
|
54
|
+
readonly glasses: readonly ["none", "round", "square", "shades", "monocle"];
|
|
55
|
+
readonly backdrop: readonly ["sparkles", "hearts", "stars", "dots", "none"];
|
|
56
|
+
};
|
|
57
|
+
/** Optional asset version 2 parts; an absent part is not drawn (or uses the first choice). */
|
|
58
|
+
declare const AVATAR_EXTRA_PARTS: {
|
|
59
|
+
readonly beard: readonly ["short", "full", "braided"];
|
|
60
|
+
readonly petals: readonly ["trumpet", "daisy", "sunflower", "tulip", "sakura"];
|
|
61
|
+
readonly flowerBase: readonly ["leaves", "pot"];
|
|
62
|
+
};
|
|
63
|
+
type AvatarExtraPart = keyof typeof AVATAR_EXTRA_PARTS;
|
|
64
|
+
type Meta = {
|
|
43
65
|
schemaVersion: 1;
|
|
44
66
|
family: 'illustrated';
|
|
45
67
|
rigVersion: 1;
|
|
46
|
-
} &
|
|
68
|
+
} & Record<AvatarColor, string>;
|
|
47
69
|
/** Asset version 1: optional split hair and geometry, all six or none. */
|
|
48
|
-
type PixelAvatarRecipeV1 =
|
|
70
|
+
type PixelAvatarRecipeV1 = Meta & {
|
|
49
71
|
assetVersion: 1;
|
|
50
|
-
} & { [P in AvatarHairPart]?: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]?: number } & {
|
|
72
|
+
} & { [P in AvatarPart]: (typeof AVATAR_PARTS)[P][number] } & { [P in AvatarHairPart]?: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]?: number } & {
|
|
51
73
|
species?: never;
|
|
52
74
|
rightSideHair?: never;
|
|
53
|
-
} & { [P in AvatarPieceColor]?: never }
|
|
75
|
+
} & { [P in AvatarPieceColor | AvatarExtraPart]?: never } & {
|
|
76
|
+
headpiece?: never;
|
|
77
|
+
};
|
|
54
78
|
/**
|
|
55
79
|
* Asset version 2: a species, the full split hair and geometry, a separate right side hair
|
|
56
80
|
* (`sideHair` is then the left side) and optional per-side hair colors.
|
|
57
81
|
*/
|
|
58
|
-
type PixelAvatarRecipeV2 =
|
|
82
|
+
type PixelAvatarRecipeV2 = Meta & {
|
|
59
83
|
assetVersion: 2;
|
|
60
|
-
} & { [P in AvatarHairPart]: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]: number } & {
|
|
84
|
+
} & { [P in AvatarPart]: (typeof AVATAR_PARTS_V2)[P][number] } & { [P in AvatarHairPart]: (typeof AVATAR_PARTS)['hair'][number] } & { [P in AvatarRange]: number } & {
|
|
61
85
|
species: AvatarSpecies;
|
|
62
86
|
rightSideHair: (typeof AVATAR_HAIR_PARTS)['sideHair'][number];
|
|
63
|
-
} & { [P in AvatarPieceColor]?: string }
|
|
64
|
-
|
|
87
|
+
} & { [P in AvatarPieceColor]?: string } & { [P in AvatarExtraPart]?: (typeof AVATAR_EXTRA_PARTS)[P][number] } & {
|
|
88
|
+
headpiece?: never;
|
|
89
|
+
};
|
|
90
|
+
/** Asset version 3: version 2 with an embedded Custom Part in the headpiece slot. */
|
|
91
|
+
type PixelAvatarRecipeV3 = Omit<PixelAvatarRecipeV2, 'assetVersion' | 'headpiece'> & {
|
|
92
|
+
assetVersion: 3;
|
|
93
|
+
headpiece: PixelCustomPart;
|
|
94
|
+
};
|
|
95
|
+
type PixelAvatarRecipe = PixelAvatarRecipeV1 | PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
|
|
65
96
|
declare const DEFAULT_RECIPE: PixelAvatarRecipeV1;
|
|
66
97
|
declare function isPixelAvatarRecipe(value: unknown): value is PixelAvatarRecipe;
|
|
67
98
|
declare function canonicalRecipe(recipe: PixelAvatarRecipe): PixelAvatarRecipe;
|
|
99
|
+
/**
|
|
100
|
+
* Saved choices the recipe keeps but does not draw: a flower shows petals and a stem instead of
|
|
101
|
+
* hair, outfit, beard and headwear, and a helmet or hood covers the hair. The choices return when
|
|
102
|
+
* the species or headwear changes back.
|
|
103
|
+
*/
|
|
104
|
+
declare function hiddenChoices(recipe: PixelAvatarRecipe): readonly string[];
|
|
68
105
|
/**
|
|
69
106
|
* Returns the recipe as asset version 2 with the given species, keeping every other choice.
|
|
70
107
|
* Side hair splits into left (`sideHair`) and right (`rightSideHair`) pieces that start equal.
|
|
71
108
|
* When the skin color is one of the previous species' suggested colors, it moves to the new
|
|
72
109
|
* species' first suggestion; a custom color is kept.
|
|
73
110
|
*/
|
|
74
|
-
declare function withSpecies(recipe:
|
|
111
|
+
declare function withSpecies(recipe: PixelAvatarRecipeV3, species: AvatarSpecies): PixelAvatarRecipeV3;
|
|
112
|
+
declare function withSpecies(recipe: PixelAvatarRecipeV1 | PixelAvatarRecipeV2, species: AvatarSpecies): PixelAvatarRecipeV2;
|
|
113
|
+
declare function withSpecies(recipe: PixelAvatarRecipe, species: AvatarSpecies): PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
|
|
114
|
+
/**
|
|
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.
|
|
117
|
+
*/
|
|
118
|
+
declare function withHeadpiece(recipe: PixelAvatarRecipe, part: PixelCustomPart | undefined): PixelAvatarRecipeV2 | PixelAvatarRecipeV3;
|
|
75
119
|
declare function detailedRecipe(recipe: PixelAvatarRecipe): PixelAvatarRecipe;
|
|
76
120
|
declare const AVATAR_SWATCHES: Record<AvatarColor, readonly string[]>;
|
|
77
121
|
/**
|
|
@@ -79,12 +123,80 @@ declare const AVATAR_SWATCHES: Record<AvatarColor, readonly string[]>;
|
|
|
79
123
|
* name; the factory takes the name only, so it is safe to pass straight to `Array.map`.
|
|
80
124
|
*/
|
|
81
125
|
declare function createSeededRecipe(namespace: string): (seed: string) => PixelAvatarRecipe;
|
|
126
|
+
/**
|
|
127
|
+
* A version 2 name-seeded recipe factory over every species. Species are equally likely; a
|
|
128
|
+
* person may get a beard or a version 2 outfit, and a flower gets petals and a base. Version 1
|
|
129
|
+
* factories (`createSeededRecipe`) keep their faces, so a consumer chooses per identity which
|
|
130
|
+
* seed version it was created with.
|
|
131
|
+
*/
|
|
132
|
+
declare function createSeededRecipeV2(namespace: string): (seed: string) => PixelAvatarRecipeV2;
|
|
133
|
+
/** Version 2 counterpart of `seededRecipe` (BotHarness's namespace). */
|
|
134
|
+
declare const seededRecipeV2: (seed: string) => PixelAvatarRecipeV2;
|
|
82
135
|
/** The same name always gives the same face (BotHarness's namespace). */
|
|
83
136
|
declare const seededRecipe: (seed: string) => PixelAvatarRecipe;
|
|
84
137
|
/** Suggested body colors per species; any color remains allowed. */
|
|
85
138
|
declare const AVATAR_SPECIES_SWATCHES: Record<AvatarSpecies, readonly string[]>;
|
|
86
139
|
declare const AVATAR_PRESETS: readonly PixelAvatarRecipe[];
|
|
87
140
|
//#endregion
|
|
141
|
+
//#region src/part.d.ts
|
|
142
|
+
/**
|
|
143
|
+
* 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.
|
|
146
|
+
*/
|
|
147
|
+
declare const PART_SLOTS: {
|
|
148
|
+
readonly headpiece: {
|
|
149
|
+
readonly width: 32;
|
|
150
|
+
readonly height: 16;
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
type PartSlot = keyof typeof PART_SLOTS;
|
|
154
|
+
/** Tone steps on the rig's shade ramp: two darker, the color itself, two lighter. */
|
|
155
|
+
declare const PART_TONES: readonly [-2, -1, 0, 1, 2];
|
|
156
|
+
type PartTone = (typeof PART_TONES)[number];
|
|
157
|
+
/** An appearance color slot (it follows the Avatar's color) or a fixed `#rrggbb` color. */
|
|
158
|
+
type PartColor = AvatarColor | `#${string}`;
|
|
159
|
+
/** One painted cell: `[x, y, color, tone]`. */
|
|
160
|
+
type PartCell = readonly [x: number, y: number, color: PartColor, tone: PartTone];
|
|
161
|
+
declare const PART_LAYERS: readonly ["front", "back"];
|
|
162
|
+
type PartLayerName = (typeof PART_LAYERS)[number];
|
|
163
|
+
/** Most fixed colors one part may use. */
|
|
164
|
+
declare const MAX_PART_FIXED_COLORS = 16;
|
|
165
|
+
/**
|
|
166
|
+
* A Human-drawn Custom Part. Its content is immutable and its identity is `customPartId`;
|
|
167
|
+
* name, author, origin and parent are metadata kept outside it.
|
|
168
|
+
*/
|
|
169
|
+
interface PixelCustomPart {
|
|
170
|
+
slot: PartSlot;
|
|
171
|
+
front: readonly PartCell[];
|
|
172
|
+
back: readonly PartCell[];
|
|
173
|
+
}
|
|
174
|
+
/** A dense layer for drawing: `layer[y][x]` is a color and tone, or `null` when empty. */
|
|
175
|
+
type PartInk = {
|
|
176
|
+
color: PartColor;
|
|
177
|
+
tone: PartTone;
|
|
178
|
+
};
|
|
179
|
+
type PartLayer = (PartInk | null)[][];
|
|
180
|
+
declare function isPixelCustomPart(value: unknown): value is PixelCustomPart;
|
|
181
|
+
/** The part with cells in row order and fixed colors in lowercase. */
|
|
182
|
+
declare function canonicalCustomPart(part: PixelCustomPart): PixelCustomPart;
|
|
183
|
+
/** Content identity: a SHA-256 of the canonical slot, cells and color references. */
|
|
184
|
+
declare function customPartId(part: PixelCustomPart): string;
|
|
185
|
+
declare function emptyPartLayer(slot: PartSlot): PartLayer;
|
|
186
|
+
declare function partLayer(slot: PartSlot, cells: readonly PartCell[]): PartLayer;
|
|
187
|
+
declare function partCells(layer: PartLayer): PartCell[];
|
|
188
|
+
/** Builds a canonical part from two drawn layers. */
|
|
189
|
+
declare function createCustomPart(slot: PartSlot, layers: Record<PartLayerName, PartLayer>): PixelCustomPart;
|
|
190
|
+
/** The column mirrored across the Avatar centerline. */
|
|
191
|
+
declare function mirrorPartX(slot: PartSlot, x: number): number;
|
|
192
|
+
/** Sets cells (pencil with ink, eraser with `null`), optionally mirrored across the centerline. */
|
|
193
|
+
declare function paintPartLayer(slot: PartSlot, layer: PartLayer, points: readonly (readonly [number, number])[], ink: PartInk | null, mirror?: boolean): PartLayer;
|
|
194
|
+
/**
|
|
195
|
+
* 4-connected flood fill from a cell: every cell reachable through cells equal to the start
|
|
196
|
+
* becomes `ink`. With `mirror`, the mirrored start is filled too.
|
|
197
|
+
*/
|
|
198
|
+
declare function fillPartLayer(slot: PartSlot, layer: PartLayer, x: number, y: number, ink: PartInk | null, mirror?: boolean): PartLayer;
|
|
199
|
+
//#endregion
|
|
88
200
|
//#region src/figure.d.ts
|
|
89
201
|
type Cell = string | undefined;
|
|
90
202
|
type Grid = Cell[][];
|
|
@@ -93,6 +205,8 @@ type PixelMouthState = 'saved' | 'closed' | 'half-open' | 'open';
|
|
|
93
205
|
interface PixelFigureOptions {
|
|
94
206
|
mouthLayers?: boolean;
|
|
95
207
|
}
|
|
208
|
+
/** The color a Custom Part cell shows at a tone step on the rig's shade ramp. */
|
|
209
|
+
declare function partToneColor(color: string, tone: PartTone): string;
|
|
96
210
|
type Recipe = PixelAvatarRecipe;
|
|
97
211
|
declare function pixelTileColor(hair: string): string;
|
|
98
212
|
declare function pixelFigure(recipe: Recipe, yawDeg: number, options?: PixelFigureOptions): {
|
|
@@ -150,5 +264,5 @@ declare function pixelSymbolCells(symbol: PixelSymbol, color: string): PixelCell
|
|
|
150
264
|
*/
|
|
151
265
|
declare function symbolArtCells(rows: readonly string[], color: string): PixelCell$1[];
|
|
152
266
|
//#endregion
|
|
153
|
-
export { AVATAR_COLORS, AVATAR_HAIR_PARTS, AVATAR_PARTS, AVATAR_PIECE_COLORS, AVATAR_PRESETS, AVATAR_RANGES, AVATAR_SPECIES, AVATAR_SPECIES_SWATCHES, AVATAR_SWATCHES, AVATAR_TURNS, type AvatarColor, type AvatarHairPart, type AvatarPart, type AvatarPieceColor, type AvatarRange, type AvatarSpecies, DEFAULT_RECIPE, PIXEL_SYMBOLS, type PixelAvatarRecipe, type PixelAvatarRecipeV1, type PixelAvatarRecipeV2, type PixelAvatarSvgOptions, type PixelCell, type PixelFigureOptions, type PixelGrid, type PixelMouthState, type PixelSymbol, canonicalRecipe, createSeededRecipe, detailedRecipe, faceCells, isPixelAvatarRecipe, pixelAvatarSvg, pixelFigure, pixelSymbolCells, pixelTileColor, seededRandom, seededRecipe, symbolArtCells, withSpecies };
|
|
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 };
|
|
154
268
|
//# sourceMappingURL=index.d.ts.map
|