core2d 2.10.2

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/src/Core2D.mjs ADDED
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+
3
+ import { Engine } from "./Engine.mjs";
4
+ import { Frame } from "./Frame.mjs";
5
+ import { Point } from "./Point.mjs";
6
+ import { Rect } from "./Rect.mjs";
7
+ import { Scene } from "./Scene.mjs";
8
+ import { Sprite } from "./Sprite.mjs";
9
+
10
+ export class Core2D {
11
+ static init(scene) {
12
+ Engine.init(scene);
13
+ }
14
+
15
+ static get everyOther() {
16
+ return Engine.everyOther;
17
+ }
18
+
19
+ static addControllerDevice(device) {
20
+ Engine.addControllerDevice(device);
21
+ }
22
+
23
+ static clear() {
24
+ Engine.clear();
25
+ }
26
+
27
+ static colorize(image, fillStyle) {
28
+ return Engine.colorize(image, fillStyle);
29
+ }
30
+
31
+ static fadeOut() {
32
+ Engine.fadeOut();
33
+ }
34
+
35
+ static flip(id) {
36
+ return Engine.flip(id);
37
+ }
38
+
39
+ static getController(id) {
40
+ return Engine.getController(id);
41
+ }
42
+
43
+ static getPointer(id) {
44
+ return Engine.getPointer(id);
45
+ }
46
+
47
+ static image(id, isMirror, isFlip) {
48
+ return Engine.image(id, isMirror, isFlip);
49
+ }
50
+
51
+ static load(namespace) {
52
+ return Engine.load(namespace);
53
+ }
54
+
55
+ static mirror(id) {
56
+ return Engine.mirror(id);
57
+ }
58
+
59
+ static mute() {
60
+ Engine.mute();
61
+ }
62
+
63
+ static paint(renderable, index) {
64
+ Engine.paint(renderable, index);
65
+ }
66
+
67
+ static play(id, volume) {
68
+ Engine.play(id, volume);
69
+ }
70
+
71
+ static playTheme(name) {
72
+ Engine.playTheme(name);
73
+ }
74
+
75
+ static random(max) {
76
+ return Engine.random(max);
77
+ }
78
+
79
+ static rotate(image, degrees) {
80
+ return Engine.rotate(image, degrees);
81
+ }
82
+
83
+ static save(data, namespace) {
84
+ Engine.save(data, namespace);
85
+ }
86
+
87
+ static setAutoScale(autoScale) {
88
+ Engine.setAutoScale(autoScale);
89
+ }
90
+
91
+ static setFrameTime(frameTime) {
92
+ Engine.setFrameTime(frameTime);
93
+ }
94
+
95
+ static setFullScreen(fullScreen) {
96
+ Engine.setFullScreen(fullScreen);
97
+ }
98
+
99
+ static setKeepAspect(keepAspect) {
100
+ Engine.setKeepAspect(keepAspect);
101
+ }
102
+
103
+ static setName(name) {
104
+ Engine.setName(name);
105
+ }
106
+
107
+ static stopTheme() {
108
+ Engine.stopTheme();
109
+ }
110
+
111
+ // factories
112
+ static animation(frames) {
113
+ return new Animation(frames);
114
+ }
115
+
116
+ static frame(image, duration) {
117
+ return new Frame(image, duration);
118
+ }
119
+
120
+ static point(x, y) {
121
+ return new Point(x, y);
122
+ }
123
+
124
+ static rect(x, y, width, height) {
125
+ return new Rect(x, y, width, height);
126
+ }
127
+
128
+ static scene() {
129
+ return new Scene();
130
+ }
131
+
132
+ static sprite(scene) {
133
+ return new Sprite(scene);
134
+ }
135
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ export class Direction {
4
+ constructor() {
5
+ this.bottom = false;
6
+ this.left = false;
7
+ this.right = false;
8
+ this.top = false;
9
+ }
10
+
11
+ setBottom(isBottom = true) {
12
+ this.bottom = isBottom;
13
+ return this;
14
+ }
15
+
16
+ setLeft(isLeft = true) {
17
+ this.left = isLeft;
18
+ return this;
19
+ }
20
+
21
+ setRight(isRight = true) {
22
+ this.right = isRight;
23
+ return this;
24
+ }
25
+
26
+ setTop(isTop = true) {
27
+ this.top = isTop;
28
+ return this;
29
+ }
30
+ }
package/src/Engine.mjs ADDED
@@ -0,0 +1,467 @@
1
+ "use strict";
2
+
3
+ import { ACL } from "./ACL.mjs";
4
+ import { Color } from "./Color.mjs";
5
+ import { CompositeOperations } from "./CompositeOperations.mjs";
6
+ import { Frame } from "./Frame.mjs";
7
+ import { Input } from "./Input.mjs";
8
+ import { Point } from "./Point.mjs";
9
+ import { Rect } from "./Rect.mjs";
10
+ import { RenderableList } from "./RenderableList.mjs";
11
+ import { Scene } from "./Scene.mjs";
12
+ import { Sound } from "./Sound.mjs";
13
+ import { Sprite } from "./Sprite.mjs";
14
+ import { Static } from "./Static.mjs";
15
+
16
+ const CANVAS_ELEMENT = "canvas";
17
+ const CONTEXT = "2d";
18
+ const DEFAULT_FRAME_TIME = 16;
19
+ const DEGRADATION_TOLERANCE = 10;
20
+
21
+ export const Engine = (() => {
22
+ const RENDER_STRATEGY = {
23
+ DEFAULT: () => {
24
+ ACL.window.requestAnimationFrame(render);
25
+ },
26
+
27
+ DEGRADED: () => {
28
+ render();
29
+ },
30
+ };
31
+
32
+ const RENDER_STRATEGIES = [
33
+ RENDER_STRATEGY.DEFAULT,
34
+ RENDER_STRATEGY.DEGRADED,
35
+ ];
36
+
37
+ let _autoScale = true;
38
+ let _canvas = Static.getElement("app") || Static.getElements(CANVAS_ELEMENT)[0];
39
+ let _context = _canvas.getContext(CONTEXT);
40
+ let _degraded = 0;
41
+ let _everyOther = true;
42
+ let _frameTime = DEFAULT_FRAME_TIME;
43
+ let _fullScreen = false;
44
+ let _height = _canvas.height;
45
+ let _imageCache = {};
46
+ let _keepAspect = false;
47
+ let _input = new Input();
48
+ let _lastRender;
49
+ let _name = "Game";
50
+ let _realHeight = _canvas.height;
51
+ let _realWidth = _canvas.width;
52
+ let _renderableLists = [];
53
+ let _renderStrategy = 0;
54
+ let _scene;
55
+ let _sound = new Sound();
56
+ let _transition;
57
+ let _width = _canvas.width;
58
+
59
+ class Engine {
60
+ static init(scene) {
61
+ _scene = scene;
62
+ boot(_canvas, _context);
63
+ }
64
+
65
+ static get bottom() {
66
+ return _height - 1;
67
+ }
68
+
69
+ static get center() {
70
+ return new Point(this.centerX, this.centerY);
71
+ }
72
+
73
+ static get centerX() {
74
+ return Math.floor(_width / 2);
75
+ }
76
+
77
+ static get centerY() {
78
+ return Math.floor(_height / 2);
79
+ }
80
+
81
+ static get height() {
82
+ return _height;
83
+ }
84
+
85
+ static getName() {
86
+ return _name;
87
+ }
88
+
89
+ static get offsetLeft() {
90
+ return _canvas.offsetLeft;
91
+ }
92
+
93
+ static get offsetTop() {
94
+ return _canvas.offsetTop;
95
+ }
96
+
97
+ static get right() {
98
+ return _width - 1;
99
+ }
100
+
101
+ static get everyOther() {
102
+ return _everyOther;
103
+ }
104
+
105
+ static get frameTime() {
106
+ return _frameTime;
107
+ }
108
+
109
+ static get realHeight() {
110
+ return _realHeight;
111
+ }
112
+
113
+ static get realWidth() {
114
+ return _realWidth;
115
+ }
116
+
117
+ static get width() {
118
+ return _width;
119
+ }
120
+
121
+ static addControllerDevice(device) {
122
+ _input.addController(device);
123
+ }
124
+
125
+ static clear() {
126
+ _context.clearRect(0, 0, _width, _height);
127
+ }
128
+
129
+ static colorize(image, fillStyle, compositeOperation = CompositeOperations.SOURCE_IN) {
130
+ const input = Static.getImage(image);
131
+ const output = ACL.document.createElement(CANVAS_ELEMENT);
132
+ output.width = input.width;
133
+ output.height = input.height;
134
+ const context = output.getContext(CONTEXT);
135
+ context.drawImage(input, 0, 0);
136
+ context.globalCompositeOperation = compositeOperation;
137
+ context.fillStyle = fillStyle;
138
+ context.fillRect(0, 0, output.width, output.height);
139
+ return output;
140
+ }
141
+
142
+ static fadeOut() {
143
+ _sound.fadeOut();
144
+ }
145
+
146
+ static flip(image) {
147
+ return invert(image, false, true);
148
+ }
149
+
150
+ static getController(id) {
151
+ return _input.getController(id);
152
+ }
153
+
154
+ static get controller() {
155
+ return this.getController();
156
+ }
157
+
158
+ static getPointer(id) {
159
+ return _input.getPointer(id);
160
+ }
161
+
162
+ static get pointer() {
163
+ return this.getPointer();
164
+ }
165
+
166
+ static load(namespace) {
167
+ const container = localStorage[namespace ?? Engine.getName()];
168
+ let result;
169
+
170
+ try {
171
+ result = container && JSON.parse(container);
172
+ } catch (error) {
173
+ console.log("Could not load saved game: " + error);
174
+ }
175
+
176
+ return result;
177
+ }
178
+
179
+ static mirror(image) {
180
+ return invert(image, true, false);
181
+ }
182
+
183
+ static mute() {
184
+ _sound.mute();
185
+ }
186
+
187
+ static paint(renderable, index = 0) {
188
+ if (index >= _renderableLists.length) {
189
+ for (let i = _renderableLists.length; i <= index; ++i) {
190
+ _renderableLists.push(new RenderableList());
191
+ }
192
+ }
193
+
194
+ _renderableLists[index].add(renderable);
195
+ }
196
+
197
+ static play(id, volume) {
198
+ _sound.play(id, volume);
199
+ }
200
+
201
+ static playTheme(name) {
202
+ _sound.playTheme(name);
203
+ }
204
+
205
+ static random(max) {
206
+ return Math.floor(Math.random() * (max + 1));
207
+ }
208
+
209
+ static rotate(image, degrees) {
210
+ const input = Static.getImage(image);
211
+ degrees = degrees % 360;
212
+
213
+ if (degrees == 0 ) {
214
+ return input;
215
+ }
216
+
217
+ const output = ACL.document.createElement(CANVAS_ELEMENT);
218
+ output.width = input.width;
219
+ output.height = input.height;
220
+ const context = output.getContext(CONTEXT);
221
+ context.translate(output.width / 2, output.height / 2);
222
+ context.rotate(Static.toRadians(degrees));
223
+ context.drawImage(input, -input.width / 2, -input.height / 2);
224
+ return output;
225
+ }
226
+
227
+ static save(data, namespace) {
228
+ try {
229
+ localStorage[namespace || Engine.getName()] = data && JSON.stringify(data);
230
+ } catch (error) {
231
+ console.log("Could not save current game: " + error);
232
+ }
233
+ }
234
+
235
+ static setAutoScale(customAutoScale = true) {
236
+ _autoScale = customAutoScale;
237
+ }
238
+
239
+ static setFrameTime(frameTime) {
240
+ _frameTime = frameTime ?? DEFAULT_FRAME_TIME;
241
+ }
242
+
243
+ static setFullScreen(customFullScreen = true) {
244
+ _fullScreen = customFullScreen;
245
+ }
246
+
247
+ static setKeepAspect(customKeepAspect = true) {
248
+ _keepAspect = customKeepAspect;
249
+ }
250
+
251
+ static setName(name) {
252
+ _name = name;
253
+ document && (ACL.document.title = _name);
254
+ }
255
+
256
+ static stopTheme() {
257
+ _sound.stopTheme();
258
+ }
259
+
260
+ // factories
261
+ static animation(frames) {
262
+ return new Animation(frames);
263
+ }
264
+
265
+ static frame(image, duration) {
266
+ return new Frame(image, duration);
267
+ }
268
+
269
+ static image(id, isMirror, isFlip) {
270
+ if (isFlip && isMirror) {
271
+ if (_imageCache[id] && _imageCache[id].flipMirror) {
272
+ return _imageCache[id].flipMirror;
273
+ }
274
+
275
+ _imageCache[id] = _imageCache[id] || {};
276
+ _imageCache[id].flipMirror = invert(Static.getElement(id), true, true);
277
+ return _imageCache[id].flip;
278
+ }
279
+
280
+ if (isFlip) {
281
+ if (_imageCache[id] && _imageCache[id].flip) {
282
+ return _imageCache[id].flip;
283
+ }
284
+
285
+ _imageCache[id] = _imageCache[id] || {};
286
+ _imageCache[id].flip = Engine.flip(Static.getElement(id));
287
+ return _imageCache[id].flip;
288
+ }
289
+
290
+ if (isMirror) {
291
+ if (_imageCache[id] && _imageCache[id].mirror) {
292
+ return _imageCache[id].mirror;
293
+ }
294
+
295
+ _imageCache[id] = _imageCache[id] || {};
296
+ _imageCache[id].mirror = Engine.mirror(Static.getElement(id));
297
+ return _imageCache[id].mirror;
298
+ }
299
+
300
+ return Static.getElement(id);
301
+ }
302
+
303
+ static point(x, y) {
304
+ return new Point(x, y);
305
+ }
306
+
307
+ static rect(x, y, width, height) {
308
+ return new Rect(x, y, width, height);
309
+ }
310
+
311
+ static scene() {
312
+ return new Scene();
313
+ }
314
+
315
+ static sprite(scene) {
316
+ return new Sprite(scene);
317
+ }
318
+ }
319
+
320
+ function boot(canvas, context) {
321
+ _canvas.style.transform = "translateZ(0)";
322
+ addEventListener("blur", focus, false);
323
+ addEventListener("click", focus, false);
324
+ addEventListener("focus", focus, false);
325
+ addEventListener("load", focus, false);
326
+ addEventListener("resize", scale, false);
327
+ // focus(); // TODO: test all platforms before finally removing it
328
+ scale();
329
+ const images = Static.getElements("img");
330
+ const total = images.length;
331
+ let complete = 0;
332
+
333
+ for (let i = 0; i < images.length; ++i) {
334
+ const IMAGE = images[i];
335
+
336
+ if (IMAGE.complete) {
337
+ ++complete;
338
+ }
339
+ }
340
+
341
+ context.fillStyle = Color.Blue;
342
+ context.fillRect(0, 0, canvas.width * complete / total, canvas.height);
343
+
344
+ if (complete < total) {
345
+ setTimeout(() => {
346
+ boot(canvas, context);
347
+ }, 100);
348
+
349
+ return;
350
+ }
351
+
352
+ initScene();
353
+ _lastRender = Date.now();
354
+ loop();
355
+ }
356
+
357
+ function focus() {
358
+ ACL.window.focus();
359
+
360
+ if (_fullScreen && _canvas.requestFullscreen) {
361
+ _canvas.requestFullscreen().catch((error) => {
362
+ console.warn("Could not request full screen", error);
363
+ });
364
+ }
365
+ }
366
+
367
+ function initScene() {
368
+ if (!_scene) {
369
+ throw new Error("Could not get the next scene");
370
+ }
371
+
372
+ _scene.scene = new Point();
373
+ _scene.height = _scene.height || _height;
374
+ _scene.width = _scene.width || _width;
375
+ _scene.init();
376
+ }
377
+
378
+ function invert(image, isMirror, isFlip) {
379
+ const input = Static.getImage(image);
380
+ const output = ACL.document.createElement(CANVAS_ELEMENT);
381
+ output.width = input.width;
382
+ output.height = input.height;
383
+ const context = output.getContext(CONTEXT);
384
+ context.translate(isMirror ? output.width : 0, isFlip ? output.height : 0);
385
+ context.scale(isMirror ? -1 : 1, isFlip ? - 1 : 1);
386
+ context.drawImage(input, 0, 0);
387
+ return output;
388
+ }
389
+
390
+ function loop() {
391
+ _everyOther = !_everyOther;
392
+ _input.update();
393
+
394
+ if (_transition != null) {
395
+ if (_transition.sync()) {
396
+ _transition = null;
397
+ initScene();
398
+ } else {
399
+ Engine.paint(_transition);
400
+ }
401
+ } else {
402
+ if (_scene.sync()) {
403
+ _transition = _scene.transition;
404
+
405
+ if (_transition) {
406
+ _transition.scene = _scene;
407
+ _transition.init();
408
+ }
409
+
410
+ _scene = _scene.next;
411
+
412
+ if (!_transition) {
413
+ initScene();
414
+ }
415
+ }
416
+ }
417
+
418
+ _sound.update();
419
+ RENDER_STRATEGIES[_renderStrategy]();
420
+ }
421
+
422
+ function render() {
423
+ for (let i = 0; i < _renderableLists.length; ++i) {
424
+ _renderableLists[i].render(_context);
425
+ }
426
+
427
+ const timeout = _frameTime + _lastRender - Date.now();
428
+
429
+ if (timeout < 0 && ++_degraded > DEGRADATION_TOLERANCE) {
430
+ if (++_renderStrategy > RENDER_STRATEGIES.length - 1) {
431
+ _renderStrategy = 0;
432
+ }
433
+ }
434
+
435
+ setTimeout(loop, timeout);
436
+ _lastRender = Date.now();
437
+ }
438
+
439
+ function scale() {
440
+ if (!_autoScale) {
441
+ return;
442
+ }
443
+
444
+ let width, height;
445
+
446
+ if (_keepAspect) {
447
+ let proportion = ACL.window.innerWidth / _canvas.width;
448
+
449
+ if (ACL.window.innerHeight < _canvas.height * proportion) {
450
+ proportion = ACL.window.innerHeight / _canvas.height;
451
+ }
452
+
453
+ width = _canvas.width * proportion;
454
+ height = _canvas.height * proportion;
455
+ } else {
456
+ width = ACL.window.innerWidth;
457
+ height = ACL.window.innerHeight;
458
+ }
459
+
460
+ _realWidth = width;
461
+ _realHeight = height;
462
+ _canvas.style.width = width + "px";
463
+ _canvas.style.height = height + "px";
464
+ }
465
+
466
+ return Engine;
467
+ })();
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+
3
+ export const FontFamily = {
4
+ Cursive: "cursive",
5
+ Fantasy: "fantasy",
6
+ Monospace: "monospace",
7
+ SansSerif: "sans-serif",
8
+ Serif: "serif",
9
+ };
package/src/Frame.mjs ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ import { Static } from "./Static.mjs";
4
+
5
+ export class Frame {
6
+ constructor(image, duration = 0) {
7
+ this._image = Static.getImage(image);
8
+ this._duration = duration;
9
+ }
10
+
11
+ get image() {
12
+ return this._image;
13
+ }
14
+
15
+ get duration() {
16
+ return this._duration;
17
+ }
18
+
19
+ get width() {
20
+ return this._image.width;
21
+ }
22
+
23
+ get height() {
24
+ return this._image.height;
25
+ }
26
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ import { Axis } from "./Axis.mjs";
4
+ import { Command } from "./Command.mjs";
5
+ import { ButtonLayout } from "./ButtonLayout.mjs";
6
+ import { ButtonLayoutMap } from "./ButtonLayoutMap.mjs";
7
+ import { Input } from "./Input.mjs";
8
+ import { Static } from "./Static.mjs";
9
+
10
+ export class GamePad {
11
+ constructor(id = 0) {
12
+ this._id = id;
13
+ this.analogThreshold = 0.5;
14
+ this.layout = ButtonLayout.standard;
15
+ const MODEL = Static.getGamepads()[id].id.toLowerCase();
16
+
17
+ for (let id in ButtonLayoutMap) {
18
+ if (MODEL.includes(id)) {
19
+ this.layout = ButtonLayout[ButtonLayoutMap[id]];
20
+ }
21
+ }
22
+ }
23
+
24
+ get commands() {
25
+ const BUTTONS = Input.getGamePadButtons(this._id);
26
+ const RESULT = {};
27
+
28
+ if (Input.getGamePadAxes(this._id)[Axis.LEFT_Y] < - this.analogThreshold) {
29
+ RESULT[Command.UP] = true;
30
+ } else if (Input.getGamePadAxes(this._id)[Axis.LEFT_Y] > this.analogThreshold) {
31
+ RESULT[Command.DOWN] = true;
32
+ }
33
+
34
+ if (Input.getGamePadAxes(this._id)[Axis.LEFT_X] < - this.analogThreshold) {
35
+ RESULT[Command.LEFT] = true;
36
+ } else if (Input.getGamePadAxes(this._id)[Axis.LEFT_X] > this.analogThreshold) {
37
+ RESULT[Command.RIGHT] = true;
38
+ }
39
+
40
+ for (let i in Command) {
41
+ const BUTTON = BUTTONS[this.layout[i]];
42
+
43
+ if (BUTTON && BUTTON.pressed) {
44
+ RESULT[Command[i]] = true;
45
+ }
46
+ }
47
+
48
+ return RESULT;
49
+ }
50
+ }