thrunium 0.0.0-stage → 1.2.0-rc.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/README.md +198 -2
- package/dist/cli.mjs +1150 -0
- package/dist/thrunium-global.mjs +20 -0
- package/dist/thrunium.js +2241 -0
- package/dist/thrunium.min.js +3 -0
- package/dist/thrunium.min.js.map +7 -0
- package/dist/thrunium.mjs +2 -0
- package/dist/types/ajax.d.ts +47 -0
- package/dist/types/collider.d.ts +130 -0
- package/dist/types/elem.d.ts +241 -0
- package/dist/types/env.d.ts +78 -0
- package/dist/types/game.d.ts +702 -0
- package/dist/types/namespace.d.ts +40 -0
- package/dist/types/reference.d.ts +52 -0
- package/dist/types/sprite.d.ts +348 -0
- package/dist/types/storage-token.d.ts +20 -0
- package/dist/types/storage.d.ts +38 -0
- package/dist/types/vector.d.ts +295 -0
- package/package.json +62 -4
- package/templates/js/game.js +17 -0
- package/templates/js/game.json +1 -0
- package/templates/js/gitignore +2 -0
- package/templates/js/package.json +13 -0
- package/templates/pnpm/pnpm-workspace.yaml +4 -0
- package/templates/ts/game.json +1 -0
- package/templates/ts/game.ts +17 -0
- package/templates/ts/gitignore +2 -0
- package/templates/ts/package.json +14 -0
- package/templates/ts/tsconfig.json +12 -0
package/dist/thrunium.js
ADDED
|
@@ -0,0 +1,2241 @@
|
|
|
1
|
+
/*! THRUNIUM v1.2.0-rc.2 | (c) Infobits ApS | MIT License */
|
|
2
|
+
"use strict";
|
|
3
|
+
(() => {
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/namespace.ts
|
|
11
|
+
window.THRUNIUM || (window.THRUNIUM = {});
|
|
12
|
+
var T = window.THRUNIUM;
|
|
13
|
+
|
|
14
|
+
// src/env.ts
|
|
15
|
+
function pageLoad() {
|
|
16
|
+
T.pageLoaded = true;
|
|
17
|
+
}
|
|
18
|
+
function ready(fn) {
|
|
19
|
+
let fired = false;
|
|
20
|
+
const once = () => {
|
|
21
|
+
if (fired) return;
|
|
22
|
+
fired = true;
|
|
23
|
+
fn();
|
|
24
|
+
};
|
|
25
|
+
if (document.readyState === "complete") {
|
|
26
|
+
once();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
document.addEventListener("DOMContentLoaded", once, false);
|
|
30
|
+
window.addEventListener("load", once, false);
|
|
31
|
+
}
|
|
32
|
+
function throwError(txt) {
|
|
33
|
+
console.error(txt);
|
|
34
|
+
T.error = true;
|
|
35
|
+
}
|
|
36
|
+
function mobileAndTabletcheck() {
|
|
37
|
+
const a = navigator.userAgent || navigator.vendor || "";
|
|
38
|
+
return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4));
|
|
39
|
+
}
|
|
40
|
+
function iOScheck() {
|
|
41
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent) && !("MSStream" in window);
|
|
42
|
+
}
|
|
43
|
+
function getRandom(min, max, float = false) {
|
|
44
|
+
if (float) return Math.random() * (max - min) + min;
|
|
45
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
46
|
+
}
|
|
47
|
+
function initEnv() {
|
|
48
|
+
T.container = null;
|
|
49
|
+
T.gameObject = null;
|
|
50
|
+
T.error = false;
|
|
51
|
+
T.pageLoaded = false;
|
|
52
|
+
T.fontsLoaded = false;
|
|
53
|
+
ready(pageLoad);
|
|
54
|
+
if (typeof document !== "undefined" && document.fonts && document.fonts.ready) {
|
|
55
|
+
void document.fonts.ready.then(() => {
|
|
56
|
+
T.fontsLoaded = true;
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
T.fontsLoaded = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/game.ts
|
|
64
|
+
var _a;
|
|
65
|
+
_a = Symbol.toStringTag;
|
|
66
|
+
var Game = class {
|
|
67
|
+
/**
|
|
68
|
+
* Creates a game: a `<canvas>` with its 2d context inside a container, the game loop and the standard events. The canvas is appended to the page and scaled to fit the window. Call `init()` and `start()` afterwards. Must be called with `new`.
|
|
69
|
+
* @param width Width of the game in pixels.
|
|
70
|
+
* @param height Height of the game in pixels.
|
|
71
|
+
* @example
|
|
72
|
+
* ```js
|
|
73
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
constructor(width, height) {
|
|
77
|
+
this[_a] = "THRUNIUM.Game";
|
|
78
|
+
/**
|
|
79
|
+
* Density of real canvas pixels per game pixel (device pixel ratio times `fitScale`). Adjusted automatically to keep the game sharp. Read-only.
|
|
80
|
+
*
|
|
81
|
+
* @category Variables
|
|
82
|
+
* @example
|
|
83
|
+
* ```js
|
|
84
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
85
|
+
* game.scale;
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
this.scale = 1;
|
|
89
|
+
/**
|
|
90
|
+
* How much the game is scaled to fit the browser window. Adjusted automatically when the window is resized. Read-only.
|
|
91
|
+
*
|
|
92
|
+
* @category Variables
|
|
93
|
+
* @example
|
|
94
|
+
* ```js
|
|
95
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
96
|
+
* game.fitScale;
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
this.fitScale = 1;
|
|
100
|
+
/**
|
|
101
|
+
* Seconds elapsed since the previous frame. Updated every game loop; multiply movement by it to keep speed independent of frame rate.
|
|
102
|
+
*
|
|
103
|
+
* @category Variables
|
|
104
|
+
* @example
|
|
105
|
+
* ```js
|
|
106
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
107
|
+
* game.states['game'] = function() {
|
|
108
|
+
* playerX += 100 * game.delta; // 100 pixels per second
|
|
109
|
+
* };
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
this.delta = 0;
|
|
113
|
+
/**
|
|
114
|
+
* Frames per second of the game loop. Updated every frame. Read-only: an overwritten value is replaced on the next frame.
|
|
115
|
+
*
|
|
116
|
+
* @category Variables
|
|
117
|
+
* @example
|
|
118
|
+
* ```js
|
|
119
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
120
|
+
* game.fps;
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
this.fps = 0;
|
|
124
|
+
// Before 1.2.0 this was the string (fps / 60).toFixed(2); it is now a number.
|
|
125
|
+
/**
|
|
126
|
+
* Current frames per second divided by 60, as a number with two decimals (for example `1`). Use it to correct game speed on devices that run below 60 fps. Updated every frame. Read-only.
|
|
127
|
+
*
|
|
128
|
+
* @category Variables
|
|
129
|
+
* @example
|
|
130
|
+
* ```js
|
|
131
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
132
|
+
* game.runSpeed;
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
this.runSpeed = 0;
|
|
136
|
+
/**
|
|
137
|
+
* Unix timestamp in milliseconds of the last time the game loop ran, or `null` before the first frame. Updated every frame. Read-only.
|
|
138
|
+
*
|
|
139
|
+
* @category Variables
|
|
140
|
+
* @example
|
|
141
|
+
* ```js
|
|
142
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
143
|
+
* game.lastRun;
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
this.lastRun = null;
|
|
147
|
+
/**
|
|
148
|
+
* Whether the game loop is running. `start()` sets it to `true` and `stop()` to `false`; setting it to `false` yourself ends the loop after the current frame.
|
|
149
|
+
*
|
|
150
|
+
* @category Variables
|
|
151
|
+
* @example
|
|
152
|
+
* ```js
|
|
153
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
154
|
+
* game.gameRunning = false; // the loop stops
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
this.gameRunning = false;
|
|
158
|
+
/**
|
|
159
|
+
* Set to `true` to draw an FPS meter in the top-left corner. Default is `false`.
|
|
160
|
+
*
|
|
161
|
+
* @category Variables
|
|
162
|
+
* @example
|
|
163
|
+
* ```js
|
|
164
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
165
|
+
* game.showFPS = true;
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
this.showFPS = false;
|
|
169
|
+
/**
|
|
170
|
+
* Free-form name for the game. The engine does not use it, so it is available for your own bookkeeping, for example to prefix saved data. Default is an empty string.
|
|
171
|
+
*
|
|
172
|
+
* @category Variables
|
|
173
|
+
* @example
|
|
174
|
+
* ```js
|
|
175
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
176
|
+
* game.namespace = 'my-game';
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
this.namespace = "";
|
|
180
|
+
/**
|
|
181
|
+
* X coordinate of the pointer in game coordinates. Updated when the mouse moves or the screen is touched or clicked. Read-only.
|
|
182
|
+
*
|
|
183
|
+
* @category Variables
|
|
184
|
+
* @example
|
|
185
|
+
* ```js
|
|
186
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
187
|
+
* game.mouseX;
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
this.mouseX = 0;
|
|
191
|
+
/**
|
|
192
|
+
* Y coordinate of the pointer in game coordinates. Updated when the mouse moves or the screen is touched or clicked. Read-only.
|
|
193
|
+
*
|
|
194
|
+
* @category Variables
|
|
195
|
+
* @example
|
|
196
|
+
* ```js
|
|
197
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
198
|
+
* game.mouseY;
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
this.mouseY = 0;
|
|
202
|
+
/**
|
|
203
|
+
* Name of the state the game loop is currently running (a key of `states`). It starts as the built-in loading screen, `"startup"`, and switches to `startState` once everything has loaded. Assign a new name to change state.
|
|
204
|
+
*
|
|
205
|
+
* @category Variables
|
|
206
|
+
* @example
|
|
207
|
+
* ```js
|
|
208
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
209
|
+
* game.state = 'game'; // run game.states['game'] each frame
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
this.state = "startup";
|
|
213
|
+
/**
|
|
214
|
+
* Name of the state to run after the loading screen. Used once, so set it before `init()`. Default is `null`, so you have to set it.
|
|
215
|
+
*
|
|
216
|
+
* @category Variables
|
|
217
|
+
* @example
|
|
218
|
+
* ```js
|
|
219
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
220
|
+
* game.startState = 'menu';
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
this.startState = null;
|
|
224
|
+
// Indexed by state name; kept as an array to match 1.0.0.
|
|
225
|
+
/**
|
|
226
|
+
* Your game states: an object of functions keyed by name. The function named by `state` is called every frame. Change `state` to switch between them.
|
|
227
|
+
*
|
|
228
|
+
* @category Variables
|
|
229
|
+
* @example
|
|
230
|
+
* ```js
|
|
231
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
232
|
+
* game.states['game'] = function() {
|
|
233
|
+
* // your code, run every frame
|
|
234
|
+
* };
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
this.states = [];
|
|
238
|
+
/**
|
|
239
|
+
* Background color drawn behind every frame. Accepts any CSS color (hex, rgb, rgba, hsl, hsla). Default is `"#fff"`.
|
|
240
|
+
*
|
|
241
|
+
* @category Variables
|
|
242
|
+
* @example
|
|
243
|
+
* ```js
|
|
244
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
245
|
+
* game.backgroundColor = '#eee';
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
this.backgroundColor = "#fff";
|
|
249
|
+
/**
|
|
250
|
+
* Assets to load on startup. Set it before `init()`. Each entry is either the path of an image or audio file, which is also the key the loaded asset is stored under, or an object `{ src, index, spriteMap? }` with the file path, the key to store it under, and an optional `spriteMap` of `{ width, height }` that loads an image as a `Sprite` sprite map. Supported images are jpg, jpeg, png, gif and xbm; audio is mp3, wav, ogg and aac. After `init()` the array is replaced by an object-like array keyed by each entry's index, holding the loaded element (or `Sprite` for sprite maps).
|
|
251
|
+
*
|
|
252
|
+
* @category Assets
|
|
253
|
+
* @example
|
|
254
|
+
* ```js
|
|
255
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
256
|
+
* // as a string: the path is also the key
|
|
257
|
+
* game.assets = ['images/sprite.png'];
|
|
258
|
+
* // the loading screen runs until every asset has loaded, then switches to startState
|
|
259
|
+
* game.startState = 'game';
|
|
260
|
+
* game.states['game'] = function() {
|
|
261
|
+
* game.drawImage(game.assets['images/sprite.png'], 0, 0);
|
|
262
|
+
* };
|
|
263
|
+
* game.init();
|
|
264
|
+
* game.start();
|
|
265
|
+
* ```
|
|
266
|
+
* @example
|
|
267
|
+
* ```js
|
|
268
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
269
|
+
* // as an object: choose the key, optionally load a sprite map
|
|
270
|
+
* game.assets = [{
|
|
271
|
+
* src: 'images/spritemap.png',
|
|
272
|
+
* index: 'tiles',
|
|
273
|
+
* spriteMap: { width: 40, height: 40 }
|
|
274
|
+
* }];
|
|
275
|
+
* game.startState = 'game';
|
|
276
|
+
* game.states['game'] = function() {
|
|
277
|
+
* // Sprite.draw(frame, x, y): frame 0 centred at (50, 50)
|
|
278
|
+
* game.assets['tiles'].draw(0, 50, 50);
|
|
279
|
+
* };
|
|
280
|
+
* game.init();
|
|
281
|
+
* game.start();
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
// Typed for strict consumers: entries going in are strings or AssetEntry, and
|
|
285
|
+
// after init() the same array is read by key, which the Record part allows.
|
|
286
|
+
this.assets = [];
|
|
287
|
+
/**
|
|
288
|
+
* Whether `loadAssets()` has run, so the entries of `assets` have been turned into loadable elements. Use `loadedAssets` to see how many have actually finished loading.
|
|
289
|
+
*
|
|
290
|
+
* @category Variables
|
|
291
|
+
* @example
|
|
292
|
+
* ```js
|
|
293
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
294
|
+
* game.assetsLoaded; // false until init()
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
this.assetsLoaded = false;
|
|
298
|
+
/**
|
|
299
|
+
* Number of requested assets that have finished loading.
|
|
300
|
+
*
|
|
301
|
+
* @category Variables
|
|
302
|
+
* @example
|
|
303
|
+
* ```js
|
|
304
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
305
|
+
* game.loadedAssets;
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
this.loadedAssets = 0;
|
|
309
|
+
/**
|
|
310
|
+
* Number of assets requested for loading. Set when `loadAssets()` runs; the loading screen is done when `loadedAssets` reaches it.
|
|
311
|
+
*
|
|
312
|
+
* @category Variables
|
|
313
|
+
* @example
|
|
314
|
+
* ```js
|
|
315
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
316
|
+
* game.assetsToLoad;
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
this.assetsToLoad = 0;
|
|
320
|
+
/**
|
|
321
|
+
* Font family used by `drawText`. Default is `"Arial"`.
|
|
322
|
+
*
|
|
323
|
+
* @category Variables
|
|
324
|
+
* @example
|
|
325
|
+
* ```js
|
|
326
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
327
|
+
* game.font = 'Courier New';
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
this.font = "Arial";
|
|
331
|
+
/**
|
|
332
|
+
* Font size used by `drawText`, as a CSS size. Default is `"30px"`.
|
|
333
|
+
*
|
|
334
|
+
* @category Variables
|
|
335
|
+
* @example
|
|
336
|
+
* ```js
|
|
337
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
338
|
+
* game.textSize = '32px';
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
this.textSize = "30px";
|
|
342
|
+
// User-assignable events (own properties, no-ops by default).
|
|
343
|
+
/**
|
|
344
|
+
* Called when the user clicks the game, and when a touch ends on it (`touchend` calls `click` just before its own handler). It updates `mouseX` and `mouseY` first. Assign a function to handle it. Handlers are attached by `init()`, so assign them before calling it.
|
|
345
|
+
* @param evt The mouse click, or the touch event when a touch ended.
|
|
346
|
+
*
|
|
347
|
+
* @category Events
|
|
348
|
+
* @example
|
|
349
|
+
* ```js
|
|
350
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
351
|
+
* game.click = function(e) {
|
|
352
|
+
* // your code here
|
|
353
|
+
* };
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
this.click = () => {
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Called when the user presses a mouse button in the game. It updates `mouseX` and `mouseY` first. Assign a function to handle it. Handlers are attached by `init()`, so assign them before calling it.
|
|
360
|
+
* @param evt The mouse event.
|
|
361
|
+
*
|
|
362
|
+
* @category Events
|
|
363
|
+
* @example
|
|
364
|
+
* ```js
|
|
365
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
366
|
+
* game.mousedown = function(e) {
|
|
367
|
+
* // your code here
|
|
368
|
+
* };
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
this.mousedown = () => {
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Called when the user releases a mouse button in the game. It updates `mouseX` and `mouseY` first. Assign a function to handle it. Handlers are attached by `init()`, so assign them before calling it.
|
|
375
|
+
* @param evt The mouse event.
|
|
376
|
+
*
|
|
377
|
+
* @category Events
|
|
378
|
+
* @example
|
|
379
|
+
* ```js
|
|
380
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
381
|
+
* game.mouseup = function(e) {
|
|
382
|
+
* // your code here
|
|
383
|
+
* };
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
this.mouseup = () => {
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Called when the pointer moves over the game. It updates `mouseX` and `mouseY` first. Assign a function to handle it.
|
|
390
|
+
*
|
|
391
|
+
* @category Events
|
|
392
|
+
* @example
|
|
393
|
+
* ```js
|
|
394
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
395
|
+
* game.mousemove = function(e) {
|
|
396
|
+
* // your code here
|
|
397
|
+
* };
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
this.mousemove = () => {
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Called when the user touches the game with a touchscreen. It updates `mouseX` and `mouseY` first. Assign a function to handle it. The handler is looked up when the event fires, so it can be assigned or changed at any time.
|
|
404
|
+
* @param evt The touch event.
|
|
405
|
+
*
|
|
406
|
+
* @category Events
|
|
407
|
+
* @example
|
|
408
|
+
* ```js
|
|
409
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
410
|
+
* game.touch = function(e) {
|
|
411
|
+
* // your code here
|
|
412
|
+
* };
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
this.touch = () => {
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Called when the user first touches the game with a touchscreen, after `touch`. It updates `mouseX` and `mouseY` first. Assign a function to handle it. The handler is looked up when the event fires, so it can be assigned or changed at any time.
|
|
419
|
+
* @param evt The touch event.
|
|
420
|
+
*
|
|
421
|
+
* @category Events
|
|
422
|
+
* @example
|
|
423
|
+
* ```js
|
|
424
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
425
|
+
* game.touchstart = function(e) {
|
|
426
|
+
* // your code here
|
|
427
|
+
* };
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
this.touchstart = () => {
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Called when the user lifts the touch from a touchscreen, after `click`. It updates `mouseX` and `mouseY` first. Assign a function to handle it. The handler is looked up when the event fires, so it can be assigned or changed at any time.
|
|
434
|
+
* @param evt The touch event.
|
|
435
|
+
*
|
|
436
|
+
* @category Events
|
|
437
|
+
* @example
|
|
438
|
+
* ```js
|
|
439
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
440
|
+
* game.touchend = function(e) {
|
|
441
|
+
* // your code here
|
|
442
|
+
* };
|
|
443
|
+
* ```
|
|
444
|
+
*/
|
|
445
|
+
this.touchend = () => {
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Called when the user presses a key. Assign a function to handle it; find key codes at keycode.info. Attached by `init()`, so assign it first.
|
|
449
|
+
* @param evt The keyboard event; `evt.key` holds the key.
|
|
450
|
+
*
|
|
451
|
+
* @category Events
|
|
452
|
+
* @example
|
|
453
|
+
* ```js
|
|
454
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
455
|
+
* game.keydown = function(e) {
|
|
456
|
+
* // your code here
|
|
457
|
+
* console.log(e.keyCode, e.key);
|
|
458
|
+
* };
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
this.keydown = () => {
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Called when the user releases a key. Assign a function to handle it. Attached by `init()`, so assign it first.
|
|
465
|
+
* @param evt The keyboard event; `evt.key` holds the key.
|
|
466
|
+
*
|
|
467
|
+
* @category Events
|
|
468
|
+
* @example
|
|
469
|
+
* ```js
|
|
470
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
471
|
+
* game.keyup = function(e) {
|
|
472
|
+
* // your code here
|
|
473
|
+
* console.log(e.keyCode, e.key);
|
|
474
|
+
* };
|
|
475
|
+
* ```
|
|
476
|
+
*/
|
|
477
|
+
this.keyup = () => {
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* Called when the orientation of the device changes. Usually only available on phones.
|
|
481
|
+
*
|
|
482
|
+
* @category Events
|
|
483
|
+
* @example
|
|
484
|
+
* ```js
|
|
485
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
486
|
+
* game.deviceorientation = function(e) {
|
|
487
|
+
* // your code here
|
|
488
|
+
* };
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
this.deviceorientation = () => {
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* Called when the device reports motion (acceleration or rotation). Usually only available on phones. Since 1.1.0 this handler is actually attached; 1.0.0 never called it.
|
|
495
|
+
*
|
|
496
|
+
* @category Events
|
|
497
|
+
* @example
|
|
498
|
+
* ```js
|
|
499
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
500
|
+
* game.devicemotion = function(e) {
|
|
501
|
+
* // your code here
|
|
502
|
+
* };
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
505
|
+
this.devicemotion = () => {
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* Called after the game has refitted itself when the window is resized. The name is misspelled on purpose and is kept for compatibility.
|
|
509
|
+
*
|
|
510
|
+
* @category Events
|
|
511
|
+
* @example
|
|
512
|
+
* ```js
|
|
513
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
514
|
+
* game.rezise = function(e) {
|
|
515
|
+
* // your code here
|
|
516
|
+
* };
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
this.rezise = () => {
|
|
520
|
+
};
|
|
521
|
+
// Arrow fields so they can be passed straight to addEventListener while
|
|
522
|
+
// always dispatching to the CURRENT user handler.
|
|
523
|
+
/**
|
|
524
|
+
* Internal listener that forwards to `deviceorientation`.
|
|
525
|
+
*
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
528
|
+
this._deviceorientation = (evt) => {
|
|
529
|
+
this.deviceorientation(evt);
|
|
530
|
+
};
|
|
531
|
+
/**
|
|
532
|
+
* Internal listener that forwards to `devicemotion`.
|
|
533
|
+
*
|
|
534
|
+
* @internal
|
|
535
|
+
*/
|
|
536
|
+
this._devicemotion = (evt) => {
|
|
537
|
+
this.devicemotion(evt);
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Internal listener for mouse and touch movement.
|
|
541
|
+
*
|
|
542
|
+
* @internal
|
|
543
|
+
*/
|
|
544
|
+
this._mousetouchmove = (evt) => {
|
|
545
|
+
evt.preventDefault();
|
|
546
|
+
this.getMouseCords(evt);
|
|
547
|
+
this.mousemove(evt);
|
|
548
|
+
};
|
|
549
|
+
/**
|
|
550
|
+
* Internal listener for touch start.
|
|
551
|
+
*
|
|
552
|
+
* @internal
|
|
553
|
+
*/
|
|
554
|
+
this._touchstart = (evt) => {
|
|
555
|
+
evt.preventDefault();
|
|
556
|
+
this.getMouseCords(evt);
|
|
557
|
+
this.touch(evt);
|
|
558
|
+
this.touchstart(evt);
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* Internal listener for touch end.
|
|
562
|
+
*
|
|
563
|
+
* @internal
|
|
564
|
+
*/
|
|
565
|
+
this._touchend = (evt) => {
|
|
566
|
+
evt.preventDefault();
|
|
567
|
+
this.getMouseCords(evt);
|
|
568
|
+
this.click(evt);
|
|
569
|
+
this.touchend(evt);
|
|
570
|
+
};
|
|
571
|
+
/**
|
|
572
|
+
* Internal window resize listener.
|
|
573
|
+
*
|
|
574
|
+
* @internal
|
|
575
|
+
*/
|
|
576
|
+
this._resize = (evt) => {
|
|
577
|
+
var _a3, _b;
|
|
578
|
+
this.updateFitScale();
|
|
579
|
+
this.updateScale();
|
|
580
|
+
const elems = (_b = (_a3 = T.Elem) == null ? void 0 : _a3.elemsList) != null ? _b : [];
|
|
581
|
+
for (let i = 0; i < elems.length; i++) elems[i].resize();
|
|
582
|
+
this.rezise(evt);
|
|
583
|
+
};
|
|
584
|
+
// Arrow field: used as an onload handler, so it must keep `this` = the Game.
|
|
585
|
+
/**
|
|
586
|
+
* Internal onload handler that counts a loaded asset.
|
|
587
|
+
*
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
this.assetLoaded = () => {
|
|
591
|
+
this.loadedAssets++;
|
|
592
|
+
};
|
|
593
|
+
// A plain `function` property on purpose: as an onload handler `this` is the
|
|
594
|
+
// image element, and the Game is reached through the captured `self`.
|
|
595
|
+
/**
|
|
596
|
+
* Internal onload handler that turns a loaded sprite-map image into a `Sprite`.
|
|
597
|
+
*
|
|
598
|
+
* @internal
|
|
599
|
+
*/
|
|
600
|
+
this.initAssetSpriteMap = /* @__PURE__ */ ((self) => function() {
|
|
601
|
+
const assets = self.assets;
|
|
602
|
+
const Sprite2 = T.Sprite;
|
|
603
|
+
const sprite = new Sprite2(this);
|
|
604
|
+
assets[this.dataset.index] = sprite;
|
|
605
|
+
sprite.setSpriteMap(Number(this.dataset.spriteWidth), Number(this.dataset.spriteHeight));
|
|
606
|
+
self.loadedAssets++;
|
|
607
|
+
})(this);
|
|
608
|
+
// Arrow field so it can be handed straight to requestAnimationFrame.
|
|
609
|
+
/**
|
|
610
|
+
* One frame of the game loop.
|
|
611
|
+
*
|
|
612
|
+
* @internal
|
|
613
|
+
*/
|
|
614
|
+
this.gameLoop = () => {
|
|
615
|
+
if (!this.lastRun) {
|
|
616
|
+
this.lastRun = Date.now();
|
|
617
|
+
this.requestFrame(this.gameLoop);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
this.delta = (Date.now() - this.lastRun) / 1e3;
|
|
621
|
+
this.lastRun = Date.now();
|
|
622
|
+
this.fps = 1 / this.delta;
|
|
623
|
+
this.runSpeed = Math.round(this.fps / 60 * 100) / 100;
|
|
624
|
+
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
625
|
+
this.canvas.style.cursor = "default";
|
|
626
|
+
this.drawRect(0, 0, this.width, this.height);
|
|
627
|
+
this.setFillColor(this.backgroundColor);
|
|
628
|
+
this.fill();
|
|
629
|
+
if (this.state == "startup") this.startup();
|
|
630
|
+
else this.states[this.state]();
|
|
631
|
+
if (this.showFPS) this.drawFPS();
|
|
632
|
+
if (this.gameRunning && !T.error) this.requestFrame(this.gameLoop);
|
|
633
|
+
};
|
|
634
|
+
this.width = width;
|
|
635
|
+
this.height = height;
|
|
636
|
+
this.contruct();
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Updates `mouseX` and `mouseY` (in game coordinates) from a mouse or touch event. The engine calls it for you before the mouse and touch handlers run, so you only need it to read the position of an event you handle yourself. Since 1.1.0 it uses this game instead of a global variable named `game`, and handles touch-end events that have no `touches`.
|
|
640
|
+
* @param evt The mouse or touch event to read the position from.
|
|
641
|
+
* @example
|
|
642
|
+
* ```js
|
|
643
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
644
|
+
* game.canvas.addEventListener('pointerdown', function(e) {
|
|
645
|
+
* game.getMouseCords(e);
|
|
646
|
+
* console.log(game.mouseX, game.mouseY);
|
|
647
|
+
* });
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
getMouseCords(evt) {
|
|
651
|
+
let point = evt;
|
|
652
|
+
if (point.clientX === void 0) {
|
|
653
|
+
const te = evt;
|
|
654
|
+
if (te.touches && te.touches[0]) point = te.touches[0];
|
|
655
|
+
else if (te.changedTouches && te.changedTouches[0]) point = te.changedTouches[0];
|
|
656
|
+
}
|
|
657
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
658
|
+
const sx = this.canvas.scrollWidth / this.width;
|
|
659
|
+
const sy = this.canvas.scrollHeight / this.height;
|
|
660
|
+
this.mouseX = (point.clientX - rect.left) / sx;
|
|
661
|
+
this.mouseY = (point.clientY - rect.top) / sy;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Sizes the canvas backing store for the device pixel ratio and fit scale.
|
|
665
|
+
*
|
|
666
|
+
* @internal
|
|
667
|
+
*/
|
|
668
|
+
updateScale() {
|
|
669
|
+
this.scale = window.devicePixelRatio * this.fitScale;
|
|
670
|
+
this.canvas.width = this.width * this.scale;
|
|
671
|
+
this.canvas.height = this.height * this.scale;
|
|
672
|
+
this.ctx.scale(this.scale, this.scale);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Fits the canvas and container to the window, preserving aspect ratio.
|
|
676
|
+
*
|
|
677
|
+
* @internal
|
|
678
|
+
*/
|
|
679
|
+
updateFitScale() {
|
|
680
|
+
if (this.width / this.height > window.innerWidth / window.innerHeight) {
|
|
681
|
+
this.fitScale = window.innerWidth / this.width;
|
|
682
|
+
} else {
|
|
683
|
+
this.fitScale = window.innerHeight / this.height;
|
|
684
|
+
}
|
|
685
|
+
const w = this.width * this.fitScale + "px";
|
|
686
|
+
const h = this.height * this.fitScale + "px";
|
|
687
|
+
this.canvas.style.width = w;
|
|
688
|
+
this.canvas.style.height = h;
|
|
689
|
+
const container = T.container;
|
|
690
|
+
container.style.width = w;
|
|
691
|
+
container.style.height = h;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Creates the container and canvas and hooks the window resize event. The constructor calls it, so you do not need to. The name is misspelled on purpose and is kept for compatibility.
|
|
695
|
+
* @example
|
|
696
|
+
* ```js
|
|
697
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
698
|
+
* // the constructor has already called game.contruct()
|
|
699
|
+
* ```
|
|
700
|
+
*/
|
|
701
|
+
contruct() {
|
|
702
|
+
document.body.style.background = "#333";
|
|
703
|
+
const container = document.createElement("div");
|
|
704
|
+
container.style.position = "relative";
|
|
705
|
+
container.style.display = "block";
|
|
706
|
+
container.style.margin = "auto";
|
|
707
|
+
T.container = container;
|
|
708
|
+
T.gameObject = this;
|
|
709
|
+
document.body.appendChild(container);
|
|
710
|
+
this.canvas = document.createElement("canvas");
|
|
711
|
+
container.appendChild(this.canvas);
|
|
712
|
+
this.ctx = this.canvas.getContext("2d");
|
|
713
|
+
this.canvas.style.display = "block";
|
|
714
|
+
this.canvas.style.margin = "auto";
|
|
715
|
+
this._resize();
|
|
716
|
+
window.addEventListener("resize", this._resize, false);
|
|
717
|
+
this.ctx.imageSmoothingEnabled = false;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Initializes the game: loads `assets`, fits the canvas to the window and attaches the event handlers as they are at this moment. Assign your handlers and `assets` before calling it, and call it before `start()`.
|
|
721
|
+
* @example
|
|
722
|
+
* ```js
|
|
723
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
724
|
+
* game.startState = 'menu';
|
|
725
|
+
* game.init();
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
init() {
|
|
729
|
+
if (!this.assetsLoaded) this.loadAssets();
|
|
730
|
+
this._resize();
|
|
731
|
+
this.canvas.addEventListener("click", this.click, false);
|
|
732
|
+
this.canvas.addEventListener("mousedown", this.mousedown, false);
|
|
733
|
+
this.canvas.addEventListener("mouseup", this.mouseup, false);
|
|
734
|
+
this.canvas.addEventListener("touchstart", this._touchstart, false);
|
|
735
|
+
this.canvas.addEventListener("touchend", this._touchend, false);
|
|
736
|
+
document.addEventListener("keydown", this.keydown, false);
|
|
737
|
+
document.addEventListener("keyup", this.keyup, false);
|
|
738
|
+
this.canvas.addEventListener("touchmove", this._mousetouchmove, false);
|
|
739
|
+
this.canvas.addEventListener("mousemove", this._mousetouchmove, false);
|
|
740
|
+
window.addEventListener("deviceorientation", this._deviceorientation);
|
|
741
|
+
window.addEventListener("devicemotion", this._devicemotion);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Creates an `Image` or `Audio` element for every entry of `assets` and starts loading it, then replaces `assets` with an object-like array keyed by each entry's index. `init()` calls it for you, so you rarely need it directly. Unknown entries and file types are logged to the console and skipped.
|
|
745
|
+
*
|
|
746
|
+
* @category Assets
|
|
747
|
+
* @example
|
|
748
|
+
* ```js
|
|
749
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
750
|
+
* game.assets = ['sounds/jump.wav'];
|
|
751
|
+
* game.loadAssets();
|
|
752
|
+
* game.assets['sounds/jump.wav'].play();
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
755
|
+
loadAssets() {
|
|
756
|
+
this.assetsToLoad = this.assets.length;
|
|
757
|
+
const tempAssets = [];
|
|
758
|
+
const byIndex = tempAssets;
|
|
759
|
+
for (let i = 0; i < this.assets.length; i++) {
|
|
760
|
+
const entry = this.assets[i];
|
|
761
|
+
let index;
|
|
762
|
+
let src;
|
|
763
|
+
let spriteMap = null;
|
|
764
|
+
if (typeof entry === "string") {
|
|
765
|
+
src = entry;
|
|
766
|
+
index = src;
|
|
767
|
+
} else if (typeof entry === "object" && entry !== null) {
|
|
768
|
+
const spec = entry;
|
|
769
|
+
src = spec.src;
|
|
770
|
+
index = spec.index;
|
|
771
|
+
if (spec.spriteMap !== void 0) spriteMap = spec.spriteMap;
|
|
772
|
+
} else {
|
|
773
|
+
console.error("ASSETS - Unknown entry");
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
const fileExtension = this.getFileExtension(src);
|
|
777
|
+
byIndex[index] = null;
|
|
778
|
+
let el;
|
|
779
|
+
if (this.isImage(fileExtension)) el = new Image();
|
|
780
|
+
else if (this.isAudio(fileExtension)) el = new Audio();
|
|
781
|
+
else {
|
|
782
|
+
console.error("ASSETS - Unknown file type: " + fileExtension + " for file " + src);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
byIndex[index] = el;
|
|
786
|
+
el.dataset.index = index;
|
|
787
|
+
if (spriteMap !== null) {
|
|
788
|
+
el.dataset.spriteWidth = String(spriteMap.width);
|
|
789
|
+
el.dataset.spriteHeight = String(spriteMap.height);
|
|
790
|
+
}
|
|
791
|
+
el.onload = spriteMap !== null ? this.initAssetSpriteMap : this.assetLoaded;
|
|
792
|
+
el.src = src;
|
|
793
|
+
}
|
|
794
|
+
this.assets = tempAssets;
|
|
795
|
+
this.assetsLoaded = true;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Returns the text after the last dot of a file name, or `undefined`.
|
|
799
|
+
*
|
|
800
|
+
* @internal
|
|
801
|
+
*/
|
|
802
|
+
getFileExtension(filename) {
|
|
803
|
+
return /[.]/.exec(filename) ? /[^.]+$/.exec(filename)[0] : void 0;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Whether a file extension is a supported image type.
|
|
807
|
+
*
|
|
808
|
+
* @internal
|
|
809
|
+
*/
|
|
810
|
+
isImage(ext) {
|
|
811
|
+
return ["jpg", "jpeg", "png", "gif", "xbm"].includes(ext);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Whether a file extension is a supported audio type.
|
|
815
|
+
*
|
|
816
|
+
* @internal
|
|
817
|
+
*/
|
|
818
|
+
isAudio(ext) {
|
|
819
|
+
return ["mp3", "wav", "ogg", "aac"].includes(ext);
|
|
820
|
+
}
|
|
821
|
+
// color
|
|
822
|
+
/**
|
|
823
|
+
* Sets the fill color used by `fill()` and `drawText`.
|
|
824
|
+
*
|
|
825
|
+
* @category Drawing
|
|
826
|
+
* @param color Any CSS color: hex, rgb, rgba, hsl, hsla or a name.
|
|
827
|
+
* @example
|
|
828
|
+
* ```js
|
|
829
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
830
|
+
* game.setFillColor('#f00');
|
|
831
|
+
* game.drawRect(10, 10, 50, 50);
|
|
832
|
+
* game.fill();
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
setFillColor(color) {
|
|
836
|
+
this.ctx.fillStyle = color;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Fills the current path (for example from `drawRect` or `drawCircle`) with the fill color.
|
|
840
|
+
*
|
|
841
|
+
* @category Drawing
|
|
842
|
+
* @example
|
|
843
|
+
* ```js
|
|
844
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
845
|
+
* game.setFillColor('#0a0');
|
|
846
|
+
* game.drawCircle(100, 100, 20);
|
|
847
|
+
* game.fill();
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
fill() {
|
|
851
|
+
this.ctx.fill();
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Sets the stroke color used by `stroke()` and `drawLine`.
|
|
855
|
+
*
|
|
856
|
+
* @category Drawing
|
|
857
|
+
* @param color Any CSS color: hex, rgb, rgba, hsl, hsla or a name.
|
|
858
|
+
* @example
|
|
859
|
+
* ```js
|
|
860
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
861
|
+
* game.setStrokeColor('#00f');
|
|
862
|
+
* game.drawLine(0, 0, 100, 100);
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
setStrokeColor(color) {
|
|
866
|
+
this.ctx.strokeStyle = color;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Strokes the outline of the current path with the stroke color.
|
|
870
|
+
*
|
|
871
|
+
* @category Drawing
|
|
872
|
+
* @example
|
|
873
|
+
* ```js
|
|
874
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
875
|
+
* game.setStrokeColor('#000');
|
|
876
|
+
* game.drawRect(10, 10, 50, 50);
|
|
877
|
+
* game.stroke();
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
stroke() {
|
|
881
|
+
this.ctx.stroke();
|
|
882
|
+
}
|
|
883
|
+
// draw
|
|
884
|
+
/**
|
|
885
|
+
* Draws a line from (x1, y1) to (x2, y2) with the stroke color. When `width` is given it sets the canvas `lineWidth`; like all canvas state it then persists for later strokes until changed (since 1.1.0; 1.0.0 ignored `width`).
|
|
886
|
+
*
|
|
887
|
+
* @category Drawing
|
|
888
|
+
* @param x1 X coordinate of the start.
|
|
889
|
+
* @param y1 Y coordinate of the start.
|
|
890
|
+
* @param x2 X coordinate of the end.
|
|
891
|
+
* @param y2 Y coordinate of the end.
|
|
892
|
+
* @param width Line width in pixels. Leave out to keep the current width.
|
|
893
|
+
* @example
|
|
894
|
+
* ```js
|
|
895
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
896
|
+
* game.drawLine(100, 100, 200, 200);
|
|
897
|
+
* game.drawLine(100, 200, 200, 100, 4); // 4 pixels wide, and later lines stay 4 wide
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
drawLine(x1, y1, x2, y2, width) {
|
|
901
|
+
this.ctx.beginPath();
|
|
902
|
+
if (width) this.ctx.lineWidth = width;
|
|
903
|
+
this.ctx.moveTo(x1, y1);
|
|
904
|
+
this.ctx.lineTo(x2, y2);
|
|
905
|
+
this.ctx.stroke();
|
|
906
|
+
this.ctx.closePath();
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Draws text at (x, y) using `font` and `textSize` and the fill color.
|
|
910
|
+
*
|
|
911
|
+
* @category Drawing
|
|
912
|
+
* @param text The text to draw.
|
|
913
|
+
* @param x X coordinate of the text.
|
|
914
|
+
* @param y Y coordinate of the text.
|
|
915
|
+
* @example
|
|
916
|
+
* ```js
|
|
917
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
918
|
+
* game.drawText('Hello World!', 100, 100);
|
|
919
|
+
* ```
|
|
920
|
+
*/
|
|
921
|
+
drawText(text, x, y) {
|
|
922
|
+
this.ctx.font = this.textSize + " " + this.font;
|
|
923
|
+
this.ctx.fillText(text, x, y);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Traces a rectangle path, with rounded corners if `radius` is given. It only builds the path: call `fill()` or `stroke()` afterwards to draw it.
|
|
927
|
+
*
|
|
928
|
+
* @category Drawing
|
|
929
|
+
* @param x X coordinate of the left edge.
|
|
930
|
+
* @param y Y coordinate of the top edge.
|
|
931
|
+
* @param width Width of the rectangle.
|
|
932
|
+
* @param height Height of the rectangle.
|
|
933
|
+
* @param radius Corner radius. Leave out for square corners.
|
|
934
|
+
* @example
|
|
935
|
+
* ```js
|
|
936
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
937
|
+
* game.drawRect(100, 100, 20, 20);
|
|
938
|
+
* game.fill();
|
|
939
|
+
* ```
|
|
940
|
+
*/
|
|
941
|
+
drawRect(x, y, width, height, radius) {
|
|
942
|
+
const ctx = this.ctx;
|
|
943
|
+
ctx.beginPath();
|
|
944
|
+
if (radius) {
|
|
945
|
+
ctx.moveTo(x + radius, y);
|
|
946
|
+
ctx.lineTo(x + width - radius, y);
|
|
947
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
948
|
+
ctx.lineTo(x + width, y + height - radius);
|
|
949
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
950
|
+
ctx.lineTo(x + radius, y + height);
|
|
951
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
952
|
+
ctx.lineTo(x, y + radius);
|
|
953
|
+
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
954
|
+
} else {
|
|
955
|
+
ctx.rect(x, y, width, height);
|
|
956
|
+
}
|
|
957
|
+
ctx.closePath();
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Traces a circle path. It only builds the path: call `fill()` or `stroke()` afterwards to draw it.
|
|
961
|
+
*
|
|
962
|
+
* @category Drawing
|
|
963
|
+
* @param x X coordinate of the center.
|
|
964
|
+
* @param y Y coordinate of the center.
|
|
965
|
+
* @param radius Radius of the circle.
|
|
966
|
+
* @example
|
|
967
|
+
* ```js
|
|
968
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
969
|
+
* game.drawCircle(100, 100, 20);
|
|
970
|
+
* game.fill();
|
|
971
|
+
* ```
|
|
972
|
+
*/
|
|
973
|
+
drawCircle(x, y, radius) {
|
|
974
|
+
this.ctx.beginPath();
|
|
975
|
+
this.ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
|
976
|
+
this.ctx.closePath();
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Draws an image at (x, y), scaled to `width` by `height` when both are given.
|
|
980
|
+
*
|
|
981
|
+
* @category Drawing
|
|
982
|
+
* @param img The image to draw, for example a loaded entry of `assets`.
|
|
983
|
+
* @param x X coordinate of the left edge.
|
|
984
|
+
* @param y Y coordinate of the top edge.
|
|
985
|
+
* @param width Width to draw at. Needs `height` as well; leave out for the natural size.
|
|
986
|
+
* @param height Height to draw at. Needs `width` as well; leave out for the natural size.
|
|
987
|
+
* @example
|
|
988
|
+
* ```js
|
|
989
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
990
|
+
* game.assets = ['image.png'];
|
|
991
|
+
* game.init();
|
|
992
|
+
* game.drawImage(game.assets['image.png'], 100, 100);
|
|
993
|
+
* game.drawImage(game.assets['image.png'], 100, 100, 200, 200);
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
drawImage(img, x, y, width = null, height = null) {
|
|
997
|
+
this.ctx.beginPath();
|
|
998
|
+
if (width !== null && height !== null) {
|
|
999
|
+
this.ctx.drawImage(img, x, y, width, height);
|
|
1000
|
+
} else {
|
|
1001
|
+
this.ctx.drawImage(img, x, y);
|
|
1002
|
+
}
|
|
1003
|
+
this.ctx.closePath();
|
|
1004
|
+
}
|
|
1005
|
+
// loop control
|
|
1006
|
+
/**
|
|
1007
|
+
* Starts the game loop. Each frame it clears the canvas, fills the background and runs the current state.
|
|
1008
|
+
* @example
|
|
1009
|
+
* ```js
|
|
1010
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1011
|
+
* game.init();
|
|
1012
|
+
* game.start();
|
|
1013
|
+
* ```
|
|
1014
|
+
*/
|
|
1015
|
+
start() {
|
|
1016
|
+
this.gameRunning = true;
|
|
1017
|
+
this.requestFrame(this.gameLoop);
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Stops the game loop. Call `start()` to run it again.
|
|
1021
|
+
* @example
|
|
1022
|
+
* ```js
|
|
1023
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1024
|
+
* game.stop();
|
|
1025
|
+
* ```
|
|
1026
|
+
*/
|
|
1027
|
+
stop() {
|
|
1028
|
+
this.gameRunning = false;
|
|
1029
|
+
}
|
|
1030
|
+
/** Prefers the legacy window.requestAnimFrame alias, else requestAnimationFrame. */
|
|
1031
|
+
requestFrame(cb) {
|
|
1032
|
+
if (typeof window.requestAnimFrame === "function") window.requestAnimFrame(cb);
|
|
1033
|
+
else requestAnimationFrame(cb);
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Built-in loading screen shown while `state` is `"startup"`.
|
|
1037
|
+
*
|
|
1038
|
+
* @internal
|
|
1039
|
+
*/
|
|
1040
|
+
startup() {
|
|
1041
|
+
this.drawRect(0, 0, this.width, this.height);
|
|
1042
|
+
this.setFillColor("#000");
|
|
1043
|
+
this.fill();
|
|
1044
|
+
this.ctx.font = "25px Arial";
|
|
1045
|
+
this.ctx.textAlign = "center";
|
|
1046
|
+
this.ctx.textBaseline = "bottom";
|
|
1047
|
+
this.setFillColor("#fff");
|
|
1048
|
+
this.ctx.fillText("Loading game", this.width / 2, this.height / 2 - 10);
|
|
1049
|
+
this.drawRect(this.width / 2 - 100, this.height / 2, 200, 20, 10);
|
|
1050
|
+
this.setStrokeColor("#fff");
|
|
1051
|
+
this.stroke();
|
|
1052
|
+
let loaded = 200 / this.assetsToLoad * this.loadedAssets;
|
|
1053
|
+
if (loaded < 20) loaded = 20;
|
|
1054
|
+
this.drawRect(this.width / 2 - 100, this.height / 2, loaded, 20, 10);
|
|
1055
|
+
this.setFillColor("#fff");
|
|
1056
|
+
this.fill();
|
|
1057
|
+
if (this.assetsToLoad == this.loadedAssets && T.pageLoaded && T.fontsLoaded) {
|
|
1058
|
+
this._resize();
|
|
1059
|
+
this.state = this.startState;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Draws the FPS meter shown when `showFPS` is true.
|
|
1064
|
+
*
|
|
1065
|
+
* @internal
|
|
1066
|
+
*/
|
|
1067
|
+
drawFPS() {
|
|
1068
|
+
this.ctx.fillStyle = "gray";
|
|
1069
|
+
this.ctx.font = "normal 16pt Arial";
|
|
1070
|
+
this.ctx.textAlign = "left";
|
|
1071
|
+
this.ctx.textBaseline = "top";
|
|
1072
|
+
this.ctx.fillText(Math.round(this.fps) + " fps - scale: " + this.scale, 5, 5);
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
// src/vector.ts
|
|
1077
|
+
var Vector = class {
|
|
1078
|
+
/**
|
|
1079
|
+
* Creates a vector. Must be called with `new`.
|
|
1080
|
+
* @param x The x component.
|
|
1081
|
+
* @param y The y component.
|
|
1082
|
+
* @example
|
|
1083
|
+
* ```js
|
|
1084
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1085
|
+
* ```
|
|
1086
|
+
*/
|
|
1087
|
+
constructor(x, y) {
|
|
1088
|
+
this.x = x;
|
|
1089
|
+
this.y = y;
|
|
1090
|
+
}
|
|
1091
|
+
// Makes Object.prototype.toString.call(v) give '[object THRUNIUM.Vector]'.
|
|
1092
|
+
get [Symbol.toStringTag]() {
|
|
1093
|
+
return "THRUNIUM.Vector";
|
|
1094
|
+
}
|
|
1095
|
+
// Built through T.Vector so a game that patches THRUNIUM.Vector gets its own class (1.0.0 behavior).
|
|
1096
|
+
/**
|
|
1097
|
+
* Returns a new vector with the same `x` and `y`. Changing the copy does not change the original.
|
|
1098
|
+
*
|
|
1099
|
+
* @example
|
|
1100
|
+
* ```js
|
|
1101
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1102
|
+
* var copy = vector.clone();
|
|
1103
|
+
* copy.scale(2);
|
|
1104
|
+
* // vector is still (10, 20), copy is (20, 40)
|
|
1105
|
+
* ```
|
|
1106
|
+
*/
|
|
1107
|
+
clone() {
|
|
1108
|
+
return new T.Vector(this.x, this.y);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Returns `true` if both components equal those of `vector`. The comparison is loose (`==`), as in 1.0.0.
|
|
1112
|
+
* @param vector The vector to compare with.
|
|
1113
|
+
* @example
|
|
1114
|
+
* ```js
|
|
1115
|
+
* var a = new THRUNIUM.Vector(10, 20);
|
|
1116
|
+
* var b = new THRUNIUM.Vector(10, 20);
|
|
1117
|
+
* a.equal(b); // true
|
|
1118
|
+
* ```
|
|
1119
|
+
*/
|
|
1120
|
+
equal(vector) {
|
|
1121
|
+
return this.x == vector.x && this.y == vector.y;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Multiplies both components by `scale`.
|
|
1125
|
+
* @param scale The factor to multiply by.
|
|
1126
|
+
* @example
|
|
1127
|
+
* ```js
|
|
1128
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1129
|
+
* vector.scale(2);
|
|
1130
|
+
* // vector is now (20, 40)
|
|
1131
|
+
* ```
|
|
1132
|
+
*/
|
|
1133
|
+
scale(scale) {
|
|
1134
|
+
this.x *= scale;
|
|
1135
|
+
this.y *= scale;
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Multiplies the x component by `scale`.
|
|
1139
|
+
* @param scale The factor to multiply by.
|
|
1140
|
+
* @example
|
|
1141
|
+
* ```js
|
|
1142
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1143
|
+
* vector.scaleX(2);
|
|
1144
|
+
* // vector is now (20, 20)
|
|
1145
|
+
* ```
|
|
1146
|
+
*/
|
|
1147
|
+
scaleX(scale) {
|
|
1148
|
+
this.x *= scale;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Multiplies the y component by `scale`.
|
|
1152
|
+
* @param scale The factor to multiply by.
|
|
1153
|
+
* @example
|
|
1154
|
+
* ```js
|
|
1155
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1156
|
+
* vector.scaleY(2);
|
|
1157
|
+
* // vector is now (10, 40)
|
|
1158
|
+
* ```
|
|
1159
|
+
*/
|
|
1160
|
+
scaleY(scale) {
|
|
1161
|
+
this.y *= scale;
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Adds `vector` to this vector.
|
|
1165
|
+
* @param vector The vector to add.
|
|
1166
|
+
* @example
|
|
1167
|
+
* ```js
|
|
1168
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1169
|
+
* vector.add(new THRUNIUM.Vector(5, 2));
|
|
1170
|
+
* // vector is now (15, 22)
|
|
1171
|
+
* ```
|
|
1172
|
+
*/
|
|
1173
|
+
add(vector) {
|
|
1174
|
+
this.x += vector.x;
|
|
1175
|
+
this.y += vector.y;
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Adds the x component of `vector` to this vector's x component.
|
|
1179
|
+
* @param vector The vector whose x component is added.
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```js
|
|
1182
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1183
|
+
* vector.addX(new THRUNIUM.Vector(5, 2));
|
|
1184
|
+
* // vector is now (15, 20)
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
addX(vector) {
|
|
1188
|
+
this.x += vector.x;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Adds the y component of `vector` to this vector's y component.
|
|
1192
|
+
* @param vector The vector whose y component is added.
|
|
1193
|
+
* @example
|
|
1194
|
+
* ```js
|
|
1195
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1196
|
+
* vector.addY(new THRUNIUM.Vector(5, 2));
|
|
1197
|
+
* // vector is now (10, 22)
|
|
1198
|
+
* ```
|
|
1199
|
+
*/
|
|
1200
|
+
addY(vector) {
|
|
1201
|
+
this.y += vector.y;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Subtracts `vector` from this vector.
|
|
1205
|
+
* @param vector The vector to subtract.
|
|
1206
|
+
* @example
|
|
1207
|
+
* ```js
|
|
1208
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1209
|
+
* vector.subtract(new THRUNIUM.Vector(2, 10));
|
|
1210
|
+
* // vector is now (8, 10)
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
1213
|
+
subtract(vector) {
|
|
1214
|
+
this.x -= vector.x;
|
|
1215
|
+
this.y -= vector.y;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Subtracts the x component of `vector` from this vector's x component.
|
|
1219
|
+
* @param vector The vector whose x component is subtracted.
|
|
1220
|
+
* @example
|
|
1221
|
+
* ```js
|
|
1222
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1223
|
+
* vector.subtractX(new THRUNIUM.Vector(2, 10));
|
|
1224
|
+
* // vector is now (8, 20)
|
|
1225
|
+
* ```
|
|
1226
|
+
*/
|
|
1227
|
+
subtractX(vector) {
|
|
1228
|
+
this.x -= vector.x;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Subtracts the y component of `vector` from this vector's y component.
|
|
1232
|
+
* @param vector The vector whose y component is subtracted.
|
|
1233
|
+
* @example
|
|
1234
|
+
* ```js
|
|
1235
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1236
|
+
* vector.subtractY(new THRUNIUM.Vector(2, 10));
|
|
1237
|
+
* // vector is now (10, 10)
|
|
1238
|
+
* ```
|
|
1239
|
+
*/
|
|
1240
|
+
subtractY(vector) {
|
|
1241
|
+
this.y -= vector.y;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Divides each component by the matching component of `vector`.
|
|
1245
|
+
* @param vector The vector to divide by.
|
|
1246
|
+
* @example
|
|
1247
|
+
* ```js
|
|
1248
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1249
|
+
* vector.divide(new THRUNIUM.Vector(2, 4));
|
|
1250
|
+
* // vector is now (5, 5)
|
|
1251
|
+
* ```
|
|
1252
|
+
*/
|
|
1253
|
+
divide(vector) {
|
|
1254
|
+
this.x /= vector.x;
|
|
1255
|
+
this.y /= vector.y;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Divides the x component by the x component of `vector`.
|
|
1259
|
+
* @param vector The vector whose x component is the divisor.
|
|
1260
|
+
* @example
|
|
1261
|
+
* ```js
|
|
1262
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1263
|
+
* vector.divideX(new THRUNIUM.Vector(2, 4));
|
|
1264
|
+
* // vector is now (5, 20)
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
divideX(vector) {
|
|
1268
|
+
this.x /= vector.x;
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Divides the y component by the y component of `vector`.
|
|
1272
|
+
* @param vector The vector whose y component is the divisor.
|
|
1273
|
+
* @example
|
|
1274
|
+
* ```js
|
|
1275
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1276
|
+
* vector.divideY(new THRUNIUM.Vector(2, 4));
|
|
1277
|
+
* // vector is now (10, 5)
|
|
1278
|
+
* ```
|
|
1279
|
+
*/
|
|
1280
|
+
divideY(vector) {
|
|
1281
|
+
this.y /= vector.y;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Multiplies each component by the matching component of `vector`.
|
|
1285
|
+
* @param vector The vector to multiply by.
|
|
1286
|
+
* @example
|
|
1287
|
+
* ```js
|
|
1288
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1289
|
+
* vector.multiply(new THRUNIUM.Vector(4, 2));
|
|
1290
|
+
* // vector is now (40, 40)
|
|
1291
|
+
* ```
|
|
1292
|
+
*/
|
|
1293
|
+
multiply(vector) {
|
|
1294
|
+
this.x *= vector.x;
|
|
1295
|
+
this.y *= vector.y;
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Multiplies the x component by the x component of `vector`.
|
|
1299
|
+
* @param vector The vector whose x component is the factor.
|
|
1300
|
+
* @example
|
|
1301
|
+
* ```js
|
|
1302
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1303
|
+
* vector.multiplyX(new THRUNIUM.Vector(4, 2));
|
|
1304
|
+
* // vector is now (40, 20)
|
|
1305
|
+
* ```
|
|
1306
|
+
*/
|
|
1307
|
+
multiplyX(vector) {
|
|
1308
|
+
this.x *= vector.x;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Multiplies the y component by the y component of `vector`.
|
|
1312
|
+
* @param vector The vector whose y component is the factor.
|
|
1313
|
+
* @example
|
|
1314
|
+
* ```js
|
|
1315
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1316
|
+
* vector.multiplyY(new THRUNIUM.Vector(4, 2));
|
|
1317
|
+
* // vector is now (10, 40)
|
|
1318
|
+
* ```
|
|
1319
|
+
*/
|
|
1320
|
+
multiplyY(vector) {
|
|
1321
|
+
this.y *= vector.y;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Flips the sign of both components, so the vector points the other way.
|
|
1325
|
+
*
|
|
1326
|
+
* @example
|
|
1327
|
+
* ```js
|
|
1328
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1329
|
+
* vector.invert();
|
|
1330
|
+
* // vector is now (-10, -20)
|
|
1331
|
+
* ```
|
|
1332
|
+
*/
|
|
1333
|
+
invert() {
|
|
1334
|
+
this.x *= -1;
|
|
1335
|
+
this.y *= -1;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Flips the sign of the x component.
|
|
1339
|
+
*
|
|
1340
|
+
* @example
|
|
1341
|
+
* ```js
|
|
1342
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1343
|
+
* vector.invertX();
|
|
1344
|
+
* // vector is now (-10, 20)
|
|
1345
|
+
* ```
|
|
1346
|
+
*/
|
|
1347
|
+
invertX() {
|
|
1348
|
+
this.x *= -1;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Flips the sign of the y component.
|
|
1352
|
+
*
|
|
1353
|
+
* @example
|
|
1354
|
+
* ```js
|
|
1355
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1356
|
+
* vector.invertY();
|
|
1357
|
+
* // vector is now (10, -20)
|
|
1358
|
+
* ```
|
|
1359
|
+
*/
|
|
1360
|
+
invertY() {
|
|
1361
|
+
this.y *= -1;
|
|
1362
|
+
}
|
|
1363
|
+
// Bug #8 fix: 1.0.0 computed x/y and y/x, which is not normalization.
|
|
1364
|
+
/**
|
|
1365
|
+
* Scales the vector to length 1, keeping its direction. The zero vector stays (0, 0). Since 1.1.0 this returns a real unit vector; 1.0.0 computed something else.
|
|
1366
|
+
*
|
|
1367
|
+
* @example
|
|
1368
|
+
* ```js
|
|
1369
|
+
* var vector = new THRUNIUM.Vector(3, 4);
|
|
1370
|
+
* vector.normalize();
|
|
1371
|
+
* // vector is now (0.6, 0.8)
|
|
1372
|
+
* ```
|
|
1373
|
+
*/
|
|
1374
|
+
normalize() {
|
|
1375
|
+
const length = Math.sqrt(this.x * this.x + this.y * this.y);
|
|
1376
|
+
if (length === 0) return;
|
|
1377
|
+
this.x /= length;
|
|
1378
|
+
this.y /= length;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Clamps each component to the range from `-vector` to `+vector` on that axis: `x` to between `-vector.x` and `vector.x`, `y` to between `-vector.y` and `vector.y`. Useful for capping a velocity. Pass a vector with non-negative components.
|
|
1382
|
+
* @param vector The largest allowed size of each component.
|
|
1383
|
+
* @example
|
|
1384
|
+
* ```js
|
|
1385
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1386
|
+
* vector.limit(new THRUNIUM.Vector(10, 10));
|
|
1387
|
+
* // vector is now (10, 10)
|
|
1388
|
+
* ```
|
|
1389
|
+
*/
|
|
1390
|
+
limit(vector) {
|
|
1391
|
+
if (this.x > vector.x) this.x = vector.x;
|
|
1392
|
+
if (this.y > vector.y) this.y = vector.y;
|
|
1393
|
+
if (this.x < vector.x * -1) this.x = vector.x * -1;
|
|
1394
|
+
if (this.y < vector.y * -1) this.y = vector.y * -1;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Does nothing. It is kept so games written for 1.0.0, where it was also empty, keep working. Use `limit()` to clamp a vector.
|
|
1398
|
+
* @param _len Ignored.
|
|
1399
|
+
* @example
|
|
1400
|
+
* ```js
|
|
1401
|
+
* var vector = new THRUNIUM.Vector(10, 20);
|
|
1402
|
+
* vector.limitLength(5);
|
|
1403
|
+
* // vector is still (10, 20)
|
|
1404
|
+
* ```
|
|
1405
|
+
*/
|
|
1406
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1407
|
+
limitLength(_len) {
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
|
|
1411
|
+
// src/collider.ts
|
|
1412
|
+
var Collider = {
|
|
1413
|
+
/**
|
|
1414
|
+
* Returns `true` if the object has the `x`, `y` and `radius` of a circle. It does not look at `pos`; call `fixPos()` first for a shape that uses one.
|
|
1415
|
+
* @param circle The object to check.
|
|
1416
|
+
* @example
|
|
1417
|
+
* ```js
|
|
1418
|
+
* THRUNIUM.Collider.isCircle({ x: 100, y: 100, radius: 50 }); // true
|
|
1419
|
+
* THRUNIUM.Collider.isCircle({ x: 100, y: 100 }); // false
|
|
1420
|
+
* ```
|
|
1421
|
+
*/
|
|
1422
|
+
isCircle(circle) {
|
|
1423
|
+
return circle.x !== void 0 && circle.y !== void 0 && circle.radius !== void 0;
|
|
1424
|
+
},
|
|
1425
|
+
/**
|
|
1426
|
+
* Returns `true` if the object has the `x`, `y`, `width` and `height` of a rect. It does not look at `pos`; call `fixPos()` first for a shape that uses one.
|
|
1427
|
+
* @param rect The object to check.
|
|
1428
|
+
* @example
|
|
1429
|
+
* ```js
|
|
1430
|
+
* THRUNIUM.Collider.isRect({ x: 100, y: 100, width: 50, height: 50 }); // true
|
|
1431
|
+
* THRUNIUM.Collider.isRect({ x: 100, y: 100, radius: 50 }); // false
|
|
1432
|
+
* ```
|
|
1433
|
+
*/
|
|
1434
|
+
isRect(rect) {
|
|
1435
|
+
return rect.x !== void 0 && rect.y !== void 0 && rect.width !== void 0 && rect.height !== void 0;
|
|
1436
|
+
},
|
|
1437
|
+
/**
|
|
1438
|
+
* Copies `pos.x` and `pos.y` onto the shape's own `x` and `y`, and returns the same object. A shape without a `pos` is returned unchanged. The result has the same shape you passed in, so a circle stays a circle and a rect stays a rect. The collision checks call it for you.
|
|
1439
|
+
* @param object The shape to update.
|
|
1440
|
+
* @example
|
|
1441
|
+
* ```js
|
|
1442
|
+
* var rect = { pos: new THRUNIUM.Vector(100, 100), width: 50, height: 50 };
|
|
1443
|
+
* THRUNIUM.Collider.fixPos(rect);
|
|
1444
|
+
* // rect.x is now 100 and rect.y is 100
|
|
1445
|
+
* ```
|
|
1446
|
+
*/
|
|
1447
|
+
fixPos(object) {
|
|
1448
|
+
if (object.pos === void 0) return object;
|
|
1449
|
+
if (object.pos.x === void 0 && object.pos.y === void 0) return object;
|
|
1450
|
+
object.x = object.pos.x;
|
|
1451
|
+
object.y = object.pos.y;
|
|
1452
|
+
return object;
|
|
1453
|
+
},
|
|
1454
|
+
/**
|
|
1455
|
+
* Returns `true` if two rects overlap. Rects whose edges just touch count as colliding. Each rect needs `x`, `y`, `width` and `height`, or `pos`, `width` and `height`.
|
|
1456
|
+
* @param _a The first rect.
|
|
1457
|
+
* @param _b The second rect.
|
|
1458
|
+
* @example
|
|
1459
|
+
* ```js
|
|
1460
|
+
* var rect1 = { x: 100, y: 100, width: 50, height: 50 };
|
|
1461
|
+
* var rect2 = { x: 140, y: 140, width: 50, height: 50 };
|
|
1462
|
+
* if (THRUNIUM.Collider.rect(rect1, rect2)) {
|
|
1463
|
+
* console.log('Collision!');
|
|
1464
|
+
* }
|
|
1465
|
+
* ```
|
|
1466
|
+
* @example
|
|
1467
|
+
* ```js
|
|
1468
|
+
* var rect1 = { pos: new THRUNIUM.Vector(100, 100), width: 50, height: 50 };
|
|
1469
|
+
* var rect2 = { pos: new THRUNIUM.Vector(140, 140), width: 50, height: 50 };
|
|
1470
|
+
* if (THRUNIUM.Collider.rect(rect1, rect2)) {
|
|
1471
|
+
* console.log('Collision!');
|
|
1472
|
+
* }
|
|
1473
|
+
* ```
|
|
1474
|
+
*/
|
|
1475
|
+
rect(_a3, _b) {
|
|
1476
|
+
const a = Collider.fixPos(_a3);
|
|
1477
|
+
const b = Collider.fixPos(_b);
|
|
1478
|
+
if (!Collider.isRect(a) || !Collider.isRect(b)) {
|
|
1479
|
+
throwError("One of the objects is not defined correctly as a rect");
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
const [ax, ay, aw, ah] = [a.x, a.y, a.width, a.height];
|
|
1483
|
+
const [bx, by, bw, bh] = [b.x, b.y, b.width, b.height];
|
|
1484
|
+
if (ax + aw / 2 < bx - bw / 2) return false;
|
|
1485
|
+
if (ax - aw / 2 > bx + bw / 2) return false;
|
|
1486
|
+
if (ay + ah / 2 < by - bh / 2) return false;
|
|
1487
|
+
if (ay - ah / 2 > by + bh / 2) return false;
|
|
1488
|
+
return true;
|
|
1489
|
+
},
|
|
1490
|
+
/**
|
|
1491
|
+
* Returns `true` if two circles overlap. Circles that just touch do not count as colliding. Each circle needs `x`, `y` and `radius`, or `pos` and `radius`.
|
|
1492
|
+
* @param _a The first circle.
|
|
1493
|
+
* @param _b The second circle.
|
|
1494
|
+
* @example
|
|
1495
|
+
* ```js
|
|
1496
|
+
* var circle1 = { x: 100, y: 100, radius: 50 };
|
|
1497
|
+
* var circle2 = { x: 140, y: 140, radius: 50 };
|
|
1498
|
+
* if (THRUNIUM.Collider.circle(circle1, circle2)) {
|
|
1499
|
+
* console.log('Collision!');
|
|
1500
|
+
* }
|
|
1501
|
+
* ```
|
|
1502
|
+
* @example
|
|
1503
|
+
* ```js
|
|
1504
|
+
* var circle1 = { pos: new THRUNIUM.Vector(100, 100), radius: 50 };
|
|
1505
|
+
* var circle2 = { pos: new THRUNIUM.Vector(140, 140), radius: 50 };
|
|
1506
|
+
* if (THRUNIUM.Collider.circle(circle1, circle2)) {
|
|
1507
|
+
* console.log('Collision!');
|
|
1508
|
+
* }
|
|
1509
|
+
* ```
|
|
1510
|
+
*/
|
|
1511
|
+
circle(_a3, _b) {
|
|
1512
|
+
const a = Collider.fixPos(_a3);
|
|
1513
|
+
const b = Collider.fixPos(_b);
|
|
1514
|
+
if (!Collider.isCircle(a) || !Collider.isCircle(b)) {
|
|
1515
|
+
throwError("One of the objects is not defined correctly as a circle");
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
const dx = a.x - b.x;
|
|
1519
|
+
const dy = a.y - b.y;
|
|
1520
|
+
return Math.sqrt(dx * dx + dy * dy) < a.radius + b.radius;
|
|
1521
|
+
},
|
|
1522
|
+
/**
|
|
1523
|
+
* Returns `true` if a circle and a rect overlap. A circle that just touches a side of the rect does not count as colliding. The circle needs `x`, `y` and `radius` (or `pos` and `radius`); the rect needs `x`, `y`, `width` and `height` (or `pos`, `width` and `height`).
|
|
1524
|
+
* @param _circle The circle.
|
|
1525
|
+
* @param _rect The rect.
|
|
1526
|
+
* @example
|
|
1527
|
+
* ```js
|
|
1528
|
+
* var circle = { x: 100, y: 100, radius: 50 };
|
|
1529
|
+
* var rect = { x: 140, y: 140, width: 50, height: 50 };
|
|
1530
|
+
* if (THRUNIUM.Collider.circleRect(circle, rect)) {
|
|
1531
|
+
* console.log('Collision!');
|
|
1532
|
+
* }
|
|
1533
|
+
* ```
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```js
|
|
1536
|
+
* var circle = { pos: new THRUNIUM.Vector(100, 100), radius: 50 };
|
|
1537
|
+
* var rect = { pos: new THRUNIUM.Vector(140, 140), width: 50, height: 50 };
|
|
1538
|
+
* if (THRUNIUM.Collider.circleRect(circle, rect)) {
|
|
1539
|
+
* console.log('Collision!');
|
|
1540
|
+
* }
|
|
1541
|
+
* ```
|
|
1542
|
+
*/
|
|
1543
|
+
circleRect(_circle, _rect) {
|
|
1544
|
+
const circle = Collider.fixPos(_circle);
|
|
1545
|
+
const rect = Collider.fixPos(_rect);
|
|
1546
|
+
if (!Collider.isCircle(circle) || !Collider.isRect(rect)) {
|
|
1547
|
+
throwError("One of the objects is not defined correctly as a circle or rect");
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
const distX = Math.abs(circle.x - rect.x);
|
|
1551
|
+
const distY = Math.abs(circle.y - rect.y);
|
|
1552
|
+
const halfW = rect.width / 2;
|
|
1553
|
+
const halfH = rect.height / 2;
|
|
1554
|
+
const r = circle.radius;
|
|
1555
|
+
if (distX >= halfW + r) return false;
|
|
1556
|
+
if (distY >= halfH + r) return false;
|
|
1557
|
+
if (distX < halfW) return true;
|
|
1558
|
+
if (distY < halfH) return true;
|
|
1559
|
+
const dx = distX - halfW;
|
|
1560
|
+
const dy = distY - halfH;
|
|
1561
|
+
return dx * dx + dy * dy <= r * r;
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
// src/sprite.ts
|
|
1566
|
+
var game = () => T.gameObject;
|
|
1567
|
+
var _a2;
|
|
1568
|
+
_a2 = Symbol.toStringTag;
|
|
1569
|
+
var Sprite = class {
|
|
1570
|
+
/**
|
|
1571
|
+
* Creates a sprite from an image. The image must have finished loading, because its size is read here; until you call `setSpriteMap()` the whole image is one frame. Must be called with `new`, and a `THRUNIUM.Game` must exist before the sprite draws.
|
|
1572
|
+
* @param image A loaded image to use as the sprite or sprite map.
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```js
|
|
1575
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1576
|
+
* var image = new Image();
|
|
1577
|
+
* image.onload = function() {
|
|
1578
|
+
* var sprite = new THRUNIUM.Sprite(image);
|
|
1579
|
+
* sprite.draw(0, 200, 200);
|
|
1580
|
+
* };
|
|
1581
|
+
* image.src = 'images/hero.png';
|
|
1582
|
+
* ```
|
|
1583
|
+
*/
|
|
1584
|
+
constructor(image) {
|
|
1585
|
+
this[_a2] = "THRUNIUM.Sprite";
|
|
1586
|
+
/**
|
|
1587
|
+
* Rotation in degrees, clockwise, around the centre of the drawn frame. Default is `0`. Set it with `setAngle()`.
|
|
1588
|
+
*
|
|
1589
|
+
* @example
|
|
1590
|
+
* ```js
|
|
1591
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1592
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1593
|
+
* game.startState = 'game';
|
|
1594
|
+
* game.states['game'] = function() {
|
|
1595
|
+
* game.assets['hero'].angle; // 0
|
|
1596
|
+
* };
|
|
1597
|
+
* game.init();
|
|
1598
|
+
* game.start();
|
|
1599
|
+
* ```
|
|
1600
|
+
*/
|
|
1601
|
+
this.angle = 0;
|
|
1602
|
+
/**
|
|
1603
|
+
* The frame `drawAnim()` is showing, or `null` when no animation is running. `drawAnim()` updates it; set it to `null` to restart an animation from its first frame.
|
|
1604
|
+
*
|
|
1605
|
+
* @example
|
|
1606
|
+
* ```js
|
|
1607
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1608
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1609
|
+
* game.startState = 'game';
|
|
1610
|
+
* game.states['game'] = function() {
|
|
1611
|
+
* var hero = game.assets['hero'];
|
|
1612
|
+
* hero.setLoop(true);
|
|
1613
|
+
* hero.drawAnim(0, 3, 200, 200);
|
|
1614
|
+
* hero.currentSprite; // 0, 1, 2 or 3
|
|
1615
|
+
* };
|
|
1616
|
+
* game.init();
|
|
1617
|
+
* game.start();
|
|
1618
|
+
* ```
|
|
1619
|
+
*/
|
|
1620
|
+
this.currentSprite = null;
|
|
1621
|
+
/**
|
|
1622
|
+
* How long one run of an animation takes, in milliseconds, split evenly over its frames. Default is `100`. Set it with `setAnimTime()`.
|
|
1623
|
+
*
|
|
1624
|
+
* @example
|
|
1625
|
+
* ```js
|
|
1626
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1627
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1628
|
+
* game.startState = 'game';
|
|
1629
|
+
* game.states['game'] = function() {
|
|
1630
|
+
* game.assets['hero'].animTimeDur; // 100
|
|
1631
|
+
* };
|
|
1632
|
+
* game.init();
|
|
1633
|
+
* game.start();
|
|
1634
|
+
* ```
|
|
1635
|
+
*/
|
|
1636
|
+
this.animTimeDur = 100;
|
|
1637
|
+
/**
|
|
1638
|
+
* Unix timestamp in milliseconds of when `drawAnim()` last moved to another frame. Starts at `0`.
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```js
|
|
1642
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1643
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1644
|
+
* game.startState = 'game';
|
|
1645
|
+
* game.states['game'] = function() {
|
|
1646
|
+
* var hero = game.assets['hero'];
|
|
1647
|
+
* hero.drawAnim(0, 3, 200, 200);
|
|
1648
|
+
* hero.frameTimeLast; // e.g. 1767225600000
|
|
1649
|
+
* };
|
|
1650
|
+
* game.init();
|
|
1651
|
+
* game.start();
|
|
1652
|
+
* ```
|
|
1653
|
+
*/
|
|
1654
|
+
this.frameTimeLast = 0;
|
|
1655
|
+
/**
|
|
1656
|
+
* Whether `drawAnim()` starts over after the last frame. Default is `false`. Set it with `setLoop()`.
|
|
1657
|
+
*
|
|
1658
|
+
* @example
|
|
1659
|
+
* ```js
|
|
1660
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1661
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1662
|
+
* game.startState = 'game';
|
|
1663
|
+
* game.states['game'] = function() {
|
|
1664
|
+
* game.assets['hero'].animLoop; // false
|
|
1665
|
+
* };
|
|
1666
|
+
* game.init();
|
|
1667
|
+
* game.start();
|
|
1668
|
+
* ```
|
|
1669
|
+
*/
|
|
1670
|
+
this.animLoop = false;
|
|
1671
|
+
/**
|
|
1672
|
+
* Function called when a non-looping animation finishes. Default does nothing. Set it with `setAnimCallback()`.
|
|
1673
|
+
*
|
|
1674
|
+
* @example
|
|
1675
|
+
* ```js
|
|
1676
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1677
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1678
|
+
* game.startState = 'game';
|
|
1679
|
+
* game.states['game'] = function() {
|
|
1680
|
+
* game.assets['hero'].animCallback(); // does nothing until you set one
|
|
1681
|
+
* };
|
|
1682
|
+
* game.init();
|
|
1683
|
+
* game.start();
|
|
1684
|
+
* ```
|
|
1685
|
+
*/
|
|
1686
|
+
this.animCallback = () => {
|
|
1687
|
+
};
|
|
1688
|
+
this.image = image;
|
|
1689
|
+
this.fullWidth = image.width;
|
|
1690
|
+
this.fullHeight = image.height;
|
|
1691
|
+
this.spriteWidth = image.width;
|
|
1692
|
+
this.spriteHeight = image.height;
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Sets the rotation, in degrees clockwise, that the sprite is drawn with from now on.
|
|
1696
|
+
* @param angle The rotation in degrees.
|
|
1697
|
+
* @example
|
|
1698
|
+
* ```js
|
|
1699
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1700
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1701
|
+
* game.startState = 'game';
|
|
1702
|
+
* game.states['game'] = function() {
|
|
1703
|
+
* var hero = game.assets['hero'];
|
|
1704
|
+
* hero.setAngle(hero.angle + 90 * game.delta); // a quarter turn per second
|
|
1705
|
+
* hero.draw(0, 200, 200);
|
|
1706
|
+
* };
|
|
1707
|
+
* game.init();
|
|
1708
|
+
* game.start();
|
|
1709
|
+
* ```
|
|
1710
|
+
*/
|
|
1711
|
+
setAngle(angle) {
|
|
1712
|
+
this.angle = angle;
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Sets whether `drawAnim()` starts over after the last frame. Without a loop, the animation calls the `setAnimCallback()` function after its last frame, and the next `drawAnim()` call starts it again. Since 1.1.0 `drawAnim()` honours this setting; 1.0.0 ignored it.
|
|
1716
|
+
* @param loop `true` to loop the animation.
|
|
1717
|
+
* @example
|
|
1718
|
+
* ```js
|
|
1719
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1720
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1721
|
+
* game.startState = 'game';
|
|
1722
|
+
* game.states['game'] = function() {
|
|
1723
|
+
* var hero = game.assets['hero'];
|
|
1724
|
+
* hero.setLoop(true);
|
|
1725
|
+
* hero.drawAnim(0, 3, 200, 200);
|
|
1726
|
+
* };
|
|
1727
|
+
* game.init();
|
|
1728
|
+
* game.start();
|
|
1729
|
+
* ```
|
|
1730
|
+
*/
|
|
1731
|
+
setLoop(loop) {
|
|
1732
|
+
this.animLoop = loop;
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Sets the function called each time a non-looping animation finishes.
|
|
1736
|
+
* @param fun The function to call; it gets no arguments.
|
|
1737
|
+
* @example
|
|
1738
|
+
* ```js
|
|
1739
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1740
|
+
* game.assets = [{ src: 'images/explosion.png', index: 'boom', spriteMap: { width: 64, height: 64 } }];
|
|
1741
|
+
* game.startState = 'game';
|
|
1742
|
+
* game.states['game'] = function() {
|
|
1743
|
+
* var boom = game.assets['boom'];
|
|
1744
|
+
* boom.setAnimCallback(function() {
|
|
1745
|
+
* game.state = 'gameover';
|
|
1746
|
+
* });
|
|
1747
|
+
* boom.drawAnim(0, 7, 200, 200);
|
|
1748
|
+
* };
|
|
1749
|
+
* game.states['gameover'] = function() {
|
|
1750
|
+
* game.drawText('Game over', 200, 200);
|
|
1751
|
+
* };
|
|
1752
|
+
* game.init();
|
|
1753
|
+
* game.start();
|
|
1754
|
+
* ```
|
|
1755
|
+
*/
|
|
1756
|
+
setAnimCallback(fun) {
|
|
1757
|
+
this.animCallback = fun;
|
|
1758
|
+
}
|
|
1759
|
+
// Bug #6: 1.0.0 assigned to `this.setAnimTime`, overwriting the method itself.
|
|
1760
|
+
/**
|
|
1761
|
+
* Sets how long one run of an animation takes, in milliseconds. The time is split evenly over the frames `drawAnim()` plays. Since 1.1.0 this works; in 1.0.0 it overwrote itself and had no effect.
|
|
1762
|
+
* @param time Duration of the whole animation in milliseconds.
|
|
1763
|
+
* @example
|
|
1764
|
+
* ```js
|
|
1765
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1766
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1767
|
+
* game.startState = 'game';
|
|
1768
|
+
* game.states['game'] = function() {
|
|
1769
|
+
* var hero = game.assets['hero'];
|
|
1770
|
+
* hero.setAnimTime(400); // 4 frames, 100 ms each
|
|
1771
|
+
* hero.setLoop(true);
|
|
1772
|
+
* hero.drawAnim(0, 3, 200, 200);
|
|
1773
|
+
* };
|
|
1774
|
+
* game.init();
|
|
1775
|
+
* game.start();
|
|
1776
|
+
* ```
|
|
1777
|
+
*/
|
|
1778
|
+
setAnimTime(time) {
|
|
1779
|
+
this.animTimeDur = time;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Turns the image into a sprite map of frames of `width` x `height` pixels. Frames are numbered from 0, left to right, then row by row. A sprite-map asset has this set already.
|
|
1783
|
+
* @param width Width of one frame in pixels.
|
|
1784
|
+
* @param height Height of one frame in pixels.
|
|
1785
|
+
* @example
|
|
1786
|
+
* ```js
|
|
1787
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1788
|
+
* var image = new Image();
|
|
1789
|
+
* image.onload = function() {
|
|
1790
|
+
* var tiles = new THRUNIUM.Sprite(image);
|
|
1791
|
+
* tiles.setSpriteMap(40, 40);
|
|
1792
|
+
* tiles.draw(2, 200, 200); // the third 40 x 40 frame
|
|
1793
|
+
* };
|
|
1794
|
+
* image.src = 'images/tiles.png';
|
|
1795
|
+
* ```
|
|
1796
|
+
*/
|
|
1797
|
+
setSpriteMap(width, height) {
|
|
1798
|
+
this.spriteWidth = width;
|
|
1799
|
+
this.spriteHeight = height;
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Draws one frame centred on (`x`, `y`), rotated by `angle`. The frame is drawn at its own size unless you pass `width` and `height`.
|
|
1803
|
+
* @param imgNum Number of the frame to draw, counting from 0 left to right, then row by row. Default is `0`.
|
|
1804
|
+
* @param x X coordinate of the centre of the drawn frame.
|
|
1805
|
+
* @param y Y coordinate of the centre of the drawn frame.
|
|
1806
|
+
* @param width Width to draw the frame at. Default is the frame width.
|
|
1807
|
+
* @param height Height to draw the frame at. Default is the frame height.
|
|
1808
|
+
* @example
|
|
1809
|
+
* ```js
|
|
1810
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1811
|
+
* game.assets = [{ src: 'images/tiles.png', index: 'tiles', spriteMap: { width: 40, height: 40 } }];
|
|
1812
|
+
* game.startState = 'game';
|
|
1813
|
+
* game.states['game'] = function() {
|
|
1814
|
+
* game.assets['tiles'].draw(0, 50, 50); // frame 0 at its own size
|
|
1815
|
+
* game.assets['tiles'].draw(1, 150, 50, 80, 80); // frame 1 at double size
|
|
1816
|
+
* };
|
|
1817
|
+
* game.init();
|
|
1818
|
+
* game.start();
|
|
1819
|
+
* ```
|
|
1820
|
+
*/
|
|
1821
|
+
draw(imgNum = 0, x, y, width, height) {
|
|
1822
|
+
const xImg = imgNum * this.spriteWidth % this.fullWidth;
|
|
1823
|
+
const yImg = Math.floor(imgNum * this.spriteWidth / this.fullWidth) * this.spriteHeight;
|
|
1824
|
+
const w = width === void 0 ? this.spriteWidth : width;
|
|
1825
|
+
const h = height === void 0 ? this.spriteHeight : height;
|
|
1826
|
+
const ctx = game().ctx;
|
|
1827
|
+
ctx.beginPath();
|
|
1828
|
+
ctx.save();
|
|
1829
|
+
ctx.translate(x, y);
|
|
1830
|
+
ctx.rotate(this.angle * Math.PI / 180);
|
|
1831
|
+
ctx.drawImage(this.image, xImg, yImg, this.spriteWidth, this.spriteHeight, -w / 2, -h / 2, w, h);
|
|
1832
|
+
ctx.restore();
|
|
1833
|
+
ctx.closePath();
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Plays an animation over the frames `imgFrom` to `imgTo`: call it every frame of your game state and it draws the current frame, moving to the next one when its share of `animTimeDur` has passed. After `imgTo` it starts over when `setLoop(true)` is set; otherwise it calls the `setAnimCallback()` function, and the next call starts again from `imgFrom`. Drawing works as in `draw()`.
|
|
1837
|
+
* @param imgFrom Number of the first frame of the animation.
|
|
1838
|
+
* @param imgTo Number of the last frame of the animation.
|
|
1839
|
+
* @param x X coordinate of the centre of the drawn frame.
|
|
1840
|
+
* @param y Y coordinate of the centre of the drawn frame.
|
|
1841
|
+
* @param width Width to draw the frame at. Default is the frame width.
|
|
1842
|
+
* @param height Height to draw the frame at. Default is the frame height.
|
|
1843
|
+
* @example
|
|
1844
|
+
* ```js
|
|
1845
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1846
|
+
* game.assets = [{ src: 'images/hero.png', index: 'hero', spriteMap: { width: 32, height: 32 } }];
|
|
1847
|
+
* game.startState = 'game';
|
|
1848
|
+
* game.states['game'] = function() {
|
|
1849
|
+
* // frames 0 to 3, drawn at double size
|
|
1850
|
+
* game.assets['hero'].drawAnim(0, 3, 200, 200, 64, 64);
|
|
1851
|
+
* };
|
|
1852
|
+
* game.init();
|
|
1853
|
+
* game.start();
|
|
1854
|
+
* ```
|
|
1855
|
+
*/
|
|
1856
|
+
drawAnim(imgFrom, imgTo, x, y, width, height) {
|
|
1857
|
+
var _a3;
|
|
1858
|
+
if (this.currentSprite == null) this.currentSprite = imgFrom;
|
|
1859
|
+
const length = this.animTimeDur / (imgTo - imgFrom + 1);
|
|
1860
|
+
const now = Date.now();
|
|
1861
|
+
if (now - this.frameTimeLast > length) {
|
|
1862
|
+
if (this.currentSprite < imgTo) this.currentSprite++;
|
|
1863
|
+
else if (this.animLoop) this.currentSprite = imgFrom;
|
|
1864
|
+
else {
|
|
1865
|
+
this.currentSprite = null;
|
|
1866
|
+
this.animCallback();
|
|
1867
|
+
}
|
|
1868
|
+
this.frameTimeLast = now;
|
|
1869
|
+
}
|
|
1870
|
+
this.draw((_a3 = this.currentSprite) != null ? _a3 : void 0, x, y, width, height);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
|
|
1874
|
+
// src/elem.ts
|
|
1875
|
+
var game2 = () => T.gameObject;
|
|
1876
|
+
var ElemElem = class {
|
|
1877
|
+
/**
|
|
1878
|
+
* Creates an HTML element with the tag `tag` and places it on the game at (`x`, `y`). It starts with a white background, black text, the game's `font` and rounded corners, and is added to `THRUNIUM.Elem.elemsList`. Create the `THRUNIUM.Game` first, because the element is added to the game's container. Must be called with `new`.
|
|
1879
|
+
* @param tag Tag name of the element to create, such as `'div'` or `'button'`.
|
|
1880
|
+
* @param x X coordinate of the element's top-left corner, in game coordinates.
|
|
1881
|
+
* @param y Y coordinate of the element's top-left corner, in game coordinates.
|
|
1882
|
+
* @example
|
|
1883
|
+
* ```js
|
|
1884
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1885
|
+
* var label = new THRUNIUM.Elem.Elem('div', 20, 20);
|
|
1886
|
+
* label.elem.textContent = 'Score: 0';
|
|
1887
|
+
* ```
|
|
1888
|
+
*/
|
|
1889
|
+
constructor(tag, x, y) {
|
|
1890
|
+
/**
|
|
1891
|
+
* The element's styles, as CSS property names in camelCase (`backgroundColor`) mapped to values. Change them with `setStyle()`, which also applies them; changing this object directly does not update the element.
|
|
1892
|
+
*
|
|
1893
|
+
* @example
|
|
1894
|
+
* ```js
|
|
1895
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1896
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
1897
|
+
* button.style.backgroundColor; // '#fff'
|
|
1898
|
+
* ```
|
|
1899
|
+
*/
|
|
1900
|
+
this.style = {};
|
|
1901
|
+
/**
|
|
1902
|
+
* The styles applied while the mouse is over the element, in the same form as `style`. It follows `style`, except for properties set with `setHoverStyle()`. Change it with `setHoverStyle()`.
|
|
1903
|
+
*
|
|
1904
|
+
* @example
|
|
1905
|
+
* ```js
|
|
1906
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1907
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
1908
|
+
* button.setHoverStyle('backgroundColor', '#ddd');
|
|
1909
|
+
* button.hoverStyle.backgroundColor; // '#ddd'
|
|
1910
|
+
* ```
|
|
1911
|
+
*/
|
|
1912
|
+
this.hoverStyle = {};
|
|
1913
|
+
/** Hover properties set explicitly via setHoverStyle; setStyle must not overwrite these. */
|
|
1914
|
+
this.hoverOverrides = {};
|
|
1915
|
+
this._tag = "THRUNIUM.Elem." + tag;
|
|
1916
|
+
this.x = x;
|
|
1917
|
+
this.y = y;
|
|
1918
|
+
this.elem = document.createElement(tag);
|
|
1919
|
+
this.elem.addEventListener("mouseenter", () => this.updateStyle(this.hoverStyle), false);
|
|
1920
|
+
this.elem.addEventListener("mouseleave", () => this.updateStyle(this.style), false);
|
|
1921
|
+
this.setStyle("display", "block");
|
|
1922
|
+
this.setStyle("position", "absolute");
|
|
1923
|
+
this.setStyle("color", "#000");
|
|
1924
|
+
this.setStyle("backgroundColor", "#fff");
|
|
1925
|
+
this.setStyle("fontFamily", game2().font);
|
|
1926
|
+
this.setStyle("borderRadius", "5px");
|
|
1927
|
+
this.resize();
|
|
1928
|
+
elemApi.elemsList.push(this);
|
|
1929
|
+
T.container.appendChild(this.elem);
|
|
1930
|
+
}
|
|
1931
|
+
get [Symbol.toStringTag]() {
|
|
1932
|
+
return this._tag;
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1935
|
+
* Moves the element so its top-left corner is at (`x`, `y`) in game coordinates.
|
|
1936
|
+
* @param x The new x coordinate.
|
|
1937
|
+
* @param y The new y coordinate.
|
|
1938
|
+
* @example
|
|
1939
|
+
* ```js
|
|
1940
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1941
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
1942
|
+
* button.setPos(150, 200);
|
|
1943
|
+
* ```
|
|
1944
|
+
*/
|
|
1945
|
+
setPos(x, y) {
|
|
1946
|
+
this.x = x;
|
|
1947
|
+
this.y = y;
|
|
1948
|
+
this.resize();
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Re-applies the element's position and scale for the game's current `fitScale`. The game calls it for every element in `THRUNIUM.Elem.elemsList` when the window is resized, so you rarely need it. Since 1.1.0 it no longer leaks a global variable or throws in strict mode.
|
|
1952
|
+
*
|
|
1953
|
+
* @example
|
|
1954
|
+
* ```js
|
|
1955
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1956
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
1957
|
+
* button.resize();
|
|
1958
|
+
* ```
|
|
1959
|
+
*/
|
|
1960
|
+
resize() {
|
|
1961
|
+
const fit = game2().fitScale;
|
|
1962
|
+
this.setStyle("left", this.x * fit + "px");
|
|
1963
|
+
this.setStyle("top", this.y * fit + "px");
|
|
1964
|
+
this.setStyle("transformOrigin", "top left");
|
|
1965
|
+
this.setStyle("transform", "scale(" + fit + ")");
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* Listens for a DOM event on the element, like the element's own `addEventListener`.
|
|
1969
|
+
* @param type Name of the event, such as `'click'`.
|
|
1970
|
+
* @param fun The function to call when the event happens; it gets the event.
|
|
1971
|
+
* @param async Passed on as the third argument of the DOM `addEventListener` (`useCapture`). Despite its name it has nothing to do with asynchronous code; leave it out.
|
|
1972
|
+
* @example
|
|
1973
|
+
* ```js
|
|
1974
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1975
|
+
* var button = THRUNIUM.Elem.Button('Press me!', 100, 100);
|
|
1976
|
+
* button.addEventListener('click', function(e) {
|
|
1977
|
+
* // button was clicked
|
|
1978
|
+
* });
|
|
1979
|
+
* ```
|
|
1980
|
+
*/
|
|
1981
|
+
addEventListener(type, fun, async) {
|
|
1982
|
+
this.elem.addEventListener(type, fun, async);
|
|
1983
|
+
}
|
|
1984
|
+
/**
|
|
1985
|
+
* Applies a set of styles to the element without storing them in `style` or `hoverStyle`. The element uses it internally to switch between `style` and `hoverStyle`; to change a style for good, use `setStyle()`.
|
|
1986
|
+
* @param styleList CSS property names in camelCase mapped to values.
|
|
1987
|
+
* @example
|
|
1988
|
+
* ```js
|
|
1989
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
1990
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
1991
|
+
* button.updateStyle({ color: '#f00', fontSize: '20px' });
|
|
1992
|
+
* ```
|
|
1993
|
+
*/
|
|
1994
|
+
updateStyle(styleList) {
|
|
1995
|
+
for (const name in styleList) {
|
|
1996
|
+
this.elem.style[name] = styleList[name];
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Sets one style of the element and applies it. Unless you have set a hover style for the same property with `setHoverStyle()`, the element keeps it while hovered too.
|
|
2001
|
+
* @param name CSS property name in camelCase, such as `'backgroundColor'`.
|
|
2002
|
+
* @param value The CSS value, such as `'#000'`.
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```js
|
|
2005
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
2006
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
2007
|
+
* button.setStyle('backgroundColor', '#000');
|
|
2008
|
+
* button.setStyle('color', '#fff');
|
|
2009
|
+
* ```
|
|
2010
|
+
*/
|
|
2011
|
+
setStyle(name, value) {
|
|
2012
|
+
this.style[name] = value;
|
|
2013
|
+
if (!this.hoverOverrides[name]) this.hoverStyle[name] = value;
|
|
2014
|
+
this.updateStyle(this.style);
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Sets a style that applies only while the mouse is over the element; the normal style comes back when the mouse leaves. Since 1.1.0 hover styles work as described; 1.0.0 applied them the wrong way round.
|
|
2018
|
+
* @param name CSS property name in camelCase, such as `'backgroundColor'`.
|
|
2019
|
+
* @param value The CSS value to use while hovered.
|
|
2020
|
+
* @example
|
|
2021
|
+
* ```js
|
|
2022
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
2023
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
2024
|
+
* button.setHoverStyle('backgroundColor', '#ddd');
|
|
2025
|
+
* ```
|
|
2026
|
+
*/
|
|
2027
|
+
setHoverStyle(name, value) {
|
|
2028
|
+
this.hoverStyle[name] = value;
|
|
2029
|
+
this.hoverOverrides[name] = true;
|
|
2030
|
+
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Shows the element again after `hide()`.
|
|
2033
|
+
*
|
|
2034
|
+
* @example
|
|
2035
|
+
* ```js
|
|
2036
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
2037
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
2038
|
+
* button.hide();
|
|
2039
|
+
* button.show();
|
|
2040
|
+
* ```
|
|
2041
|
+
*/
|
|
2042
|
+
show() {
|
|
2043
|
+
this.setStyle("display", "block");
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Hides the element. It stays in place, so `show()` brings it back.
|
|
2047
|
+
*
|
|
2048
|
+
* @example
|
|
2049
|
+
* ```js
|
|
2050
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
2051
|
+
* var button = THRUNIUM.Elem.Button('Play', 100, 100);
|
|
2052
|
+
* button.addEventListener('click', function() {
|
|
2053
|
+
* button.hide();
|
|
2054
|
+
* });
|
|
2055
|
+
* ```
|
|
2056
|
+
*/
|
|
2057
|
+
hide() {
|
|
2058
|
+
this.setStyle("display", "none");
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
function Button(txt, x, y) {
|
|
2062
|
+
const button = new ElemElem("button", x, y);
|
|
2063
|
+
button.elem.textContent = txt;
|
|
2064
|
+
button.elem.style.border = "none";
|
|
2065
|
+
return button;
|
|
2066
|
+
}
|
|
2067
|
+
function Input(x, y, options) {
|
|
2068
|
+
const g = game2();
|
|
2069
|
+
const input = document.createElement("input");
|
|
2070
|
+
input.style.position = "absolute";
|
|
2071
|
+
input.style.left = x * g.scale * g.fitScale + "px";
|
|
2072
|
+
input.style.top = y * g.scale * g.fitScale + "px";
|
|
2073
|
+
input.style.border = "2px solid #000";
|
|
2074
|
+
input.style.borderRadius = "5px";
|
|
2075
|
+
input.style.color = "#000";
|
|
2076
|
+
input.style.backgroundColor = "#fff";
|
|
2077
|
+
input.style.fontFamily = g.font;
|
|
2078
|
+
input.style.fontSize = g.textSize;
|
|
2079
|
+
input.type = options.type ? options.type : "text";
|
|
2080
|
+
if (options.placeholder) input.placeholder = options.placeholder;
|
|
2081
|
+
T.container.appendChild(input);
|
|
2082
|
+
return input;
|
|
2083
|
+
}
|
|
2084
|
+
var elemApi = {
|
|
2085
|
+
/**
|
|
2086
|
+
* Every `Elem.Elem` created so far, including buttons, in creation order. The game uses it to reposition them when the window is resized. Inputs from `Input()` are not in it.
|
|
2087
|
+
*
|
|
2088
|
+
* @example
|
|
2089
|
+
* ```js
|
|
2090
|
+
* var game = new THRUNIUM.Game(400, 400);
|
|
2091
|
+
* THRUNIUM.Elem.Button('Play', 100, 100);
|
|
2092
|
+
* THRUNIUM.Elem.elemsList.length; // 1
|
|
2093
|
+
* ```
|
|
2094
|
+
*/
|
|
2095
|
+
elemsList: [],
|
|
2096
|
+
Elem: ElemElem,
|
|
2097
|
+
Button,
|
|
2098
|
+
Input
|
|
2099
|
+
};
|
|
2100
|
+
|
|
2101
|
+
// src/ajax.ts
|
|
2102
|
+
function ajax(data) {
|
|
2103
|
+
var _a3, _b, _c, _d;
|
|
2104
|
+
const success = (_a3 = data.success) != null ? _a3 : ((e) => console.log(e));
|
|
2105
|
+
const error = (_b = data.error) != null ? _b : (() => {
|
|
2106
|
+
});
|
|
2107
|
+
const contentType = (_c = data.contentType) != null ? _c : "application/x-www-form-urlencoded";
|
|
2108
|
+
const xhttp = new XMLHttpRequest();
|
|
2109
|
+
xhttp.withCredentials = (_d = data.withCredentials) != null ? _d : false;
|
|
2110
|
+
xhttp.onreadystatechange = function() {
|
|
2111
|
+
if (xhttp.readyState !== 4) return;
|
|
2112
|
+
if (xhttp.status === 200) success(xhttp);
|
|
2113
|
+
else error(xhttp);
|
|
2114
|
+
};
|
|
2115
|
+
xhttp.open(data.type, data.url, true);
|
|
2116
|
+
if (contentType !== false) xhttp.setRequestHeader("Content-Type", contentType);
|
|
2117
|
+
xhttp.send(data.data);
|
|
2118
|
+
return xhttp;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/storage.ts
|
|
2122
|
+
function escapeValue(value) {
|
|
2123
|
+
return value.replace(/[%;\x00-\x1f\x7f]/g, encodeURIComponent).replace(/^ +| +$/g, (spaces) => spaces.replace(/ /g, "%20"));
|
|
2124
|
+
}
|
|
2125
|
+
function set(name, value) {
|
|
2126
|
+
const date = /* @__PURE__ */ new Date();
|
|
2127
|
+
date.setTime(date.getTime() + 365 * 24 * 60 * 60 * 1e3);
|
|
2128
|
+
const expires = "expires=" + date.toUTCString();
|
|
2129
|
+
document.cookie = name + "=" + escapeValue(String(value)) + ";" + expires + ";path=" + document.location.pathname;
|
|
2130
|
+
return true;
|
|
2131
|
+
}
|
|
2132
|
+
function get(name) {
|
|
2133
|
+
const prefix = name + "=";
|
|
2134
|
+
for (let c of document.cookie.split(";")) {
|
|
2135
|
+
c = c.replace(/^ +/, "");
|
|
2136
|
+
if (c.indexOf(prefix) === 0) {
|
|
2137
|
+
const raw = c.substring(prefix.length);
|
|
2138
|
+
try {
|
|
2139
|
+
return decodeURIComponent(raw);
|
|
2140
|
+
} catch (e) {
|
|
2141
|
+
return raw;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
return null;
|
|
2146
|
+
}
|
|
2147
|
+
var storage = { set, get };
|
|
2148
|
+
|
|
2149
|
+
// src/storage-token.ts
|
|
2150
|
+
var storage_token_exports = {};
|
|
2151
|
+
__export(storage_token_exports, {
|
|
2152
|
+
_options: () => _options,
|
|
2153
|
+
_receiveMessage: () => _receiveMessage,
|
|
2154
|
+
accessRequested: () => accessRequested,
|
|
2155
|
+
loginPopup: () => loginPopup,
|
|
2156
|
+
reqToken: () => reqToken,
|
|
2157
|
+
reqUserToken: () => reqUserToken,
|
|
2158
|
+
request: () => request,
|
|
2159
|
+
setReqToken: () => setReqToken,
|
|
2160
|
+
timeout: () => timeout,
|
|
2161
|
+
userToken: () => userToken
|
|
2162
|
+
});
|
|
2163
|
+
var reqToken = null;
|
|
2164
|
+
var userToken = null;
|
|
2165
|
+
var timeout = null;
|
|
2166
|
+
var loginPopup = null;
|
|
2167
|
+
var accessRequested = false;
|
|
2168
|
+
var _options = {
|
|
2169
|
+
succes: (e) => console.log(e),
|
|
2170
|
+
error: (e) => console.log(e)
|
|
2171
|
+
};
|
|
2172
|
+
function setReqToken(token) {
|
|
2173
|
+
T.Storage.reqToken = token;
|
|
2174
|
+
}
|
|
2175
|
+
function reqUserToken(options) {
|
|
2176
|
+
T.Storage._options.succes = options.succes;
|
|
2177
|
+
T.Storage._options.error = options.error;
|
|
2178
|
+
ajax({
|
|
2179
|
+
url: "https://api.thrungames.com/request/user-token",
|
|
2180
|
+
type: "post",
|
|
2181
|
+
data: "requestToken=" + T.Storage.reqToken,
|
|
2182
|
+
// 1.0.0 typo `succes` kept: ajax ignores it (part of why the flow is WIP).
|
|
2183
|
+
succes: function(e) {
|
|
2184
|
+
var _a3, _b, _c, _d;
|
|
2185
|
+
const response = JSON.parse(e.response);
|
|
2186
|
+
if (response.access == "0" && !T.Storage.accessRequested) {
|
|
2187
|
+
window.addEventListener("message", T.Storage._receiveMessage, false);
|
|
2188
|
+
window.parent.postMessage({ reqToken: T.Storage.reqToken }, document.origin);
|
|
2189
|
+
T.Storage.timeout = setTimeout(function() {
|
|
2190
|
+
const leftPosition = screen.width ? (screen.width - 400) / 2 : 0;
|
|
2191
|
+
const topPosition = screen.height ? (screen.height - 500) / 2 : 0;
|
|
2192
|
+
const settings = "height=500,width=400,top=" + topPosition + ",left=" + leftPosition + ",scrollbars=yes,resizable";
|
|
2193
|
+
const url = "https://api.thrungames.com/user/access?token=" + T.Storage.reqToken;
|
|
2194
|
+
T.Storage.loginPopup = window.open(url, "Approval of game", settings);
|
|
2195
|
+
}, 200);
|
|
2196
|
+
} else if (response.access == "1") {
|
|
2197
|
+
T.Storage.userToken = window.event.data.userToken;
|
|
2198
|
+
(_b = (_a3 = T.Storage._options).succes) == null ? void 0 : _b.call(_a3, { type: "userAccepted", event: e });
|
|
2199
|
+
} else {
|
|
2200
|
+
(_d = (_c = T.Storage._options).error) == null ? void 0 : _d.call(_c, { type: "ajaxFailed", event: e });
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
error: function(e) {
|
|
2204
|
+
var _a3, _b;
|
|
2205
|
+
(_b = (_a3 = T.Storage._options).error) == null ? void 0 : _b.call(_a3, { type: "ajaxFailed", event: e });
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
function _receiveMessage(e) {
|
|
2210
|
+
var _a3, _b, _c;
|
|
2211
|
+
const event = window.event;
|
|
2212
|
+
if (event.origin !== "https://www.thrungames.com/" && event.origin !== "https://www.thrungames.com/") return;
|
|
2213
|
+
clearTimeout((_a3 = T.Storage.timeout) != null ? _a3 : void 0);
|
|
2214
|
+
T.Storage.accessRequested = true;
|
|
2215
|
+
if (event.data.state == 1) {
|
|
2216
|
+
T.Storage.reqUserToken(T.Storage._options);
|
|
2217
|
+
} else {
|
|
2218
|
+
(_c = (_b = T.Storage._options).error) == null ? void 0 : _c.call(_b, { type: "userRejected", event: e });
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
function request(_options2) {
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// src/index.ts
|
|
2225
|
+
T.version = "1.2.0-rc.2";
|
|
2226
|
+
T.pageLoad = pageLoad;
|
|
2227
|
+
T.ready = ready;
|
|
2228
|
+
T.throwError = throwError;
|
|
2229
|
+
T.mobileAndTabletcheck = mobileAndTabletcheck;
|
|
2230
|
+
T.iOScheck = iOScheck;
|
|
2231
|
+
T.getRandom = getRandom;
|
|
2232
|
+
initEnv();
|
|
2233
|
+
T.Game = Game;
|
|
2234
|
+
T.Vector = Vector;
|
|
2235
|
+
T.Collider = Object.assign(T.Collider || {}, Collider);
|
|
2236
|
+
T.Sprite = Sprite;
|
|
2237
|
+
T.Elem = Object.assign(T.Elem || {}, elemApi);
|
|
2238
|
+
T.ajax = ajax;
|
|
2239
|
+
T.Storage = Object.assign(T.Storage || {}, storage, storage_token_exports);
|
|
2240
|
+
window.requestAnimFrame = typeof window.requestAnimationFrame === "function" ? window.requestAnimationFrame.bind(window) : (cb) => window.setTimeout(() => cb(Date.now()), 1e3 / 60);
|
|
2241
|
+
})();
|