honeytree 1.1.6 → 1.2.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/LICENSE +21 -0
- package/README.md +43 -141
- package/bin/honeydew.js +4 -3
- package/package.json +9 -9
- package/src/animation-keyframes.js +890 -0
- package/src/animation.js +151 -0
- package/src/camera.js +54 -0
- package/src/diffactions.js +32 -0
- package/src/diffpanel.js +83 -0
- package/src/diffparser.js +44 -0
- package/src/diffwatch.js +52 -0
- package/src/pointcloud.js +193 -0
- package/src/renderer.js +49 -3
- package/src/renderer3d.js +135 -0
- package/src/scanner.js +102 -0
- package/src/sprites.js +2 -1
- package/src/viewer.js +375 -148
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation keyframes for tree growth
|
|
3
|
+
* Each keyframe defines a sprite at a specific time in the animation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parse animation template with support for bud characters
|
|
8
|
+
* Similar to sprites.js parse(), but supports special tokens:
|
|
9
|
+
* - Regular letters → "█" with palette color
|
|
10
|
+
* - "1" → "░" with palette["1"] color (light bud)
|
|
11
|
+
* - "2" → "▒" with palette["2"] color (medium bud)
|
|
12
|
+
* - "3" → "░" with palette["3"] color (bud variant)
|
|
13
|
+
*/
|
|
14
|
+
export function parseAnim(template, palette) {
|
|
15
|
+
const lines = template.trim().split("\n");
|
|
16
|
+
const width = Math.max(...lines.map((line) => line.length));
|
|
17
|
+
const rows = lines
|
|
18
|
+
.map((line) => line.padEnd(width, " "))
|
|
19
|
+
.map((line) =>
|
|
20
|
+
Array.from(line, (token) => {
|
|
21
|
+
const color = palette[token] ?? null;
|
|
22
|
+
if (!color) return [" ", null];
|
|
23
|
+
|
|
24
|
+
// Special bud tokens
|
|
25
|
+
if (token === "1") return ["░", color];
|
|
26
|
+
if (token === "2") return ["▒", color];
|
|
27
|
+
if (token === "3") return ["░", color];
|
|
28
|
+
|
|
29
|
+
// Regular token → solid block
|
|
30
|
+
return ["█", color];
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
.reverse();
|
|
34
|
+
|
|
35
|
+
return { rows, width };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Animation keyframes for each tree type
|
|
40
|
+
* Each keyframe has:
|
|
41
|
+
* - time: seconds into animation
|
|
42
|
+
* - sprite: result from parseAnim
|
|
43
|
+
* - groundEffect: optional { radius, color }
|
|
44
|
+
*/
|
|
45
|
+
export const ANIMATION_KEYFRAMES = {
|
|
46
|
+
oak: [
|
|
47
|
+
// KF1 (0.0s): Ground sparkles - 8×6, sparkles at trunk position
|
|
48
|
+
{
|
|
49
|
+
time: 0.0,
|
|
50
|
+
sprite: parseAnim(
|
|
51
|
+
`
|
|
52
|
+
........
|
|
53
|
+
........
|
|
54
|
+
........
|
|
55
|
+
........
|
|
56
|
+
**
|
|
57
|
+
**
|
|
58
|
+
`,
|
|
59
|
+
{ "*": "#a4e28d", ".": null },
|
|
60
|
+
),
|
|
61
|
+
groundEffect: { radius: 5, color: "#f5c842" },
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// KF2 (0.5s): Trunk base appears - 2 rows of trunk
|
|
65
|
+
{
|
|
66
|
+
time: 0.5,
|
|
67
|
+
sprite: parseAnim(
|
|
68
|
+
`
|
|
69
|
+
........
|
|
70
|
+
........
|
|
71
|
+
........
|
|
72
|
+
........
|
|
73
|
+
tt
|
|
74
|
+
tt
|
|
75
|
+
`,
|
|
76
|
+
{ t: "#8e6238", ".": null },
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// KF3 (0.9s): Trunk full height - fresh wood at top
|
|
81
|
+
{
|
|
82
|
+
time: 0.9,
|
|
83
|
+
sprite: parseAnim(
|
|
84
|
+
`
|
|
85
|
+
........
|
|
86
|
+
........
|
|
87
|
+
........
|
|
88
|
+
ww
|
|
89
|
+
tt
|
|
90
|
+
tt
|
|
91
|
+
`,
|
|
92
|
+
{ t: "#8e6238", w: "#b18552", ".": null },
|
|
93
|
+
),
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// KF4 (1.2s): Bare branches - brown stubs extend from trunk
|
|
97
|
+
{
|
|
98
|
+
time: 1.2,
|
|
99
|
+
sprite: parseAnim(
|
|
100
|
+
`
|
|
101
|
+
........
|
|
102
|
+
........
|
|
103
|
+
b b
|
|
104
|
+
btttb
|
|
105
|
+
tt
|
|
106
|
+
tt
|
|
107
|
+
`,
|
|
108
|
+
{ t: "#8e6238", b: "#8e6238", ".": null },
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// KF5 (1.5s): Wide branches - stubs reach further
|
|
113
|
+
{
|
|
114
|
+
time: 1.5,
|
|
115
|
+
sprite: parseAnim(
|
|
116
|
+
`
|
|
117
|
+
........
|
|
118
|
+
........
|
|
119
|
+
b t b
|
|
120
|
+
btttb
|
|
121
|
+
tt
|
|
122
|
+
tt
|
|
123
|
+
`,
|
|
124
|
+
{ t: "#8e6238", b: "#8e6238", ".": null },
|
|
125
|
+
),
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// KF6 (2.0s): First buds - light green ░ around branches
|
|
129
|
+
{
|
|
130
|
+
time: 2.0,
|
|
131
|
+
sprite: parseAnim(
|
|
132
|
+
`
|
|
133
|
+
........
|
|
134
|
+
1 1
|
|
135
|
+
11ttt11
|
|
136
|
+
1ttt1
|
|
137
|
+
tt
|
|
138
|
+
tt
|
|
139
|
+
`,
|
|
140
|
+
{ t: "#8e6238", "1": "#a4e28d", ".": null },
|
|
141
|
+
),
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// KF7 (2.5s): Buds thicken - ▒ characters, denser
|
|
145
|
+
{
|
|
146
|
+
time: 2.5,
|
|
147
|
+
sprite: parseAnim(
|
|
148
|
+
`
|
|
149
|
+
22
|
|
150
|
+
2GGGGG2
|
|
151
|
+
22GGGG22
|
|
152
|
+
2GGGG2
|
|
153
|
+
tt
|
|
154
|
+
tt
|
|
155
|
+
`,
|
|
156
|
+
{ t: "#8e6238", G: "#7cc96a", "2": "#a4e28d" },
|
|
157
|
+
),
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// KF8 (3.2s): Canopy solid, pale - yellow-green palette
|
|
161
|
+
{
|
|
162
|
+
time: 3.2,
|
|
163
|
+
sprite: parseAnim(
|
|
164
|
+
`
|
|
165
|
+
ll
|
|
166
|
+
lLLLL
|
|
167
|
+
llLLLLll
|
|
168
|
+
lLLLLl
|
|
169
|
+
tt
|
|
170
|
+
tt
|
|
171
|
+
`,
|
|
172
|
+
{ t: "#8e6238", l: "#a4e28d", L: "#7cc96a" },
|
|
173
|
+
),
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// KF9 (4.0s): Canopy ripened - MUST MATCH getSprite("oak", 1.0) EXACTLY
|
|
177
|
+
{
|
|
178
|
+
time: 4.0,
|
|
179
|
+
sprite: parseAnim(
|
|
180
|
+
`
|
|
181
|
+
gg
|
|
182
|
+
gGGGG
|
|
183
|
+
ggGGGGgg
|
|
184
|
+
gGGGGg
|
|
185
|
+
tt
|
|
186
|
+
tt
|
|
187
|
+
`,
|
|
188
|
+
{ g: "#5b9a4a", G: "#3f7132", t: "#8e6238" },
|
|
189
|
+
),
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// KF10 (4.5s): Glow + sway - brightened colors, shifted 1px left
|
|
193
|
+
{
|
|
194
|
+
time: 4.5,
|
|
195
|
+
sprite: parseAnim(
|
|
196
|
+
`
|
|
197
|
+
bg
|
|
198
|
+
bgGGGG
|
|
199
|
+
bgGGGGgg
|
|
200
|
+
bgGGGGg
|
|
201
|
+
tt
|
|
202
|
+
tt
|
|
203
|
+
`,
|
|
204
|
+
{ b: "#a4e28d", g: "#5b9a4a", G: "#3f7132", t: "#8e6238" },
|
|
205
|
+
),
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
|
|
209
|
+
pine: [
|
|
210
|
+
// KF1 (0.0s): Ground sparkles - 8×8, sparkles at trunk position
|
|
211
|
+
{
|
|
212
|
+
time: 0.0,
|
|
213
|
+
sprite: parseAnim(
|
|
214
|
+
`
|
|
215
|
+
........
|
|
216
|
+
........
|
|
217
|
+
........
|
|
218
|
+
........
|
|
219
|
+
........
|
|
220
|
+
........
|
|
221
|
+
**
|
|
222
|
+
**
|
|
223
|
+
`,
|
|
224
|
+
{ "*": "#4a7a3a", ".": null },
|
|
225
|
+
),
|
|
226
|
+
groundEffect: { radius: 5, color: "#f5c842" },
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// KF2 (0.5s): Trunk base appears - 2 rows of trunk
|
|
230
|
+
{
|
|
231
|
+
time: 0.5,
|
|
232
|
+
sprite: parseAnim(
|
|
233
|
+
`
|
|
234
|
+
........
|
|
235
|
+
........
|
|
236
|
+
........
|
|
237
|
+
........
|
|
238
|
+
........
|
|
239
|
+
........
|
|
240
|
+
tt
|
|
241
|
+
tt
|
|
242
|
+
`,
|
|
243
|
+
{ t: "#6f4c2f", ".": null },
|
|
244
|
+
),
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// KF3 (1.0s): Trunk full height - dark brown
|
|
248
|
+
{
|
|
249
|
+
time: 1.0,
|
|
250
|
+
sprite: parseAnim(
|
|
251
|
+
`
|
|
252
|
+
........
|
|
253
|
+
........
|
|
254
|
+
........
|
|
255
|
+
........
|
|
256
|
+
........
|
|
257
|
+
........
|
|
258
|
+
tt
|
|
259
|
+
tt
|
|
260
|
+
`,
|
|
261
|
+
{ t: "#6f4c2f", ".": null },
|
|
262
|
+
),
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
// KF4 (1.5s): Bottom branches sprout - needles start at base
|
|
266
|
+
{
|
|
267
|
+
time: 1.5,
|
|
268
|
+
sprite: parseAnim(
|
|
269
|
+
`
|
|
270
|
+
........
|
|
271
|
+
........
|
|
272
|
+
........
|
|
273
|
+
........
|
|
274
|
+
1111
|
|
275
|
+
1tttt1
|
|
276
|
+
tt
|
|
277
|
+
tt
|
|
278
|
+
`,
|
|
279
|
+
{ t: "#6f4c2f", "1": "#3a5a36", ".": null },
|
|
280
|
+
),
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// KF5 (2.0s): Middle branches extend
|
|
284
|
+
{
|
|
285
|
+
time: 2.0,
|
|
286
|
+
sprite: parseAnim(
|
|
287
|
+
`
|
|
288
|
+
........
|
|
289
|
+
........
|
|
290
|
+
........
|
|
291
|
+
222
|
|
292
|
+
22222
|
|
293
|
+
2GGGG2
|
|
294
|
+
2tttt2
|
|
295
|
+
tt
|
|
296
|
+
`,
|
|
297
|
+
{ t: "#6f4c2f", G: "#3f7132", "2": "#3a5a36", ".": null },
|
|
298
|
+
),
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// KF6 (2.5s): Upper branches appear - conical shape forming
|
|
302
|
+
{
|
|
303
|
+
time: 2.5,
|
|
304
|
+
sprite: parseAnim(
|
|
305
|
+
`
|
|
306
|
+
........
|
|
307
|
+
33
|
|
308
|
+
3333
|
|
309
|
+
33GG33
|
|
310
|
+
3GGGGG3
|
|
311
|
+
3GGGG3
|
|
312
|
+
gttttg
|
|
313
|
+
tt
|
|
314
|
+
`,
|
|
315
|
+
{ t: "#6f4c2f", g: "#2d5b29", G: "#3f7132", "3": "#4a7a3a", ".": null },
|
|
316
|
+
),
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// KF7 (3.0s): Top tuft grows
|
|
320
|
+
{
|
|
321
|
+
time: 3.0,
|
|
322
|
+
sprite: parseAnim(
|
|
323
|
+
`
|
|
324
|
+
3
|
|
325
|
+
333
|
|
326
|
+
gGGGg
|
|
327
|
+
gGGGGGg
|
|
328
|
+
gGGGGGg
|
|
329
|
+
gGGGGg
|
|
330
|
+
gttttg
|
|
331
|
+
tt
|
|
332
|
+
`,
|
|
333
|
+
{ t: "#6f4c2f", g: "#2d5b29", G: "#3f7132", "3": "#4a7a3a", ".": null },
|
|
334
|
+
),
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
// KF8 (3.5s): Needles fill in - brighter green
|
|
338
|
+
{
|
|
339
|
+
time: 3.5,
|
|
340
|
+
sprite: parseAnim(
|
|
341
|
+
`
|
|
342
|
+
g
|
|
343
|
+
ggg
|
|
344
|
+
gGGGg
|
|
345
|
+
gGGGGGg
|
|
346
|
+
gGGGGGG
|
|
347
|
+
gGGGGg
|
|
348
|
+
gttttg
|
|
349
|
+
tt
|
|
350
|
+
`,
|
|
351
|
+
{ t: "#6f4c2f", g: "#2d5b29", G: "#3f7132", ".": null },
|
|
352
|
+
),
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
// KF9 (4.0s): Full pine - MUST MATCH getSprite("pine", 1.0) EXACTLY
|
|
356
|
+
{
|
|
357
|
+
time: 4.0,
|
|
358
|
+
sprite: parseAnim(
|
|
359
|
+
`
|
|
360
|
+
g
|
|
361
|
+
ggg
|
|
362
|
+
gGGGg
|
|
363
|
+
gGGGGGg
|
|
364
|
+
ggGGGGGG
|
|
365
|
+
gGGGGG
|
|
366
|
+
t
|
|
367
|
+
t
|
|
368
|
+
`,
|
|
369
|
+
{ g: "#2d5b29", G: "#3f7132", t: "#6f4c2f" },
|
|
370
|
+
),
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
// KF10 (4.5s): Glow + slight sway
|
|
374
|
+
{
|
|
375
|
+
time: 4.5,
|
|
376
|
+
sprite: parseAnim(
|
|
377
|
+
`
|
|
378
|
+
g....
|
|
379
|
+
ggg...
|
|
380
|
+
gGGGg..
|
|
381
|
+
gGGGGGg.
|
|
382
|
+
gGGGGGGg
|
|
383
|
+
gGGGGg.
|
|
384
|
+
t....
|
|
385
|
+
t....
|
|
386
|
+
`,
|
|
387
|
+
{ g: "#2d5b29", G: "#3f7132", t: "#6f4c2f", ".": null },
|
|
388
|
+
),
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
|
|
392
|
+
birch: [
|
|
393
|
+
// KF1 (0.0s): Ground sparkles - 7×6, sparkles at trunk position
|
|
394
|
+
{
|
|
395
|
+
time: 0.0,
|
|
396
|
+
sprite: parseAnim(
|
|
397
|
+
`
|
|
398
|
+
.......
|
|
399
|
+
.......
|
|
400
|
+
.......
|
|
401
|
+
.......
|
|
402
|
+
**
|
|
403
|
+
**
|
|
404
|
+
`,
|
|
405
|
+
{ "*": "#c4e2ba", ".": null },
|
|
406
|
+
),
|
|
407
|
+
groundEffect: { radius: 5, color: "#f5c842" },
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
// KF2 (0.5s): Trunk base appears - 2 rows, pale white
|
|
411
|
+
{
|
|
412
|
+
time: 0.5,
|
|
413
|
+
sprite: parseAnim(
|
|
414
|
+
`
|
|
415
|
+
.......
|
|
416
|
+
.......
|
|
417
|
+
.......
|
|
418
|
+
.......
|
|
419
|
+
bb
|
|
420
|
+
bb
|
|
421
|
+
`,
|
|
422
|
+
{ b: "#d9d6d2", ".": null },
|
|
423
|
+
),
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
// KF3 (1.0s): Trunk full height - birch white
|
|
427
|
+
{
|
|
428
|
+
time: 1.0,
|
|
429
|
+
sprite: parseAnim(
|
|
430
|
+
`
|
|
431
|
+
.......
|
|
432
|
+
.......
|
|
433
|
+
.......
|
|
434
|
+
ww
|
|
435
|
+
bb
|
|
436
|
+
bb
|
|
437
|
+
`,
|
|
438
|
+
{ b: "#d9d6d2", w: "#e9e6e2", ".": null },
|
|
439
|
+
),
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
// KF4 (1.5s): Asymmetric branches - thin sparse stubs
|
|
443
|
+
{
|
|
444
|
+
time: 1.5,
|
|
445
|
+
sprite: parseAnim(
|
|
446
|
+
`
|
|
447
|
+
.......
|
|
448
|
+
.......
|
|
449
|
+
b b
|
|
450
|
+
bbbbb
|
|
451
|
+
bb
|
|
452
|
+
bb
|
|
453
|
+
`,
|
|
454
|
+
{ b: "#d9d6d2", ".": null },
|
|
455
|
+
),
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
// KF5 (2.0s): Buds appear - sparse, airy, light green
|
|
459
|
+
{
|
|
460
|
+
time: 2.0,
|
|
461
|
+
sprite: parseAnim(
|
|
462
|
+
`
|
|
463
|
+
.......
|
|
464
|
+
1 1
|
|
465
|
+
11bbb1
|
|
466
|
+
1bbbbb1
|
|
467
|
+
bb
|
|
468
|
+
bb
|
|
469
|
+
`,
|
|
470
|
+
{ b: "#d9d6d2", "1": "#c4e2ba", ".": null },
|
|
471
|
+
),
|
|
472
|
+
},
|
|
473
|
+
|
|
474
|
+
// KF6 (2.5s): Buds thicken - light green filling in
|
|
475
|
+
{
|
|
476
|
+
time: 2.5,
|
|
477
|
+
sprite: parseAnim(
|
|
478
|
+
`
|
|
479
|
+
22
|
|
480
|
+
2ggg2
|
|
481
|
+
2gghhhg
|
|
482
|
+
2ggg2
|
|
483
|
+
bb
|
|
484
|
+
bb
|
|
485
|
+
`,
|
|
486
|
+
{ b: "#d9d6d2", g: "#7cc96a", h: "#a4e28d", "2": "#c4e2ba" },
|
|
487
|
+
),
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
// KF7 (3.0s): Canopy brightens - bright accents appear
|
|
491
|
+
{
|
|
492
|
+
time: 3.0,
|
|
493
|
+
sprite: parseAnim(
|
|
494
|
+
`
|
|
495
|
+
hh...
|
|
496
|
+
hggg..
|
|
497
|
+
ggghhh.
|
|
498
|
+
hgggh.
|
|
499
|
+
bb..
|
|
500
|
+
bb..
|
|
501
|
+
`,
|
|
502
|
+
{ b: "#d9d6d2", g: "#7cc96a", h: "#a4e28d", ".": null },
|
|
503
|
+
),
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
// KF8 (3.5s): Nearly mature - colors deepen slightly
|
|
507
|
+
{
|
|
508
|
+
time: 3.5,
|
|
509
|
+
sprite: parseAnim(
|
|
510
|
+
`
|
|
511
|
+
hh
|
|
512
|
+
hgggh
|
|
513
|
+
ggghhgg
|
|
514
|
+
hgggh
|
|
515
|
+
bb
|
|
516
|
+
bb
|
|
517
|
+
`,
|
|
518
|
+
{ b: "#d9d6d2", g: "#7cc96a", h: "#a4e28d" },
|
|
519
|
+
),
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
// KF9 (4.0s): Full birch - MUST MATCH getSprite("birch", 1.0) EXACTLY
|
|
523
|
+
{
|
|
524
|
+
time: 4.0,
|
|
525
|
+
sprite: parseAnim(
|
|
526
|
+
`
|
|
527
|
+
hh
|
|
528
|
+
hgggh
|
|
529
|
+
ggghhgg
|
|
530
|
+
hgggh
|
|
531
|
+
bb
|
|
532
|
+
bb
|
|
533
|
+
`,
|
|
534
|
+
{ g: "#7cc96a", h: "#a4e28d", b: "#d9d6d2" },
|
|
535
|
+
),
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
// KF10 (4.5s): Glow + sway
|
|
539
|
+
{
|
|
540
|
+
time: 4.5,
|
|
541
|
+
sprite: parseAnim(
|
|
542
|
+
`
|
|
543
|
+
hh...
|
|
544
|
+
hgggh..
|
|
545
|
+
gghhgg.
|
|
546
|
+
hgggh..
|
|
547
|
+
bb...
|
|
548
|
+
bb...
|
|
549
|
+
`,
|
|
550
|
+
{ g: "#7cc96a", h: "#a4e28d", b: "#d9d6d2", ".": null },
|
|
551
|
+
),
|
|
552
|
+
},
|
|
553
|
+
],
|
|
554
|
+
|
|
555
|
+
willow: [
|
|
556
|
+
// KF1 (0.0s): Ground sparkles - 11×7, sparkles at trunk position
|
|
557
|
+
{
|
|
558
|
+
time: 0.0,
|
|
559
|
+
sprite: parseAnim(
|
|
560
|
+
`
|
|
561
|
+
...........
|
|
562
|
+
...........
|
|
563
|
+
...........
|
|
564
|
+
...........
|
|
565
|
+
...........
|
|
566
|
+
**
|
|
567
|
+
**
|
|
568
|
+
`,
|
|
569
|
+
{ "*": "#9ac98a", ".": null },
|
|
570
|
+
),
|
|
571
|
+
groundEffect: { radius: 5, color: "#f5c842" },
|
|
572
|
+
},
|
|
573
|
+
|
|
574
|
+
// KF2 (0.5s): Trunk base appears - 2 rows
|
|
575
|
+
{
|
|
576
|
+
time: 0.5,
|
|
577
|
+
sprite: parseAnim(
|
|
578
|
+
`
|
|
579
|
+
...........
|
|
580
|
+
...........
|
|
581
|
+
...........
|
|
582
|
+
...........
|
|
583
|
+
...........
|
|
584
|
+
tt
|
|
585
|
+
tt
|
|
586
|
+
`,
|
|
587
|
+
{ t: "#8e6238", ".": null },
|
|
588
|
+
),
|
|
589
|
+
},
|
|
590
|
+
|
|
591
|
+
// KF3 (0.9s): Trunk full height
|
|
592
|
+
{
|
|
593
|
+
time: 0.9,
|
|
594
|
+
sprite: parseAnim(
|
|
595
|
+
`
|
|
596
|
+
...........
|
|
597
|
+
...........
|
|
598
|
+
...........
|
|
599
|
+
...........
|
|
600
|
+
...........
|
|
601
|
+
tt
|
|
602
|
+
tt
|
|
603
|
+
`,
|
|
604
|
+
{ t: "#8e6238", ".": null },
|
|
605
|
+
),
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
// KF4 (1.3s): Crown buds FIRST - top-down growth pattern unique to willow
|
|
609
|
+
{
|
|
610
|
+
time: 1.3,
|
|
611
|
+
sprite: parseAnim(
|
|
612
|
+
`
|
|
613
|
+
...........
|
|
614
|
+
...........
|
|
615
|
+
...........
|
|
616
|
+
1111
|
|
617
|
+
11111
|
|
618
|
+
tt
|
|
619
|
+
tt
|
|
620
|
+
`,
|
|
621
|
+
{ t: "#8e6238", "1": "#b4d9a4", ".": null },
|
|
622
|
+
),
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
// KF5 (1.8s): Crown spreads wide - fronds start to droop
|
|
626
|
+
{
|
|
627
|
+
time: 1.8,
|
|
628
|
+
sprite: parseAnim(
|
|
629
|
+
`
|
|
630
|
+
...........
|
|
631
|
+
...........
|
|
632
|
+
22222
|
|
633
|
+
2222222
|
|
634
|
+
2222222
|
|
635
|
+
tt
|
|
636
|
+
tt
|
|
637
|
+
`,
|
|
638
|
+
{ t: "#8e6238", "2": "#9ac98a", ".": null },
|
|
639
|
+
),
|
|
640
|
+
},
|
|
641
|
+
|
|
642
|
+
// KF6 (2.4s): Fronds cascade down - gaps form
|
|
643
|
+
{
|
|
644
|
+
time: 2.4,
|
|
645
|
+
sprite: parseAnim(
|
|
646
|
+
`
|
|
647
|
+
...........
|
|
648
|
+
33333
|
|
649
|
+
3333333
|
|
650
|
+
33 333 33
|
|
651
|
+
33 33
|
|
652
|
+
tt
|
|
653
|
+
tt
|
|
654
|
+
`,
|
|
655
|
+
{ t: "#8e6238", "3": "#9ac98a", ".": null },
|
|
656
|
+
),
|
|
657
|
+
},
|
|
658
|
+
|
|
659
|
+
// KF7 (3.0s): Lower fronds extend - willow curtain forms
|
|
660
|
+
{
|
|
661
|
+
time: 3.0,
|
|
662
|
+
sprite: parseAnim(
|
|
663
|
+
`
|
|
664
|
+
ggggg..
|
|
665
|
+
gggggggg.
|
|
666
|
+
gg ggggg gg
|
|
667
|
+
gg ggg gg
|
|
668
|
+
gg gg
|
|
669
|
+
tt.....
|
|
670
|
+
tt.....
|
|
671
|
+
`,
|
|
672
|
+
{ t: "#8e6238", g: "#7cc96a", ".": null },
|
|
673
|
+
),
|
|
674
|
+
},
|
|
675
|
+
|
|
676
|
+
// KF8 (3.5s): Fronds fill in - pale mint green
|
|
677
|
+
{
|
|
678
|
+
time: 3.5,
|
|
679
|
+
sprite: parseAnim(
|
|
680
|
+
`
|
|
681
|
+
ggggg
|
|
682
|
+
gggggggg
|
|
683
|
+
gg ggggg gg
|
|
684
|
+
gg ggg gg
|
|
685
|
+
gg gg
|
|
686
|
+
tt
|
|
687
|
+
tt
|
|
688
|
+
`,
|
|
689
|
+
{ t: "#8e6238", g: "#7cc96a" },
|
|
690
|
+
),
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
// KF9 (4.0s): Full willow - MUST MATCH getSprite("willow", 1.0) EXACTLY
|
|
694
|
+
{
|
|
695
|
+
time: 4.0,
|
|
696
|
+
sprite: parseAnim(
|
|
697
|
+
`
|
|
698
|
+
ggggg
|
|
699
|
+
gggggggg
|
|
700
|
+
gg ggggg gg
|
|
701
|
+
gg ggg gg
|
|
702
|
+
gg gg
|
|
703
|
+
tt
|
|
704
|
+
tt
|
|
705
|
+
`,
|
|
706
|
+
{ g: "#7cc96a", t: "#8e6238" },
|
|
707
|
+
),
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
// KF10 (4.5s): Glow + gentle sway
|
|
711
|
+
{
|
|
712
|
+
time: 4.5,
|
|
713
|
+
sprite: parseAnim(
|
|
714
|
+
`
|
|
715
|
+
ggggg.....
|
|
716
|
+
gggggggg...
|
|
717
|
+
ggggg gg...
|
|
718
|
+
ggg gg...
|
|
719
|
+
gg....
|
|
720
|
+
tt.......
|
|
721
|
+
tt.......
|
|
722
|
+
`,
|
|
723
|
+
{ g: "#7cc96a", t: "#8e6238", ".": null },
|
|
724
|
+
),
|
|
725
|
+
},
|
|
726
|
+
],
|
|
727
|
+
|
|
728
|
+
cherry: [
|
|
729
|
+
// KF1 (0.0s): Ground sparkles - 8×6, sparkles at trunk position
|
|
730
|
+
{
|
|
731
|
+
time: 0.0,
|
|
732
|
+
sprite: parseAnim(
|
|
733
|
+
`
|
|
734
|
+
........
|
|
735
|
+
........
|
|
736
|
+
........
|
|
737
|
+
........
|
|
738
|
+
**
|
|
739
|
+
**
|
|
740
|
+
`,
|
|
741
|
+
{ "*": "#f0d0e0", ".": null },
|
|
742
|
+
),
|
|
743
|
+
groundEffect: { radius: 5, color: "#f5c842" },
|
|
744
|
+
},
|
|
745
|
+
|
|
746
|
+
// KF2 (0.5s): Trunk base appears - 2 rows, light trunk
|
|
747
|
+
{
|
|
748
|
+
time: 0.5,
|
|
749
|
+
sprite: parseAnim(
|
|
750
|
+
`
|
|
751
|
+
........
|
|
752
|
+
........
|
|
753
|
+
........
|
|
754
|
+
........
|
|
755
|
+
tt
|
|
756
|
+
tt
|
|
757
|
+
`,
|
|
758
|
+
{ t: "#b18552", ".": null },
|
|
759
|
+
),
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
// KF3 (0.9s): Trunk full height - elegant upward branches
|
|
763
|
+
{
|
|
764
|
+
time: 0.9,
|
|
765
|
+
sprite: parseAnim(
|
|
766
|
+
`
|
|
767
|
+
........
|
|
768
|
+
........
|
|
769
|
+
........
|
|
770
|
+
ww
|
|
771
|
+
tt
|
|
772
|
+
tt
|
|
773
|
+
`,
|
|
774
|
+
{ t: "#b18552", w: "#c9a572", ".": null },
|
|
775
|
+
),
|
|
776
|
+
},
|
|
777
|
+
|
|
778
|
+
// KF4 (1.3s): Upward branches extend
|
|
779
|
+
{
|
|
780
|
+
time: 1.3,
|
|
781
|
+
sprite: parseAnim(
|
|
782
|
+
`
|
|
783
|
+
........
|
|
784
|
+
........
|
|
785
|
+
b b
|
|
786
|
+
btttb
|
|
787
|
+
tt
|
|
788
|
+
tt
|
|
789
|
+
`,
|
|
790
|
+
{ t: "#b18552", b: "#c9a572", ".": null },
|
|
791
|
+
),
|
|
792
|
+
},
|
|
793
|
+
|
|
794
|
+
// KF5 (1.8s): White buds radiate from center
|
|
795
|
+
{
|
|
796
|
+
time: 1.8,
|
|
797
|
+
sprite: parseAnim(
|
|
798
|
+
`
|
|
799
|
+
........
|
|
800
|
+
1
|
|
801
|
+
11111
|
|
802
|
+
1ttt1
|
|
803
|
+
tt
|
|
804
|
+
tt
|
|
805
|
+
`,
|
|
806
|
+
{ t: "#b18552", "1": "#f0d0e0", ".": null },
|
|
807
|
+
),
|
|
808
|
+
},
|
|
809
|
+
|
|
810
|
+
// KF6 (2.4s): Buds thicken - white blooms spread
|
|
811
|
+
{
|
|
812
|
+
time: 2.4,
|
|
813
|
+
sprite: parseAnim(
|
|
814
|
+
`
|
|
815
|
+
222...
|
|
816
|
+
22222..
|
|
817
|
+
222P222.
|
|
818
|
+
2PPP2..
|
|
819
|
+
tt...
|
|
820
|
+
tt...
|
|
821
|
+
`,
|
|
822
|
+
{ t: "#b18552", P: "#de93b8", "2": "#f0d0e0", ".": null },
|
|
823
|
+
),
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
// KF7 (3.0s): Pink blush appears - elegant bloom pattern
|
|
827
|
+
{
|
|
828
|
+
time: 3.0,
|
|
829
|
+
sprite: parseAnim(
|
|
830
|
+
`
|
|
831
|
+
pPp
|
|
832
|
+
pPPPPp
|
|
833
|
+
pPPpPPPp
|
|
834
|
+
pPPPp
|
|
835
|
+
tt
|
|
836
|
+
tt
|
|
837
|
+
`,
|
|
838
|
+
{ t: "#b18552", p: "#f0b7cf", P: "#de93b8" },
|
|
839
|
+
),
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
// KF8 (3.5s): Blooms deepen - more pink
|
|
843
|
+
{
|
|
844
|
+
time: 3.5,
|
|
845
|
+
sprite: parseAnim(
|
|
846
|
+
`
|
|
847
|
+
pPp
|
|
848
|
+
pPPPPp
|
|
849
|
+
pPPpPPPp
|
|
850
|
+
pPPPpp
|
|
851
|
+
tt
|
|
852
|
+
tt
|
|
853
|
+
`,
|
|
854
|
+
{ t: "#b18552", p: "#f0b7cf", P: "#de93b8" },
|
|
855
|
+
),
|
|
856
|
+
},
|
|
857
|
+
|
|
858
|
+
// KF9 (4.0s): Full cherry - MUST MATCH getSprite("cherry", 1.0) EXACTLY
|
|
859
|
+
{
|
|
860
|
+
time: 4.0,
|
|
861
|
+
sprite: parseAnim(
|
|
862
|
+
`
|
|
863
|
+
pPp
|
|
864
|
+
pPPPPp
|
|
865
|
+
pPPpPPPp
|
|
866
|
+
pPPPpp
|
|
867
|
+
tt
|
|
868
|
+
tt
|
|
869
|
+
`,
|
|
870
|
+
{ p: "#f0b7cf", P: "#de93b8", t: "#b18552" },
|
|
871
|
+
),
|
|
872
|
+
},
|
|
873
|
+
|
|
874
|
+
// KF10 (4.5s): Glow + soft sway
|
|
875
|
+
{
|
|
876
|
+
time: 4.5,
|
|
877
|
+
sprite: parseAnim(
|
|
878
|
+
`
|
|
879
|
+
pPp...
|
|
880
|
+
pPPPPp..
|
|
881
|
+
PPpPPPp.
|
|
882
|
+
pPPPpp..
|
|
883
|
+
tt....
|
|
884
|
+
tt....
|
|
885
|
+
`,
|
|
886
|
+
{ p: "#f0b7cf", P: "#de93b8", t: "#b18552", ".": null },
|
|
887
|
+
),
|
|
888
|
+
},
|
|
889
|
+
],
|
|
890
|
+
};
|