adofai 2.9.7

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,351 @@
1
+ import { AdofaiEvent, LevelOptions, EventCallback, GuidCallback, Tile, ParseProvider } from './interfaces';
2
+ import pathData from '../pathdata';
3
+ import exportAsADOFAI from '../format'
4
+ import {StringParser} from '../parser';
5
+ import effectProcessor from '../effectProcessor';
6
+ import * as presets from '../presets';
7
+
8
+ export class Level {
9
+ private _events: Map<string, EventCallback[]>;
10
+ private guidCallbacks: Map<string, GuidCallback>;
11
+ private guidCounter: number;
12
+ private _options: string | LevelOptions;
13
+ private _provider?: ParseProvider;
14
+ public angleData!: number[];
15
+ public actions!: AdofaiEvent[];
16
+ public settings!: Record<string, any>;
17
+ public __decorations!: AdofaiEvent[];
18
+ public tiles!: Tile[];
19
+ private _angleDir!: number;
20
+ private _twirlCount!: number;
21
+
22
+ constructor(opt: string | LevelOptions, provider?: ParseProvider) {
23
+ this._events = new Map();
24
+ this.guidCallbacks = new Map();
25
+ this.guidCounter = 0;
26
+
27
+ this._options = opt;
28
+ this._provider = provider;
29
+ }
30
+
31
+ generateGUID(): string {
32
+ return `event_${Date.now()}_${this.guidCounter++}_${Math.floor(Math.random() * 1000)}`;
33
+ }
34
+
35
+ load(): Promise<boolean> {
36
+ return new Promise((resolve, reject) => {
37
+ let opt = this._options;
38
+ let options: LevelOptions;
39
+
40
+ switch (typeof opt) {
41
+ case 'string':
42
+ try {
43
+ options = StringParser.parseAsObject(opt, this._provider);
44
+ } catch (e) {
45
+ reject(e);
46
+ return;
47
+ }
48
+ break;
49
+ case 'object':
50
+ options = Object.assign({}, opt);
51
+ break;
52
+ default:
53
+ reject("Options must be String or Object");
54
+ return;
55
+ }
56
+ if ('pathData' in options) {
57
+ this.angleData = pathData.parseToangleData(options['pathData']!);
58
+ } else {
59
+ if ('angleData' in options) {
60
+ this.angleData = options['angleData']!;
61
+ } else {
62
+ reject("There is not any angle datas.");
63
+ return;
64
+ }
65
+ }
66
+ if ('actions' in options) {
67
+ this.actions = options['actions']!;
68
+ } else {
69
+ this.actions = [];
70
+ }
71
+ if ('settings' in options) {
72
+ this.settings = options['settings']!;
73
+ } else {
74
+ reject("There is no ADOFAI settings.");
75
+ return;
76
+ }
77
+ if ('decorations' in options) {
78
+ this.__decorations = options['decorations']!;
79
+ } else {
80
+ this.__decorations = [];
81
+ }
82
+ this.tiles = [];
83
+ this._angleDir = -180;
84
+ this._twirlCount = 0;
85
+ this._createArray(this.angleData.length, { angleData: this.angleData, actions: this.actions, decorations: this.__decorations })
86
+ .then(e => {
87
+ this.tiles = e;
88
+ this.trigger('load', this);
89
+ resolve(true);
90
+ }).catch(e => {
91
+ reject(e);
92
+ });
93
+
94
+ });
95
+ }
96
+
97
+ on(eventName: string, callback: Function): string {
98
+ if (!this._events.has(eventName)) {
99
+ this._events.set(eventName, []);
100
+ }
101
+
102
+ const guid = this.generateGUID();
103
+ const eventCallbacks = this._events.get(eventName)!;
104
+
105
+ eventCallbacks.push({ guid, callback });
106
+ this.guidCallbacks.set(guid, { eventName, callback });
107
+
108
+ return guid;
109
+ }
110
+
111
+ trigger(eventName: string, data: any): void {
112
+ if (!this._events.has(eventName)) return;
113
+
114
+ const callbacks = this._events.get(eventName)!;
115
+ callbacks.forEach(({ callback }) => callback(data));
116
+ }
117
+
118
+ off(guid: string): void {
119
+ if (!this.guidCallbacks.has(guid)) return;
120
+
121
+ const { eventName } = this.guidCallbacks.get(guid)!;
122
+ this.guidCallbacks.delete(guid);
123
+
124
+ if (!this._events.has(eventName)) return;
125
+
126
+ const callbacks = this._events.get(eventName)!;
127
+ const index = callbacks.findIndex(cb => cb.guid === guid);
128
+
129
+ if (index !== -1) {
130
+ callbacks.splice(index, 1);
131
+ }
132
+ }
133
+
134
+ private async _createArray(xLength: number, opt: { angleData: number[], actions: AdofaiEvent[], decorations: AdofaiEvent[] }): Promise<Tile[]> {
135
+ let m = Array.from({ length: xLength }, (_, i) => ({
136
+ direction: opt.angleData[i],
137
+ _lastdir: opt.angleData[i - 1] || 0,
138
+ actions: this._filterByFloor(opt.actions, i),
139
+ angle: this._parseAngle(opt.angleData, i, this._twirlCount % 2),
140
+ addDecorations: this._filterByFloorwithDeco(opt.decorations, i),
141
+ twirl: this._twirlCount,
142
+ extraProps: {}
143
+ }));
144
+ return m;
145
+ }
146
+
147
+ private _changeAngle(): Tile[] {
148
+ let y = 0;
149
+ let m = this.tiles.map(t => {
150
+ y++;
151
+ t.angle = this._parsechangedAngle(t.direction!, y, t.twirl!, t._lastdir!);
152
+ return t;
153
+ });
154
+ return m;
155
+ }
156
+
157
+ private _parsechangedAngle(agd: number, i: number, isTwirl: number, lstagd: number): number {
158
+ let prev = 0;
159
+ if (i === 0) { this._angleDir = 180; }
160
+ if (agd === 999) {
161
+ this._angleDir = lstagd;
162
+ if (isNaN(this._angleDir)) {
163
+ this._angleDir = 0;
164
+ }
165
+ prev = 0;
166
+ } else {
167
+ if (isTwirl === 0) {
168
+ prev = (this._angleDir - agd) % 360;
169
+ } else {
170
+ prev = 360 - (this._angleDir - agd) % 360;
171
+ }
172
+ if (prev === 0) {
173
+ prev = 360;
174
+ }
175
+ this._angleDir = agd + 180;
176
+ }
177
+ return prev;
178
+ }
179
+
180
+
181
+ private _filterByFloor(arr: AdofaiEvent[], i: number): AdofaiEvent[] {
182
+ let actionT = arr.filter(item => item.floor === i);
183
+ this._twirlCount += actionT.filter(t => t.eventType === 'Twirl').length;
184
+ return actionT.map(({ floor, ...rest }) => rest);
185
+ }
186
+
187
+ private _flattenAngleDatas(arr: Tile[]): number[] {
188
+ return arr.map(item => item.direction!);
189
+ }
190
+ private _flattenActionsWithFloor(arr: Tile[]): AdofaiEvent[] {
191
+ return arr.map(item => item.actions).flat();
192
+ }
193
+ private _filterByFloorwithDeco(arr: AdofaiEvent[], i: number): AdofaiEvent[] {
194
+ let actionT = arr.filter(item => item.floor === i);
195
+ return actionT.map(({ floor, ...rest }) => rest);
196
+ }
197
+
198
+ private _flattenDecorationsWithFloor(arr: Tile[]): AdofaiEvent[] {
199
+ return arr.map(item => item.addDecorations).flat() as AdofaiEvent[];
200
+ }
201
+ private _parseAngle(agd: number[], i: number, isTwirl: number): number {
202
+ let prev = 0;
203
+ if (i === 0) { this._angleDir = 180; }
204
+ if (agd[i] === 999) {
205
+ this._angleDir = agd[i - 1];
206
+ if (isNaN(this._angleDir)) {
207
+ this._angleDir = 0;
208
+ }
209
+ prev = 0;
210
+ } else {
211
+ if (isTwirl === 0) {
212
+ prev = (this._angleDir - agd[i]) % 360;
213
+ } else {
214
+ prev = 360 - (this._angleDir - agd[i]) % 360;
215
+ }
216
+ if (prev === 0) {
217
+ prev = 360;
218
+ }
219
+ this._angleDir = agd[i] + 180;
220
+ }
221
+ return prev;
222
+ }
223
+
224
+ public filterActionsByEventType(en: string): { index: number, action: AdofaiEvent }[] {
225
+ return Object.entries(this.tiles)
226
+ .flatMap(([index, a]) =>
227
+ (a.actions || []).map(b => ({ b, index }))
228
+ )
229
+ .filter(({ b }) => b.eventType === en)
230
+ .map(({ b, index }) => ({
231
+ index: Number(index),
232
+ action: b
233
+ }));
234
+ }
235
+
236
+ public getActionsByIndex(en: string, index: number): { count: number, actions: AdofaiEvent[] } {
237
+ const filtered = this.filterActionsByEventType(en);
238
+ const matches = filtered.filter(item => item.index === index);
239
+
240
+ return {
241
+ count: matches.length,
242
+ actions: matches.map(item => item.action)
243
+ };
244
+ }
245
+ public calculateTileCoordinates(): void {
246
+ let angles = this.angleData;
247
+ let floats: number[] = [];
248
+ let startPos = [0, 0];
249
+
250
+ for (let i = 0; i < this.tiles.length; i++) {
251
+ let value = angles[i];
252
+ if (value === 999) {
253
+ value = angles[i - 1] + 180;
254
+ }
255
+ floats.push(value);
256
+ }
257
+
258
+ for (let i = 0; i <= floats.length; i++) {
259
+ let angle1 = Number((i === floats.length) ? floats[i - 1] : floats[i]) || 0;
260
+ let angle2 = Number((i === 0) ? 0 : floats[i - 1]) || 0;
261
+ let currentTile = this.tiles[i];
262
+ if (this.getActionsByIndex('PositionTrack', i).count > 0) {
263
+ let pevent = this.getActionsByIndex('PositionTrack', i).actions[0];
264
+ if ('positionOffset' in pevent) {
265
+ if (pevent['editorOnly'] !== true && pevent['editorOnly'] !== 'Enabled') {
266
+ startPos[0] += pevent['positionOffset'][0];
267
+ startPos[1] += pevent['positionOffset'][1];
268
+ }
269
+ }
270
+ }
271
+ startPos[0] += Math.cos(angle1 * Math.PI / 180);
272
+ startPos[1] += Math.sin(angle1 * Math.PI / 180);
273
+ if (typeof currentTile !== 'undefined') {
274
+ currentTile.position = [
275
+ Number(startPos[0].toFixed(8)),
276
+ Number(startPos[1].toFixed(8))
277
+ ];
278
+ currentTile.extraProps!.angle1 = angle1;
279
+ currentTile.extraProps!.angle2 = angle2 - 180;
280
+ currentTile.extraProps!.cangle = i === floats.length ? floats[i - 1] + 180 : floats[i];
281
+ }
282
+ }
283
+ }
284
+ public floorOperation(info: { type: 'append' | 'insert' | 'delete', direction: number, id?: number } = { type: 'append', direction: 0 }): void {
285
+ switch (info.type) {
286
+ case 'append':
287
+ this.appendFloor(info);
288
+ break;
289
+ case 'insert':
290
+ if (typeof info.id === 'number') {
291
+ this.tiles.splice(info.id, 0, {
292
+ direction: info.direction || 0,
293
+ angle: 0,
294
+ actions: [],
295
+ addDecorations: [],
296
+ _lastdir: this.tiles[info.id - 1].direction,
297
+ twirl: this.tiles[info.id - 1].twirl
298
+ });
299
+ }
300
+ break;
301
+ case 'delete':
302
+ if (typeof info.id === 'number') {
303
+ this.tiles.splice(info.id, 1);
304
+ }
305
+ break;
306
+ }
307
+ this._changeAngle();
308
+ }
309
+
310
+ public appendFloor(args: { direction: number }): void {
311
+ this.tiles.push({
312
+ direction: args.direction,
313
+ angle: 0,
314
+ actions: [],
315
+ addDecorations: [],
316
+ _lastdir: this.tiles[this.tiles.length - 1].direction,
317
+ twirl: this.tiles[this.tiles.length - 1].twirl,
318
+ extraProps: {}
319
+ });
320
+ this._changeAngle();
321
+ }
322
+
323
+ public clearDeco(): boolean {
324
+ this.tiles = effectProcessor.clearDecorations(this.tiles) as Tile[];
325
+ return true;
326
+ }
327
+
328
+ public clearEffect(presetName: string): void {
329
+ this.clearEvent(presets[presetName as keyof typeof presets]);
330
+ }
331
+
332
+ public clearEvent(preset: { type: string, events: string[] }): void {
333
+ switch (preset.type) {
334
+ case 'include':
335
+ this.tiles = effectProcessor.keepEvents(preset.events, this.tiles) as Tile[];
336
+ break;
337
+ case 'exclude':
338
+ this.tiles = effectProcessor.clearEvents(preset.events, this.tiles) as Tile[];
339
+ break;
340
+ }
341
+ }
342
+ public export(type: 'string' | 'object', indent?: number, useAdofaiStyle: boolean = true): string | Record<string, any> {
343
+ const ADOFAI = {
344
+ angleData: this._flattenAngleDatas(this.tiles),
345
+ settings: this.settings,
346
+ actions: this._flattenActionsWithFloor(this.tiles),
347
+ decorations: this._flattenDecorationsWithFloor(this.tiles)
348
+ };
349
+ return type === 'object' ? ADOFAI : exportAsADOFAI(ADOFAI, indent, useAdofaiStyle);
350
+ }
351
+ }
@@ -0,0 +1,38 @@
1
+ export interface AdofaiEvent {
2
+ eventType: string;
3
+ [key: string]: any;
4
+ }
5
+
6
+ export interface LevelOptions {
7
+ pathData: string;
8
+ angleData: number[];
9
+ actions: AdofaiEvent[];
10
+ settings: Record<string, any>;
11
+ decorations: AdofaiEvent[];
12
+ [key: string]: any;
13
+ }
14
+
15
+ export interface EventCallback {
16
+ guid: string;
17
+ callback: Function;
18
+ }
19
+
20
+ export interface GuidCallback {
21
+ eventName: string;
22
+ callback: Function;
23
+ }
24
+
25
+ export interface Tile {
26
+ direction?: number;
27
+ angle?: number;
28
+ actions: AdofaiEvent[];
29
+ addDecorations?: AdofaiEvent[];
30
+ _lastdir?: number;
31
+ twirl?: number;
32
+ position?: number[];
33
+ extraProps?: Record<string, any>;
34
+ }
35
+
36
+ export interface ParseProvider {
37
+ parse(t: string): LevelOptions;
38
+ }
@@ -0,0 +1,6 @@
1
+ import { Level } from './structure/Level';
2
+ import { AdofaiEvent, LevelOptions, EventCallback, GuidCallback, Tile, ParseProvider } from './structure/interfaces';
3
+
4
+ export { AdofaiEvent, LevelOptions, EventCallback, GuidCallback, Tile, ParseProvider };
5
+
6
+ export default Level;