@oidoid/void 0.1.0-7 → 0.1.0-8

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.
@@ -1,780 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import {describe, test} from 'node:test'
3
- import type {Anim, AtlasJSON} from '../../src/graphics/atlas.ts'
4
- import type {XY} from '../../src/types/geo.ts'
5
- import {
6
- AsepriteDirection,
7
- type AsepriteFrameMap,
8
- type AsepriteTagSpan
9
- } from './aseprite.ts'
10
- import {
11
- parseAnim,
12
- parseAnimFrames,
13
- parseAtlasJSON,
14
- parseCel,
15
- parseHitboxes
16
- } from './atlas-json-parser.ts'
17
-
18
- describe('parseAtlasJSON()', () => {
19
- test('parses empty.', () => {
20
- assert.deepEqual<AtlasJSON>(
21
- parseAtlasJSON({
22
- meta: {frameTags: [], size: {w: 0, h: 0}, slices: []},
23
- frames: {}
24
- }),
25
- {anim: {}, celXY: []}
26
- )
27
- })
28
-
29
- test('parses nonempty.', () => {
30
- const frameTags = [
31
- {name: 'scenery--Cloud', from: 0, to: 0, direction: 'forward'},
32
- {name: 'palette--red', from: 1, to: 1, direction: 'forward'},
33
- {name: 'scenery--Conifer', from: 2, to: 2, direction: 'forward'},
34
- {name: 'scenery--ConiferShadow', from: 3, to: 3, direction: 'forward'},
35
- {name: 'backpacker--WalkRight', from: 0, to: 7, direction: 'pingpong'}
36
- ]
37
- const frames = {
38
- 'scenery--Cloud--0': {
39
- frame: {x: 220, y: 18, w: 18, h: 18},
40
- sourceSize: {w: 16, h: 16},
41
- duration: 1
42
- },
43
- 'palette--red--1': {
44
- frame: {x: 90, y: 54, w: 18, h: 18},
45
- sourceSize: {w: 16, h: 16},
46
- duration: 65535
47
- },
48
- 'scenery--Conifer--2': {
49
- frame: {x: 72, y: 54, w: 18, h: 18},
50
- sourceSize: {w: 16, h: 16},
51
- duration: 65535
52
- },
53
- 'scenery--ConiferShadow--3': {
54
- frame: {x: 54, y: 54, w: 18, h: 18},
55
- sourceSize: {w: 16, h: 16},
56
- duration: 65535
57
- },
58
- 'backpacker--WalkRight--0': {
59
- frame: {x: 1408, y: 28, w: 8, h: 13},
60
- sourceSize: {w: 8, h: 13},
61
- duration: 62
62
- },
63
- 'backpacker--WalkRight--1': {
64
- frame: {x: 1400, y: 28, w: 8, h: 13},
65
- sourceSize: {w: 8, h: 13},
66
- duration: 62
67
- },
68
- 'backpacker--WalkRight--2': {
69
- frame: {x: 1392, y: 28, w: 8, h: 13},
70
- sourceSize: {w: 8, h: 13},
71
- duration: 62
72
- },
73
- 'backpacker--WalkRight--3': {
74
- frame: {x: 1384, y: 28, w: 8, h: 13},
75
- sourceSize: {w: 8, h: 13},
76
- duration: 62
77
- },
78
- 'backpacker--WalkRight--4': {
79
- frame: {x: 1376, y: 28, w: 8, h: 13},
80
- sourceSize: {w: 8, h: 13},
81
- duration: 62
82
- },
83
- 'backpacker--WalkRight--5': {
84
- frame: {x: 1416, y: 28, w: 8, h: 13},
85
- sourceSize: {w: 8, h: 13},
86
- duration: 62
87
- },
88
- 'backpacker--WalkRight--6': {
89
- frame: {x: 1392, y: 28, w: 8, h: 13},
90
- sourceSize: {w: 8, h: 13},
91
- duration: 62
92
- },
93
- 'backpacker--WalkRight--7': {
94
- frame: {x: 1368, y: 28, w: 8, h: 13},
95
- sourceSize: {w: 8, h: 13},
96
- duration: 62
97
- },
98
- 'backpacker--WalkDown--8': {
99
- frame: {x: 1360, y: 28, w: 8, h: 13},
100
- sourceSize: {w: 8, h: 13},
101
- duration: 62
102
- }
103
- }
104
- const slices = [
105
- {
106
- name: 'scenery--Cloud',
107
- color: '#ff0000ff',
108
- keys: [{frame: 0, bounds: {x: 8, y: 12, w: 2, h: 3}}]
109
- },
110
- {
111
- name: 'scenery--Cloud',
112
- color: '#00ff00ff',
113
- keys: [{frame: 0, bounds: {x: 1, y: 2, w: 3, h: 4}}]
114
- },
115
- {
116
- name: 'palette--red',
117
- color: '#ff0000ff',
118
- keys: [{frame: 0, bounds: {x: 7, y: 11, w: 3, h: 4}}]
119
- },
120
- {
121
- name: 'scenery--Conifer',
122
- color: '#ff0000ff',
123
- keys: [{frame: 0, bounds: {x: 7, y: 10, w: 3, h: 5}}]
124
- },
125
- {
126
- name: 'scenery--ConiferShadow',
127
- color: '#ff0000ff',
128
- keys: [{frame: 0, bounds: {x: 7, y: 9, w: 3, h: 6}}]
129
- },
130
- {
131
- name: 'backpacker--WalkRight',
132
- color: '#ff0000ff',
133
- keys: [{frame: 0, bounds: {x: 2, y: 0, w: 4, h: 4}}]
134
- }
135
- ]
136
- assert.deepEqual<AtlasJSON>(
137
- parseAtlasJSON({meta: {frameTags, size: {w: 0, h: 0}, slices}, frames}),
138
- {
139
- anim: {
140
- 'scenery--Cloud': {
141
- cels: 1,
142
- id: 0,
143
- w: 16,
144
- h: 16,
145
- hitbox: {x: 8, y: 12, w: 2, h: 3},
146
- hurtbox: {x: 1, y: 2, w: 3, h: 4}
147
- },
148
- 'palette--red': {
149
- cels: 1,
150
- id: 1,
151
- w: 16,
152
- h: 16,
153
- hitbox: {x: 7, y: 11, w: 3, h: 4},
154
- hurtbox: undefined
155
- },
156
- 'scenery--Conifer': {
157
- cels: 1,
158
- id: 2,
159
- w: 16,
160
- h: 16,
161
- hitbox: {x: 7, y: 10, w: 3, h: 5},
162
- hurtbox: undefined
163
- },
164
- 'scenery--ConiferShadow': {
165
- cels: 1,
166
- id: 3,
167
- w: 16,
168
- h: 16,
169
- hitbox: {x: 7, y: 9, w: 3, h: 6},
170
- hurtbox: undefined
171
- },
172
- 'backpacker--WalkRight': {
173
- cels: 14,
174
- id: 4,
175
- w: 8,
176
- h: 13,
177
- hitbox: {x: 2, y: 0, w: 4, h: 4},
178
- hurtbox: undefined
179
- }
180
- },
181
- celXY: [
182
- 221, 19, 91, 55, 73, 55, 55, 55, 1408, 28, 1400, 28, 1392, 28, 1384,
183
- 28, 1376, 28, 1416, 28, 1392, 28, 1368, 28, 1392, 28, 1416, 28, 1376,
184
- 28, 1384, 28, 1392, 28, 1400, 28
185
- ]
186
- }
187
- )
188
- })
189
-
190
- test('throws Error on duplicate FrameTag.', () => {
191
- const frameTags = [
192
- {name: 'scenery--Cloud', from: 0, to: 0, direction: 'forward'},
193
- {name: 'palette--red', from: 1, to: 1, direction: 'forward'},
194
- {name: 'scenery--Cloud', from: 0, to: 0, direction: 'forward'}
195
- ]
196
- const frames = {
197
- 'scenery--Cloud--0': {
198
- frame: {x: 220, y: 18, w: 18, h: 18},
199
- sourceSize: {w: 16, h: 16},
200
- duration: 1
201
- },
202
- 'palette--red--1': {
203
- frame: {x: 90, y: 54, w: 18, h: 18},
204
- sourceSize: {w: 16, h: 16},
205
- duration: 65535
206
- }
207
- }
208
- assert.throws(
209
- () =>
210
- parseAtlasJSON({
211
- meta: {frameTags, size: {w: 0, h: 0}, slices: []},
212
- frames
213
- }),
214
- Error('atlas tag "scenery--Cloud" duplicate')
215
- )
216
- })
217
- })
218
-
219
- describe('parseAnim()', () => {
220
- test('parses FrameTag, Frame from Frame[], and Slice.', () => {
221
- const frameTag: AsepriteTagSpan = {
222
- direction: 'pingpong',
223
- name: 'cloud--s',
224
- from: 1,
225
- to: 1
226
- }
227
- const frames = {
228
- 'cloud--xs--0': {
229
- frame: {x: 202, y: 36, w: 18, h: 18},
230
- sourceSize: {w: 16, h: 16},
231
- duration: 65535
232
- },
233
- 'cloud--s--1': {
234
- frame: {x: 184, y: 36, w: 18, h: 18},
235
- sourceSize: {w: 16, h: 16},
236
- duration: 65535
237
- },
238
- 'cloud--m--2': {
239
- frame: {x: 166, y: 36, w: 18, h: 18},
240
- sourceSize: {w: 16, h: 16},
241
- duration: 65535
242
- }
243
- }
244
- const slices = [
245
- {
246
- name: 'cloud--xs',
247
- color: '#ff0000ff',
248
- keys: [{frame: 0, bounds: {x: 4, y: 12, w: 7, h: 3}}]
249
- },
250
- {
251
- name: 'cloud--s',
252
- color: '#ff0000ff',
253
- keys: [{frame: 0, bounds: {x: 4, y: 11, w: 9, h: 4}}]
254
- },
255
- {
256
- name: 'cloud--m',
257
- color: '#ff0000ff',
258
- keys: [{frame: 0, bounds: {x: 3, y: 11, w: 10, h: 4}}]
259
- }
260
- ]
261
- assert.deepEqual<Anim>(parseAnim(16, frameTag, frames, slices), {
262
- cels: 1,
263
- id: 16,
264
- w: 16,
265
- h: 16,
266
- hitbox: {x: 4, y: 11, w: 9, h: 4},
267
- hurtbox: undefined
268
- })
269
- })
270
-
271
- test('throws error when no frame is associated with tag.', () => {
272
- const frameTag: AsepriteTagSpan = {
273
- direction: 'pingpong',
274
- name: 'frog--walk',
275
- from: 0,
276
- to: 0
277
- }
278
- assert.throws(
279
- () => parseAnim(16, frameTag, {}, []),
280
- Error('no atlas frame "frog--walk--0"')
281
- )
282
- })
283
- })
284
-
285
- describe('parseAnimFrames()', () => {
286
- test('single cell', () => {
287
- for (const direction of Object.values(AsepriteDirection)) {
288
- const span: AsepriteTagSpan = {
289
- direction,
290
- name: 'stem--foo',
291
- from: 0,
292
- to: 0
293
- }
294
- const map: AsepriteFrameMap = {
295
- 'stem--foo--0': {
296
- duration: 1,
297
- frame: {x: 0, y: 0, w: 0, h: 0},
298
- sourceSize: {w: 0, h: 0}
299
- }
300
- }
301
- assertAnimFrames(span, map, [0], direction)
302
- }
303
- })
304
-
305
- test('full anim', () => {
306
- const expected: {[dir in AsepriteDirection]: number[]} = {
307
- forward: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
308
- pingpong: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
309
- pingpong_reverse: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
310
- reverse: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
311
- }
312
- for (const direction of Object.values(AsepriteDirection)) {
313
- const span: AsepriteTagSpan = {
314
- direction,
315
- name: 'stem--foo',
316
- from: 0,
317
- to: 15
318
- }
319
- const map: AsepriteFrameMap = {
320
- 'stem--foo--0': {
321
- duration: 1,
322
- frame: {x: 0, y: 0, w: 0, h: 0},
323
- sourceSize: {w: 0, h: 0}
324
- },
325
- 'stem--foo--1': {
326
- duration: 1,
327
- frame: {x: 0, y: 0, w: 0, h: 0},
328
- sourceSize: {w: 0, h: 0}
329
- },
330
- 'stem--foo--2': {
331
- duration: 1,
332
- frame: {x: 0, y: 0, w: 0, h: 0},
333
- sourceSize: {w: 0, h: 0}
334
- },
335
- 'stem--foo--3': {
336
- duration: 1,
337
- frame: {x: 0, y: 0, w: 0, h: 0},
338
- sourceSize: {w: 0, h: 0}
339
- },
340
- 'stem--foo--4': {
341
- duration: 1,
342
- frame: {x: 0, y: 0, w: 0, h: 0},
343
- sourceSize: {w: 0, h: 0}
344
- },
345
- 'stem--foo--5': {
346
- duration: 1,
347
- frame: {x: 0, y: 0, w: 0, h: 0},
348
- sourceSize: {w: 0, h: 0}
349
- },
350
- 'stem--foo--6': {
351
- duration: 1,
352
- frame: {x: 0, y: 0, w: 0, h: 0},
353
- sourceSize: {w: 0, h: 0}
354
- },
355
- 'stem--foo--7': {
356
- duration: 1,
357
- frame: {x: 0, y: 0, w: 0, h: 0},
358
- sourceSize: {w: 0, h: 0}
359
- },
360
- 'stem--foo--8': {
361
- duration: 1,
362
- frame: {x: 0, y: 0, w: 0, h: 0},
363
- sourceSize: {w: 0, h: 0}
364
- },
365
- 'stem--foo--9': {
366
- duration: 1,
367
- frame: {x: 0, y: 0, w: 0, h: 0},
368
- sourceSize: {w: 0, h: 0}
369
- },
370
- 'stem--foo--10': {
371
- duration: 1,
372
- frame: {x: 0, y: 0, w: 0, h: 0},
373
- sourceSize: {w: 0, h: 0}
374
- },
375
- 'stem--foo--11': {
376
- duration: 1,
377
- frame: {x: 0, y: 0, w: 0, h: 0},
378
- sourceSize: {w: 0, h: 0}
379
- },
380
- 'stem--foo--12': {
381
- duration: 1,
382
- frame: {x: 0, y: 0, w: 0, h: 0},
383
- sourceSize: {w: 0, h: 0}
384
- },
385
- 'stem--foo--13': {
386
- duration: 1,
387
- frame: {x: 0, y: 0, w: 0, h: 0},
388
- sourceSize: {w: 0, h: 0}
389
- },
390
- 'stem--foo--14': {
391
- duration: 1,
392
- frame: {x: 0, y: 0, w: 0, h: 0},
393
- sourceSize: {w: 0, h: 0}
394
- },
395
- 'stem--foo--15': {
396
- duration: 1,
397
- frame: {x: 0, y: 0, w: 0, h: 0},
398
- sourceSize: {w: 0, h: 0}
399
- }
400
- }
401
- assertAnimFrames(span, map, expected[direction], direction)
402
- }
403
- })
404
-
405
- test('short anim', () => {
406
- const expected: {[dir in AsepriteDirection]: number[]} = {
407
- forward: [0, 1, 2],
408
- pingpong: [0, 1, 2, 1],
409
- pingpong_reverse: [2, 1, 0, 1],
410
- reverse: [2, 1, 0]
411
- }
412
- for (const direction of Object.values(AsepriteDirection)) {
413
- const span: AsepriteTagSpan = {
414
- direction,
415
- name: 'stem--foo',
416
- from: 0,
417
- to: 2
418
- }
419
- const map: AsepriteFrameMap = {
420
- 'stem--foo--0': {
421
- duration: 1,
422
- frame: {x: 0, y: 0, w: 0, h: 0},
423
- sourceSize: {w: 0, h: 0}
424
- },
425
- 'stem--foo--1': {
426
- duration: 1,
427
- frame: {x: 0, y: 0, w: 0, h: 0},
428
- sourceSize: {w: 0, h: 0}
429
- },
430
- 'stem--foo--2': {
431
- duration: 1,
432
- frame: {x: 0, y: 0, w: 0, h: 0},
433
- sourceSize: {w: 0, h: 0}
434
- }
435
- }
436
- assertAnimFrames(span, map, expected[direction], direction)
437
- }
438
- })
439
-
440
- test('short anim with another anim', () => {
441
- const expected: {[dir in AsepriteDirection]: number[]} = {
442
- forward: [1, 2, 3],
443
- pingpong: [1, 2, 3, 2],
444
- pingpong_reverse: [3, 2, 1, 2],
445
- reverse: [3, 2, 1]
446
- }
447
- for (const direction of Object.values(AsepriteDirection)) {
448
- const span: AsepriteTagSpan = {
449
- direction,
450
- name: 'stem--bar',
451
- from: 1,
452
- to: 3
453
- }
454
- const map: AsepriteFrameMap = {
455
- 'stem--foo--0': {
456
- duration: 1,
457
- frame: {x: 0, y: 0, w: 0, h: 0},
458
- sourceSize: {w: 0, h: 0}
459
- },
460
- 'stem--bar--1': {
461
- duration: 1,
462
- frame: {x: 0, y: 0, w: 0, h: 0},
463
- sourceSize: {w: 0, h: 0}
464
- },
465
- 'stem--bar--2': {
466
- duration: 1,
467
- frame: {x: 0, y: 0, w: 0, h: 0},
468
- sourceSize: {w: 0, h: 0}
469
- },
470
- 'stem--bar--3': {
471
- duration: 1,
472
- frame: {x: 0, y: 0, w: 0, h: 0},
473
- sourceSize: {w: 0, h: 0}
474
- }
475
- }
476
- assertAnimFrames(span, map, expected[direction], direction)
477
- }
478
- })
479
-
480
- test('short anim with multi-cel durations', () => {
481
- const expected: {[dir in AsepriteDirection]: number[]} = {
482
- forward: [0, 1, 1, 2],
483
- pingpong: [0, 1, 1, 2, 1, 1],
484
- pingpong_reverse: [2, 1, 1, 0, 1, 1],
485
- reverse: [2, 1, 1, 0]
486
- }
487
- for (const direction of Object.values(AsepriteDirection)) {
488
- const span: AsepriteTagSpan = {
489
- direction,
490
- name: 'stem--foo',
491
- from: 0,
492
- to: 2
493
- }
494
- const map: AsepriteFrameMap = {
495
- 'stem--foo--0': {
496
- duration: 1,
497
- frame: {x: 0, y: 0, w: 0, h: 0},
498
- sourceSize: {w: 0, h: 0}
499
- },
500
- 'stem--foo--1': {
501
- duration: 63,
502
- frame: {x: 0, y: 0, w: 0, h: 0},
503
- sourceSize: {w: 0, h: 0}
504
- },
505
- 'stem--foo--2': {
506
- duration: 1,
507
- frame: {x: 0, y: 0, w: 0, h: 0},
508
- sourceSize: {w: 0, h: 0}
509
- }
510
- }
511
- assertAnimFrames(span, map, expected[direction], direction)
512
- }
513
- })
514
- })
515
-
516
- describe('parseCel()', () => {
517
- test('parses 1:1 texture mapping/', () => {
518
- const frame = {
519
- frame: {x: 1, y: 2, w: 3, h: 4},
520
- rotated: false,
521
- trimmed: false,
522
- sourceSize: {w: 3, h: 4},
523
- duration: 1
524
- }
525
- assert.deepEqual<XY>(parseCel(frame), {x: 1, y: 2})
526
- })
527
-
528
- test('parses texture mapping with padding', () => {
529
- const frame = {
530
- frame: {x: 1, y: 2, w: 5, h: 6},
531
- rotated: false,
532
- trimmed: false,
533
- sourceSize: {w: 3, h: 4},
534
- duration: 1
535
- }
536
- assert.deepEqual<XY>(parseCel(frame), {x: 2, y: 3})
537
- })
538
- })
539
-
540
- describe('parseHitboxes()', () => {
541
- test('parses hitbox.', () => {
542
- const span: AsepriteTagSpan = {
543
- direction: 'pingpong',
544
- name: 'stem--foo',
545
- from: 0,
546
- to: 0
547
- }
548
- const slices = [
549
- {
550
- name: 'stem--foo',
551
- color: '#ff0000ff',
552
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
553
- }
554
- ]
555
- assert.deepEqual(parseHitboxes(span, slices), {
556
- hitbox: {x: 0, y: 1, w: 2, h: 3},
557
- hurtbox: undefined
558
- })
559
- })
560
-
561
- test('parses hurtbox.', () => {
562
- const span: AsepriteTagSpan = {
563
- direction: 'pingpong',
564
- name: 'stem--foo',
565
- from: 0,
566
- to: 0
567
- }
568
- const slices = [
569
- {
570
- name: 'stem--foo',
571
- color: '#00ff00ff',
572
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
573
- }
574
- ]
575
- assert.deepEqual(parseHitboxes(span, slices), {
576
- hitbox: undefined,
577
- hurtbox: {x: 0, y: 1, w: 2, h: 3}
578
- })
579
- })
580
-
581
- test('parses hitbox and hurtbox (blue).', () => {
582
- const span: AsepriteTagSpan = {
583
- direction: 'pingpong',
584
- name: 'stem--foo',
585
- from: 0,
586
- to: 0
587
- }
588
- const slices = [
589
- {
590
- name: 'stem--foo',
591
- color: '#0000ffff',
592
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
593
- }
594
- ]
595
- assert.deepEqual(parseHitboxes(span, slices), {
596
- hitbox: {x: 0, y: 1, w: 2, h: 3},
597
- hurtbox: {x: 0, y: 1, w: 2, h: 3}
598
- })
599
- })
600
-
601
- test('parses hitbox and hurtbox.', () => {
602
- const span: AsepriteTagSpan = {
603
- direction: 'pingpong',
604
- name: 'stem--foo',
605
- from: 0,
606
- to: 0
607
- }
608
- const slices = [
609
- {
610
- name: 'stem--foo',
611
- color: '#ff0000ff',
612
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
613
- },
614
- {
615
- name: 'stem--foo',
616
- color: '#00ff00ff',
617
- keys: [{frame: 0, bounds: {x: 4, y: 5, w: 6, h: 7}}]
618
- }
619
- ]
620
- assert.deepEqual(parseHitboxes(span, slices), {
621
- hitbox: {x: 0, y: 1, w: 2, h: 3},
622
- hurtbox: {x: 4, y: 5, w: 6, h: 7}
623
- })
624
- })
625
-
626
- test('filters out unrelated tags.', () => {
627
- const span: AsepriteTagSpan = {
628
- direction: 'pingpong',
629
- name: 'stem--foo',
630
- from: 0,
631
- to: 0
632
- }
633
- const slices = [
634
- {
635
- name: 'unrelated--bar',
636
- color: '#ff0000ff',
637
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
638
- },
639
- {
640
- name: 'stem--foo',
641
- color: '#ff0000ff',
642
- keys: [{frame: 0, bounds: {x: 4, y: 5, w: 6, h: 7}}]
643
- }
644
- ]
645
- assert.deepEqual(parseHitboxes(span, slices), {
646
- hitbox: {x: 4, y: 5, w: 6, h: 7},
647
- hurtbox: undefined
648
- })
649
- })
650
-
651
- test('throws on frame with multiple keys.', () => {
652
- const span: AsepriteTagSpan = {
653
- direction: 'pingpong',
654
- name: 'stem--foo',
655
- from: 0,
656
- to: 2
657
- }
658
- const slices = [
659
- {
660
- name: 'stem--foo',
661
- color: '0000ffff',
662
- keys: [
663
- {frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}},
664
- {frame: 1, bounds: {x: 4, y: 5, w: 6, h: 7}},
665
- {frame: 2, bounds: {x: 8, y: 9, w: 10, h: 11}}
666
- ]
667
- }
668
- ]
669
- assert.throws(
670
- () => parseHitboxes(span, slices),
671
- Error('atlas tag "stem--foo" hitbox bounds varies across frames')
672
- )
673
- })
674
-
675
- test('defaults to undefined hitbox.', () => {
676
- const span: AsepriteTagSpan = {
677
- direction: 'pingpong',
678
- name: 'stem--foo',
679
- from: 0,
680
- to: 0
681
- }
682
- assert.deepEqual(parseHitboxes(span, []), {
683
- hitbox: undefined,
684
- hurtbox: undefined
685
- })
686
- })
687
-
688
- test('throws on unsupported color.', () => {
689
- const span: AsepriteTagSpan = {
690
- direction: 'pingpong',
691
- name: 'stem--foo',
692
- from: 0,
693
- to: 0
694
- }
695
- const slices = [
696
- {
697
- name: 'stem--foo',
698
- color: '#ff00ffff',
699
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
700
- }
701
- ]
702
- assert.throws(
703
- () => parseHitboxes(span, slices),
704
- Error('atlas tag "stem--foo" hitbox color #ff00ffff unsupported')
705
- )
706
- })
707
-
708
- test('throws on multiple hitboxes.', () => {
709
- const span: AsepriteTagSpan = {
710
- direction: 'pingpong',
711
- name: 'stem--foo',
712
- from: 0,
713
- to: 1
714
- }
715
- const slices = [
716
- {
717
- name: 'stem--foo',
718
- color: '#ff0000ff',
719
- keys: [
720
- {frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}},
721
- {frame: 1, bounds: {x: 4, y: 5, w: 6, h: 7}},
722
- {frame: 2, bounds: {x: 12, y: 13, w: 14, h: 15}}
723
- ]
724
- },
725
- {
726
- name: 'stem--foo',
727
- color: '#ff0000ff',
728
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
729
- }
730
- ]
731
- assert.throws(
732
- () => parseHitboxes(span, slices),
733
- Error('atlas tag "stem--foo" hitbox bounds varies across frames')
734
- )
735
- })
736
-
737
- test('throws on multiple hurtboxes.', () => {
738
- const span: AsepriteTagSpan = {
739
- direction: 'pingpong',
740
- name: 'stem--foo',
741
- from: 0,
742
- to: 1
743
- }
744
- const slices = [
745
- {
746
- name: 'stem--foo',
747
- color: '#00ff00ff',
748
- keys: [
749
- {frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}},
750
- {frame: 1, bounds: {x: 4, y: 5, w: 6, h: 7}},
751
- {frame: 2, bounds: {x: 12, y: 13, w: 14, h: 15}}
752
- ]
753
- },
754
- {
755
- name: 'stem--foo',
756
- color: '#00ff00ff',
757
- keys: [{frame: 0, bounds: {x: 0, y: 1, w: 2, h: 3}}]
758
- }
759
- ]
760
- assert.throws(
761
- () => parseHitboxes(span, slices),
762
- Error('atlas tag "stem--foo" hitbox bounds varies across frames')
763
- )
764
- })
765
- })
766
-
767
- function assertAnimFrames(
768
- span: Readonly<AsepriteTagSpan>,
769
- map: Readonly<AsepriteFrameMap>,
770
- expected: number[],
771
- msg?: string | undefined
772
- ): void {
773
- assert.deepEqual(
774
- [...parseAnimFrames(span, map)].map(frame =>
775
- Object.values(map).indexOf(frame)
776
- ),
777
- expected,
778
- msg
779
- )
780
- }