adofai 3.2.0 → 3.3.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 +423 -51
- package/dist/src/pathdata/index.d.ts +1 -4
- package/dist/src/pathdata/index.js +45 -2
- package/dist/src/structure/Level.d.ts +0 -65
- package/dist/src/structure/Level.js +23 -395
- package/dist/src/structure/levelAngle.d.ts +40 -0
- package/dist/src/structure/levelAngle.js +316 -0
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# ADOFAI
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A zero-dependency JavaScript/TypeScript library for parsing, editing, and exporting ADOFAI level files. Fully browser-compatible.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
Preview / Edit the `.adofai` file.
|
|
5
|
+
## Features
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
- **Multiple Parsers** — `StringParser`, `BufferParser`, `ArrayBufferParser`, `LargeFileParser` for incremental large-file parsing
|
|
8
|
+
- **Level Management** — load, edit, and export `.adofai` files with full tile and event access
|
|
9
|
+
- **Typed Events** — 57 typed event interfaces covering all ADOFAI event types
|
|
10
|
+
- **Shared Types** — const enums and utility types for ADOFAI-specific values (angles, hitboxes, filters, etc.)
|
|
11
|
+
- **PathData Conversion** — convert between pathData string and angleData array
|
|
12
|
+
- **Effect Filtering** — preset and custom event filtering (clear effects, keep/exclude events)
|
|
13
|
+
- **Precompute Mode** — batch-process and cache progress events for rendering pipelines
|
|
14
|
+
- **Lightweight Data** — memory-efficient tile data extraction for large levels
|
|
9
15
|
|
|
10
16
|
## Installation
|
|
11
17
|
|
|
@@ -14,99 +20,465 @@ npm install adofai
|
|
|
14
20
|
# or
|
|
15
21
|
yarn add adofai
|
|
16
22
|
# or
|
|
17
|
-
pnpm
|
|
23
|
+
pnpm add adofai
|
|
18
24
|
```
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
## Import
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
**ESM:**
|
|
29
|
+
```ts
|
|
30
|
+
import * as adofai from 'adofai';
|
|
31
|
+
import { Level, Parsers, Types, Events, Structure } from 'adofai';
|
|
32
|
+
```
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
**CommonJS:**
|
|
27
35
|
```ts
|
|
28
36
|
const adofai = require('adofai');
|
|
29
37
|
```
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
**Subpath imports:**
|
|
32
40
|
```ts
|
|
33
|
-
import
|
|
41
|
+
import { StringParser } from 'adofai/parser/string';
|
|
42
|
+
import { BufferParser } from 'adofai/parser/buffer';
|
|
43
|
+
import { ArrayBufferParser } from 'adofai/parser/array-buffer';
|
|
44
|
+
import * as Types from 'adofai/types';
|
|
45
|
+
import * as Events from 'adofai/event';
|
|
34
46
|
```
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Parsers
|
|
51
|
+
|
|
52
|
+
Four parsers handle different input formats. All are zero-dependency and browser-compatible.
|
|
53
|
+
|
|
54
|
+
### StringParser
|
|
55
|
+
|
|
56
|
+
Parses ADOFAI JSON from a string. Handles non-standard formatting (trailing commas, raw newlines in strings).
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { StringParser } from 'adofai';
|
|
60
|
+
|
|
61
|
+
const parser = new StringParser();
|
|
62
|
+
const data = parser.parse(`{ "angleData": [...], "settings": {...}, "actions": [...] }`);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### BufferParser (Uint8Array)
|
|
66
|
+
|
|
67
|
+
Parses ADOFAI JSON directly from a `Uint8Array` binary stream using a byte-level state machine. No intermediate string conversion, handles BOM stripping automatically.
|
|
37
68
|
|
|
38
69
|
```ts
|
|
39
|
-
|
|
70
|
+
import { BufferParser } from 'adofai';
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
const parser = new BufferParser();
|
|
73
|
+
const u8 = new Uint8Array(await file.arrayBuffer());
|
|
74
|
+
const data = parser.parse(u8);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### ArrayBufferParser
|
|
42
78
|
|
|
43
|
-
|
|
44
|
-
|
|
79
|
+
Accepts `ArrayBuffer` or `string`. Handles BOM stripping, trailing comma normalization, and UTF-8 decoding.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { ArrayBufferParser } from 'adofai';
|
|
45
83
|
|
|
46
|
-
|
|
84
|
+
const parser = new ArrayBufferParser();
|
|
85
|
+
const buffer = await response.arrayBuffer();
|
|
86
|
+
const data = parser.parse(buffer);
|
|
47
87
|
```
|
|
48
88
|
|
|
49
|
-
|
|
89
|
+
### LargeFileParser
|
|
90
|
+
|
|
91
|
+
Memory-optimized parser for large `.adofai` files. Scans raw bytes to find JSON root properties, then parses sections incrementally without loading the entire file into a JS string. Ideal for files with massive `angleData` or `action` arrays.
|
|
92
|
+
|
|
50
93
|
```ts
|
|
51
|
-
|
|
52
|
-
constructor(opt: string | LevelOptions, provider?: ParseProvider)
|
|
53
|
-
}
|
|
94
|
+
import { LargeFileParser } from 'adofai';
|
|
54
95
|
|
|
96
|
+
const parser = new LargeFileParser((stage, percent) => {
|
|
97
|
+
console.log(`[${stage}] ${percent}%`);
|
|
98
|
+
}, {
|
|
99
|
+
skipLargeActions: false, // skip actions if > 100MB
|
|
100
|
+
maxActions: 0 // limit parsed actions count
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const result = parser.parse(arrayBuffer);
|
|
104
|
+
// result: { settings?, angleData?, pathData?, actions?, decorations? }
|
|
55
105
|
```
|
|
56
|
-
Available ParseProviders:
|
|
57
|
-
`StringParser` `ArrayBufferParser` `BufferParser`
|
|
58
106
|
|
|
107
|
+
Key behaviors:
|
|
108
|
+
- **< 50MB** — sections are fully JSON.parsed normally.
|
|
109
|
+
- **> 50MB** — `actions` is parsed incrementally (each object parsed independently).
|
|
110
|
+
- **> 100MB** — actions can be skipped entirely via `skipLargeActions: true`.
|
|
111
|
+
- **Any size** — `angleData` is always parsed incrementally (number-by-number).
|
|
112
|
+
- BOM is automatically stripped.
|
|
113
|
+
|
|
114
|
+
---
|
|
59
115
|
|
|
60
|
-
|
|
61
|
-
but you can use `BufferParser` to parse ADOFAI files in Node environment.
|
|
116
|
+
## Level
|
|
62
117
|
|
|
63
|
-
|
|
64
|
-
|
|
118
|
+
The `Level` class is the core data structure. It accepts ADOFAI file content (string, object, ArrayBuffer, Uint8Array, or Buffer) and provides tile management and export.
|
|
119
|
+
|
|
120
|
+
### Create & Load
|
|
65
121
|
|
|
66
|
-
### Load Level
|
|
67
122
|
```ts
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
123
|
+
import { Level } from 'adofai';
|
|
124
|
+
|
|
125
|
+
// From string
|
|
126
|
+
const level = new Level(adofaiJsonString);
|
|
127
|
+
await level.load();
|
|
128
|
+
|
|
129
|
+
// With custom parser provider
|
|
130
|
+
const level = new Level(rawData, bufferParser);
|
|
131
|
+
await level.load();
|
|
132
|
+
|
|
133
|
+
// From already-parsed object
|
|
134
|
+
const level = new Level({
|
|
135
|
+
angleData: [...],
|
|
136
|
+
settings: { ... },
|
|
137
|
+
actions: [...],
|
|
138
|
+
decorations: [...]
|
|
139
|
+
});
|
|
140
|
+
await level.load();
|
|
141
|
+
|
|
142
|
+
// Event-based loading
|
|
143
|
+
level.on('load', () => {
|
|
144
|
+
console.log('Level loaded:', level.tiles.length, 'tiles');
|
|
145
|
+
});
|
|
146
|
+
level.load();
|
|
147
|
+
|
|
148
|
+
// Progress events
|
|
149
|
+
level.on('parse:progress', (event) => {
|
|
150
|
+
// { stage: 'relativeAngle', current: 500, total: 1000, percent: 50 }
|
|
151
|
+
console.log(`${event.stage}: ${event.percent}%`);
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Progress stages: `start` → `pathData` | `angleData` → `relativeAngle` → `tilePosition` → `complete`
|
|
156
|
+
|
|
157
|
+
### Data Model Overview
|
|
158
|
+
|
|
159
|
+
After loading, the level data is organized into two layers:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
┌──────────────────────────────────────────┐
|
|
163
|
+
│ Source Data (read-only initial values) │
|
|
164
|
+
│ level.angleData — raw angle array │
|
|
165
|
+
│ level.actions — flat event list │
|
|
166
|
+
│ level.__decorations — flat deco list │
|
|
167
|
+
│ level.settings — level settings │
|
|
168
|
+
├──────────────────────────────────────────┤
|
|
169
|
+
│ Working Data (primary operation target) │
|
|
170
|
+
│ level.tiles — Tile[] │
|
|
171
|
+
└──────────────────────────────────────────┘
|
|
72
172
|
```
|
|
73
173
|
|
|
74
|
-
|
|
174
|
+
**`level.tiles` is where all data operations happen.** The source arrays (`angleData`, `actions`, `decorations`) are initial inputs and are **not** kept in sync when you modify tiles. When you export, `angleData`, `actions`, and `decorations` are reconstructed from `level.tiles`.
|
|
175
|
+
|
|
176
|
+
### Tile Structure
|
|
177
|
+
|
|
178
|
+
Each tile in `level.tiles` has the following structure:
|
|
179
|
+
|
|
75
180
|
```ts
|
|
76
|
-
|
|
181
|
+
interface Tile {
|
|
182
|
+
direction?: number; // Original angle data value (incl. 999)
|
|
183
|
+
angle?: number; // Computed relative angle (for rendering)
|
|
184
|
+
_lastdir?: number; // Previous tile's direction
|
|
185
|
+
twirl?: number; // Accumulated twirl count up to this tile
|
|
186
|
+
actions: ActionData[]; // Events belonging to this tile
|
|
187
|
+
addDecorations?: ActionData[]; // Decorations on this tile
|
|
188
|
+
position?: number[]; // Computed [x, y] position
|
|
189
|
+
extraProps?: Record<string, any>; // Extra computed data (angle1, angle2, cangle)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
77
192
|
|
|
78
|
-
|
|
193
|
+
### Working with Tiles
|
|
194
|
+
|
|
195
|
+
**Read tile data:**
|
|
196
|
+
```ts
|
|
197
|
+
// Total tile count
|
|
198
|
+
level.tiles.length;
|
|
199
|
+
|
|
200
|
+
// Access a specific tile
|
|
201
|
+
const tile = level.tiles[42];
|
|
202
|
+
tile.direction; // raw angle value
|
|
203
|
+
tile.angle; // relative angle
|
|
204
|
+
tile.actions; // events on this tile
|
|
205
|
+
tile.addDecorations; // decorations on this tile
|
|
206
|
+
tile.twirl; // twirl count
|
|
207
|
+
tile.position; // [x, y] (after calculateTilePosition)
|
|
79
208
|
```
|
|
80
209
|
|
|
81
|
-
|
|
210
|
+
**Modify tiles:**
|
|
82
211
|
```ts
|
|
83
|
-
|
|
212
|
+
// Append a new tile
|
|
213
|
+
level.floorOperation({ type: 'append', direction: 180 });
|
|
84
214
|
|
|
85
|
-
|
|
215
|
+
// Insert at specific index
|
|
216
|
+
level.floorOperation({ type: 'insert', direction: 90, id: 10 });
|
|
217
|
+
|
|
218
|
+
// Delete a tile
|
|
219
|
+
level.floorOperation({ type: 'delete', id: 10 });
|
|
86
220
|
```
|
|
87
221
|
|
|
88
|
-
|
|
222
|
+
**Query events across tiles:**
|
|
223
|
+
```ts
|
|
224
|
+
// Find all tiles with a specific event type
|
|
225
|
+
const results = level.filterActionsByEventType('Flash');
|
|
226
|
+
// returns { index: number, action: ActionData }[]
|
|
89
227
|
|
|
90
|
-
|
|
91
|
-
|
|
228
|
+
// Get events at a specific tile index
|
|
229
|
+
const { count, actions } = level.getActionsByIndex('MoveTrack', 5);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Calculate Tile Positions
|
|
233
|
+
|
|
234
|
+
Populates `tile.position` and `tile.extraProps` for all tiles.
|
|
92
235
|
|
|
93
236
|
```ts
|
|
94
|
-
|
|
95
|
-
|
|
237
|
+
const positions = level.calculateTilePosition();
|
|
238
|
+
// returns number[][] — [x, y] for each tile including endpoint
|
|
96
239
|
|
|
97
|
-
|
|
98
|
-
|
|
240
|
+
// After this call, each tile.position is set
|
|
241
|
+
level.tiles[5].position; // [x, y]
|
|
242
|
+
level.tiles[5].extraProps; // { angle1, angle2, cangle }
|
|
99
243
|
```
|
|
100
244
|
|
|
245
|
+
### Effect Filtering (operates on tiles)
|
|
246
|
+
|
|
247
|
+
All effect operations modify `level.tiles` in-place.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import { Presets } from 'adofai';
|
|
251
|
+
|
|
252
|
+
// Using a preset
|
|
253
|
+
level.clearEffect('preset_noeffect');
|
|
101
254
|
|
|
102
|
-
|
|
255
|
+
// Custom filter — keep only specific events
|
|
256
|
+
level.clearEvent({ type: 'include', events: ['SetSpeed', 'Twirl'] });
|
|
103
257
|
|
|
104
|
-
|
|
258
|
+
// Custom filter — exclude specific events
|
|
259
|
+
level.clearEvent({ type: 'exclude', events: ['Flash', 'Bloom'] });
|
|
260
|
+
|
|
261
|
+
// Clear all decorations from all tiles
|
|
262
|
+
level.clearDeco();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Export (reconstructs from tiles)
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
// Export as formatted ADOFAI JSON string
|
|
269
|
+
const str = level.export('string', 0, true);
|
|
270
|
+
// fs.writeFileSync('output.adofai', str);
|
|
271
|
+
|
|
272
|
+
// Export as object
|
|
273
|
+
const obj = level.export('object', 0, true);
|
|
274
|
+
// { angleData, settings, actions, decorations }
|
|
275
|
+
// All three arrays are reconstructed from level.tiles
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Event System
|
|
105
279
|
|
|
106
280
|
```ts
|
|
107
|
-
//
|
|
108
|
-
const
|
|
281
|
+
// Listen to lifecycle events
|
|
282
|
+
const guid = level.on('load', (level) => { /* ... */ });
|
|
283
|
+
|
|
284
|
+
// Remove listener by GUID
|
|
285
|
+
level.off(guid);
|
|
286
|
+
|
|
287
|
+
// Trigger custom events
|
|
288
|
+
level.trigger('custom:event', data);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Types
|
|
294
|
+
|
|
295
|
+
Shared ADOFAI-specific types and const enums, exported via `Types.*` or `adofai/types`.
|
|
296
|
+
|
|
297
|
+
### Const Enums
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { Types } from 'adofai';
|
|
301
|
+
|
|
302
|
+
Types.TextAnchor.UpperLeft // 'UpperLeft'
|
|
303
|
+
Types.Hitbox.None // 'None'
|
|
304
|
+
Types.FilterType.Bloom // 'Bloom'
|
|
305
|
+
Types.FlashStyle.Flash // 'Flash'
|
|
306
|
+
Types.RelativeTo.Tile // 'Tile'
|
|
307
|
+
Types.TargetPlanet.All // 'All'
|
|
308
|
+
Types.AngleCorrectionDir.CW // 'CW'
|
|
309
|
+
Types.InputEventState.Subscribe // 'Subscribe'
|
|
310
|
+
Types.InputEventTarget.Pressed // 'Pressed'
|
|
311
|
+
Types.Condition.IfPassed // 'IfPassed'
|
|
312
|
+
Types.BgDisplayMode.FitToScreen // 'FitToScreen'
|
|
313
|
+
Types.BgShapeType.Circle // 'Circle'
|
|
314
|
+
Types.HitsoundType.Kick // 'Kick'
|
|
315
|
+
Types.HoldMidSoundTimingRelativeTo.Start // 'Start'
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Utility Types
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
Types.Vec2 // [number, number]
|
|
322
|
+
Types.ABoolean // boolean | 'Enabled' | 'Disabled' | 'true' | 'false'
|
|
323
|
+
Types.TileReference // [number, TileReferenceType]
|
|
324
|
+
Types.TileReferenceType // 'ThisTile' | 'Start' | 'End'
|
|
325
|
+
Types.Vec2Like // [number, number] | { x: number; y: number }
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Utility Functions
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
// Evaluate ABoolean values
|
|
332
|
+
Types.isEventEnabled(value, defaultValue); // boolean
|
|
333
|
+
|
|
334
|
+
// Resolve relative tile references
|
|
335
|
+
Types.resolveTileReference([2, 'End'], currentTileId, totalTiles);
|
|
336
|
+
// = totalTiles - 1 + 2
|
|
337
|
+
|
|
338
|
+
Types.resolveTileReference([-1, 'ThisTile'], 5, 100);
|
|
339
|
+
// = 4
|
|
340
|
+
|
|
341
|
+
// Normalize ADOFAI position formats (array and object forms)
|
|
342
|
+
Types.normalizeVec2([3, 5]); // [3, 5]
|
|
343
|
+
Types.normalizeVec2({ x: 3, y: 5 }); // [3, 5]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## PathData
|
|
349
|
+
|
|
350
|
+
Convert pathData strings (e.g. `"REJW"`) into angleData arrays.
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
import { pathData } from 'adofai';
|
|
354
|
+
|
|
355
|
+
// Character → angle mapping table
|
|
356
|
+
pathData.pathDataTable;
|
|
357
|
+
// { R: 0, p: 15, J: 30, E: 45, T: 60, ... }
|
|
358
|
+
|
|
359
|
+
// Convert path string to angle numbers
|
|
360
|
+
const angleData = pathData.parseToangleData("REJW");
|
|
361
|
+
// [0, 45, 30, 165]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Event Interfaces
|
|
367
|
+
|
|
368
|
+
57 typed event interfaces covering all ADOFAI event types. Each extends `AdofaiEvent` with `floor` and `eventType` fields.
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import type { Events } from 'adofai';
|
|
372
|
+
|
|
373
|
+
// Each event type has a matching interface:
|
|
374
|
+
const flash: Events.Flash = {
|
|
375
|
+
floor: 0,
|
|
376
|
+
eventType: 'Flash',
|
|
377
|
+
duration: 1,
|
|
378
|
+
color: 'ffffff',
|
|
379
|
+
flashStyle: Types.FlashStyle.Flash,
|
|
380
|
+
opacity: 100,
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const setSpeed: Events.SetSpeed = {
|
|
384
|
+
floor: 0,
|
|
385
|
+
eventType: 'SetSpeed',
|
|
386
|
+
speed: 1.0,
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
const moveTrack: Events.MoveTrack = {
|
|
390
|
+
floor: 5,
|
|
391
|
+
eventType: 'MoveTrack',
|
|
392
|
+
duration: 4,
|
|
393
|
+
positionOffset: [0, 3],
|
|
394
|
+
rotation: 90,
|
|
395
|
+
easing: 'Linear',
|
|
396
|
+
};
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Full list of event types: `SetSpeed`, `Twirl`, `Checkpoint`, `MoveCamera`, `CustomBackground`, `ChangeTrack`, `ColorTrack`, `AnimateTrack`, `RecolorTrack`, `MoveTrack`, `SetText`, `Flash`, `SetHitsound`, `SetFilter`, `SetFilterAdvanced`, `SetPlanetRotation`, `HallOfMirrors`, `ShakeScreen`, `MoveDecorations`, `PositionTrack`, `RepeatEvents`, `Bloom`, `Hold`, `SetHoldSound`, `SetConditionalEvents`, `ScreenTile`, `ScreenScroll`, `EditorComment`, `Bookmark`, `CallMethod`, `AddComponent`, `PlaySound`, `MultiPlanet`, `FreeRoam`, `FreeRoamTwirl`, `FreeRoamRemove`, `Pause`, `AutoPlayTiles`, `Hide`, `ScaleMargin`, `ScaleRadius`, `Multitap`, `TileDimensions`, `KillPlayer`, `ScalePlanets`, `SetFloorIcon`, `AddDecoration`, `AddText`, `AddObject`, `SetObject`, `SetDefaultText`, `SetFrameRate`, `AddParticle`, `SetParticle`, `EmitParticle`, `SetInputEvent`.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Effect Presets
|
|
406
|
+
|
|
407
|
+
## Advanced: Precompute Mode
|
|
408
|
+
|
|
409
|
+
For rendering pipelines that need deterministic event replay. Instead of firing progress events during load, events are cached and can be polled.
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
level.enablePrecomputeMode();
|
|
413
|
+
await level.load();
|
|
414
|
+
level.calculateTilePosition();
|
|
415
|
+
|
|
416
|
+
const events = level.getPrecomputedEvents();
|
|
417
|
+
// { start: [...], pathData: [...], angleData: [...], ... }
|
|
418
|
+
|
|
419
|
+
// Get events up to a specific progress percentage
|
|
420
|
+
const at50 = level.getEventsAtPercent(50);
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Advanced: Lightweight Tile Data
|
|
426
|
+
|
|
427
|
+
For large levels where storing full `Tile[]` objects is memory-prohibitive. Precomputes only rendering-essential data.
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
// Precompute lightweight data (angles + positions + twirl flags)
|
|
431
|
+
level.precomputeLightweight();
|
|
432
|
+
|
|
433
|
+
// Access the compact data
|
|
434
|
+
const data = level.getLightweightData();
|
|
435
|
+
// { totalTiles, angles: number[], positions: [number,number][], twirlFlags: boolean[] }
|
|
436
|
+
|
|
437
|
+
// Get a range of tiles (chunked access)
|
|
438
|
+
const chunk = level.getLightweightDataRange(0, 100);
|
|
439
|
+
// { angles: [...], positions: [...], twirlFlags: [...] }
|
|
440
|
+
|
|
441
|
+
// Get single tile render data
|
|
442
|
+
const tile = level.getTileRenderData(42);
|
|
443
|
+
// { angle, position, hasTwirl }
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Structure Interfaces
|
|
449
|
+
|
|
450
|
+
Available via `adofai/structure`:
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
import type { AdofaiEvent, LevelOptions, Tile, ParseProgressEvent } from 'adofai/structure';
|
|
454
|
+
|
|
455
|
+
// AdofaiEvent: { floor: number, eventType: string, [key: string]: any }
|
|
456
|
+
// LevelOptions: { pathData?: string, angleData?: number[], actions, settings, decorations }
|
|
457
|
+
// Tile: { direction?, angle?, actions, addDecorations?, position?, ... }
|
|
458
|
+
// ParseProgressEvent: { stage, current, total, percent, data? }
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Package Exports
|
|
464
|
+
|
|
465
|
+
| Import path | Contents |
|
|
466
|
+
|---|---|
|
|
467
|
+
| `adofai` | Main: Level, Parsers, Types, Events, Structure, Presets, pathData |
|
|
468
|
+
| `adofai/parser` | Parser classes |
|
|
469
|
+
| `adofai/parser/string` | StringParser |
|
|
470
|
+
| `adofai/parser/buffer` | BufferParser |
|
|
471
|
+
| `adofai/parser/array-buffer` | ArrayBufferParser |
|
|
472
|
+
| `adofai/types` | Types (const enums, utilities) |
|
|
473
|
+
| `adofai/event` | All event type interfaces |
|
|
474
|
+
| `adofai/structure` | Core interfaces (LevelOptions, Tile, etc.) |
|
|
475
|
+
| `adofai/filter` | Filter presets |
|
|
476
|
+
| `adofai/filter/effect-processor` | Low-level effect processor |
|
|
477
|
+
| `adofai/pathdata` | PathData conversion table |
|
|
478
|
+
| `adofai/types` | Utility types and functions |
|
|
109
479
|
|
|
480
|
+
---
|
|
110
481
|
|
|
482
|
+
## License
|
|
111
483
|
|
|
112
|
-
|
|
484
|
+
BSD-3-Clause
|
|
@@ -1,5 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
1
|
+
/** Standard direction characters → absolute angle */
|
|
2
|
+
const pathDataTable = {
|
|
3
|
+
"R": 0, "p": 15, "J": 30, "E": 45, "T": 60, "o": 75, "U": 90, "q": 105,
|
|
4
|
+
"G": 120, "Q": 135, "H": 150, "W": 165, "L": 180, "x": 195, "N": 210,
|
|
5
|
+
"Z": 225, "F": 240, "V": 255, "D": 270, "Y": 285, "B": 300, "C": 315,
|
|
6
|
+
"M": 330, "A": 345, "!": 999
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Special offset characters — these are NOT absolute angles.
|
|
10
|
+
* Instead, they represent a relative change from the previous angle:
|
|
11
|
+
* result = previous_angle + offset
|
|
12
|
+
*/
|
|
13
|
+
const offsetMap = {
|
|
14
|
+
"5": 72,
|
|
15
|
+
"6": -72,
|
|
16
|
+
"7": 52,
|
|
17
|
+
"8": -52,
|
|
18
|
+
"9": -30,
|
|
19
|
+
"h": 120,
|
|
20
|
+
"j": -120,
|
|
21
|
+
"t": 60,
|
|
22
|
+
"y": 300,
|
|
23
|
+
};
|
|
24
|
+
const parseToangleData = (pathdata) => {
|
|
25
|
+
const result = new Array(pathdata.length);
|
|
26
|
+
let prev = 0;
|
|
27
|
+
for (let i = 0; i < pathdata.length; i++) {
|
|
28
|
+
const c = pathdata[i];
|
|
29
|
+
if (c in pathDataTable) {
|
|
30
|
+
// Standard character: absolute angle
|
|
31
|
+
result[i] = pathDataTable[c];
|
|
32
|
+
prev = pathDataTable[c];
|
|
33
|
+
}
|
|
34
|
+
else if (c in offsetMap) {
|
|
35
|
+
// Special character: relative offset from previous angle
|
|
36
|
+
result[i] = prev + offsetMap[c];
|
|
37
|
+
prev = result[i];
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Unknown character: keep current angle
|
|
41
|
+
result[i] = prev;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
3
46
|
export default {
|
|
4
47
|
pathDataTable,
|
|
5
48
|
parseToangleData
|