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.
@@ -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
+ };