littlejsengine 1.10.4 → 1.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -78
- package/dist/littlejs.d.ts +31 -17
- package/dist/littlejs.esm.js +121 -55
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +121 -55
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +121 -55
- package/examples/box2d/game.js +15 -4
- package/examples/box2d/gameObjects.js +10 -9
- package/examples/box2d/index.html +5 -5
- package/examples/box2d/scenes.js +4 -4
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/game.js +3 -0
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/index.html +3 -3
- package/examples/js13k/game.js +3 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/game.js +3 -0
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +4 -1
- package/examples/platformer/game.js +19 -15
- package/examples/platformer/gameCharacter.js +3 -3
- package/examples/platformer/gameLevel.js +1 -0
- package/examples/platformer/index.html +8 -8
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/game.js +3 -0
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +2 -0
- package/examples/typescript/game.ts +3 -0
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +23 -16
- package/examples/uiSystem/index.html +3 -14
- package/package.json +1 -1
- package/plugins/postProcess.js +2 -9
- package/plugins/uiSystem.js +84 -24
- package/src/engine.js +11 -11
- package/src/engineAudio.js +7 -13
- package/src/engineDraw.js +1 -1
- package/src/engineInput.js +7 -3
- package/src/engineMedals.js +19 -5
- package/src/engineObject.js +8 -5
- package/src/engineTileLayer.js +8 -7
- package/src/engineUtilities.js +59 -10
- package/examples/js13k/build/index.html +0 -1
- package/examples/js13k/build/index.js +0 -1
- package/examples/js13k/build/tiles.png +0 -0
- package/examples/js13k/game - Copy.zip +0 -0
- package/examples/js13k/game.zip +0 -0
- package/examples/starter/build/index.html +0 -2
- package/examples/starter/build/index.js +0 -1
- package/examples/starter/build/tiles.png +0 -0
- package/examples/starter/game.zip +0 -0
- package/examples/typescript/build/build/littlejs.esm.js +0 -4327
- package/examples/typescript/build/dist/littlejs.esm.js +0 -4425
- package/examples/typescript/build/examples/typescript/build.js +0 -24
- package/examples/typescript/build/examples/typescript/game.js +0 -100
- package/examples/typescript/build/examples/typescript/test/build/littlejs.esm.js +0 -3934
- package/examples/typescript/build/examples/typescript/test/examples/typescript/build.js +0 -86
- package/examples/typescript/build/examples/typescript/test/examples/typescript/game.js +0 -92
package/plugins/uiSystem.js
CHANGED
|
@@ -40,7 +40,7 @@ function initUISystem(context=overlayContext)
|
|
|
40
40
|
o.pos = o.localPos.add(o.parent.pos);
|
|
41
41
|
o.update();
|
|
42
42
|
for(const c of o.children)
|
|
43
|
-
updateObject(c)
|
|
43
|
+
updateObject(c);
|
|
44
44
|
}
|
|
45
45
|
uiObjects.forEach(o=> o.parent || updateObject(o));
|
|
46
46
|
}
|
|
@@ -50,9 +50,11 @@ function initUISystem(context=overlayContext)
|
|
|
50
50
|
{
|
|
51
51
|
if (!o.visible)
|
|
52
52
|
return;
|
|
53
|
+
if (o.parent)
|
|
54
|
+
o.pos = o.localPos.add(o.parent.pos);
|
|
53
55
|
o.render();
|
|
54
56
|
for(const c of o.children)
|
|
55
|
-
renderObject(c)
|
|
57
|
+
renderObject(c);
|
|
56
58
|
}
|
|
57
59
|
uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
58
60
|
}
|
|
@@ -82,9 +84,9 @@ function drawUILine(posA, posB, thickness=uiDefaultLineWidth, color=uiDefaultLin
|
|
|
82
84
|
uiContext.stroke();
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
function drawUITile(pos, size, tileInfo, color=uiDefaultColor, angle=0, mirror=false
|
|
87
|
+
function drawUITile(pos, size, tileInfo, color=uiDefaultColor, angle=0, mirror=false)
|
|
86
88
|
{
|
|
87
|
-
drawTile(pos, size, tileInfo, color, angle, mirror,
|
|
89
|
+
drawTile(pos, size, tileInfo, color, angle, mirror, BLACK, false, true, uiContext);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
function drawUIText(text, pos, size, color=uiDefaultColor, lineWidth=uiDefaultLineWidth, lineColor=uiDefaultLineColor, align='center', font=uiDefaultFont)
|
|
@@ -96,7 +98,7 @@ function drawUIText(text, pos, size, color=uiDefaultColor, lineWidth=uiDefaultLi
|
|
|
96
98
|
|
|
97
99
|
class UIObject
|
|
98
100
|
{
|
|
99
|
-
constructor(localPos, size=vec2())
|
|
101
|
+
constructor(localPos=vec2(), size=vec2())
|
|
100
102
|
{
|
|
101
103
|
this.localPos = localPos.copy();
|
|
102
104
|
this.pos = localPos.copy();
|
|
@@ -131,21 +133,28 @@ class UIObject
|
|
|
131
133
|
{
|
|
132
134
|
// track mouse input
|
|
133
135
|
const mouseWasOver = this.mouseIsOver;
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
const mouseDown = mouseIsDown(0);
|
|
137
|
+
if (!mouseDown || isTouchDevice)
|
|
138
|
+
{
|
|
139
|
+
this.mouseIsOver = isOverlapping(this.pos, this.size, mousePosScreen);
|
|
140
|
+
if (!mouseDown && isTouchDevice)
|
|
141
|
+
this.mouseIsOver = false;
|
|
142
|
+
if (this.mouseIsOver && !mouseWasOver)
|
|
143
|
+
this.onEnter();
|
|
144
|
+
if (!this.mouseIsOver && mouseWasOver)
|
|
145
|
+
this.onLeave();
|
|
146
|
+
}
|
|
139
147
|
if (mouseWasPressed(0) && this.mouseIsOver)
|
|
140
148
|
{
|
|
141
149
|
this.mouseIsHeld = true;
|
|
142
150
|
this.onPress();
|
|
151
|
+
if (isTouchDevice)
|
|
152
|
+
this.mouseIsOver = false;
|
|
143
153
|
}
|
|
144
|
-
else if (this.mouseIsHeld && !
|
|
154
|
+
else if (this.mouseIsHeld && !mouseDown)
|
|
145
155
|
{
|
|
146
156
|
this.mouseIsHeld = false;
|
|
147
|
-
|
|
148
|
-
this.onClick();
|
|
157
|
+
this.onRelease();
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
160
|
render()
|
|
@@ -155,17 +164,18 @@ class UIObject
|
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
// callback functions
|
|
158
|
-
onEnter()
|
|
159
|
-
onLeave()
|
|
160
|
-
onPress()
|
|
161
|
-
|
|
167
|
+
onEnter() {}
|
|
168
|
+
onLeave() {}
|
|
169
|
+
onPress() {}
|
|
170
|
+
onRelease() {}
|
|
171
|
+
onChange() {}
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
///////////////////////////////////////////////////////////////////////////////
|
|
165
175
|
|
|
166
176
|
class UIText extends UIObject
|
|
167
177
|
{
|
|
168
|
-
constructor(pos, size, text, align='center', font=fontDefault)
|
|
178
|
+
constructor(pos, size, text='', align='center', font=fontDefault)
|
|
169
179
|
{
|
|
170
180
|
super(pos, size);
|
|
171
181
|
|
|
@@ -184,7 +194,7 @@ class UIText extends UIObject
|
|
|
184
194
|
|
|
185
195
|
class UITile extends UIObject
|
|
186
196
|
{
|
|
187
|
-
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false
|
|
197
|
+
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false)
|
|
188
198
|
{
|
|
189
199
|
super(pos, size);
|
|
190
200
|
|
|
@@ -192,11 +202,10 @@ class UITile extends UIObject
|
|
|
192
202
|
this.color = color;
|
|
193
203
|
this.angle = angle;
|
|
194
204
|
this.mirror = mirror;
|
|
195
|
-
this.additiveColor = additiveColor;
|
|
196
205
|
}
|
|
197
206
|
render()
|
|
198
207
|
{
|
|
199
|
-
drawUITile(this.pos, this.size, this.tileInfo, this.color, this.angle, this.mirror
|
|
208
|
+
drawUITile(this.pos, this.size, this.tileInfo, this.color, this.angle, this.mirror);
|
|
200
209
|
}
|
|
201
210
|
}
|
|
202
211
|
|
|
@@ -215,8 +224,9 @@ class UIButton extends UIObject
|
|
|
215
224
|
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
216
225
|
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
217
226
|
drawUIRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
const textSize = vec2(this.size.x, this.size.y*.8);
|
|
228
|
+
drawUIText(this.text, this.pos, textSize,
|
|
229
|
+
this.textColor, 0, undefined, this.align, this.font);
|
|
220
230
|
}
|
|
221
231
|
}
|
|
222
232
|
|
|
@@ -229,7 +239,11 @@ class UICheckbox extends UIObject
|
|
|
229
239
|
super(pos, size);
|
|
230
240
|
this.checked = checked;
|
|
231
241
|
}
|
|
232
|
-
|
|
242
|
+
onPress()
|
|
243
|
+
{
|
|
244
|
+
this.checked = !this.checked;
|
|
245
|
+
this.onChange();
|
|
246
|
+
}
|
|
233
247
|
render()
|
|
234
248
|
{
|
|
235
249
|
drawUIRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor);
|
|
@@ -241,3 +255,49 @@ class UICheckbox extends UIObject
|
|
|
241
255
|
}
|
|
242
256
|
}
|
|
243
257
|
}
|
|
258
|
+
|
|
259
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
260
|
+
|
|
261
|
+
class UIScrollbar extends UIObject
|
|
262
|
+
{
|
|
263
|
+
constructor(pos, size, value=.5, text='')
|
|
264
|
+
{
|
|
265
|
+
super(pos, size);
|
|
266
|
+
this.value = value;
|
|
267
|
+
this.text = text;
|
|
268
|
+
this.color = uiDefaultButtonColor;
|
|
269
|
+
this.handleColor = WHITE;
|
|
270
|
+
}
|
|
271
|
+
update()
|
|
272
|
+
{
|
|
273
|
+
super.update();
|
|
274
|
+
if (this.mouseIsHeld)
|
|
275
|
+
{
|
|
276
|
+
const handleSize = vec2(this.size.y);
|
|
277
|
+
const handleWidth = this.size.x - handleSize.x;
|
|
278
|
+
const p1 = this.pos.x - handleWidth/2;
|
|
279
|
+
const p2 = this.pos.x + handleWidth/2;
|
|
280
|
+
const oldValue = this.value;
|
|
281
|
+
this.value = percent(mousePosScreen.x, p1, p2);
|
|
282
|
+
this.value == oldValue || this.onChange();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
render()
|
|
286
|
+
{
|
|
287
|
+
const lineColor = this.mouseIsHeld ? this.color : this.lineColor;
|
|
288
|
+
const color = this.mouseIsOver? this.hoverColor : this.color;
|
|
289
|
+
drawUIRect(this.pos, this.size, color, this.lineWidth, lineColor);
|
|
290
|
+
|
|
291
|
+
const handleSize = vec2(this.size.y);
|
|
292
|
+
const handleWidth = this.size.x - handleSize.x;
|
|
293
|
+
const p1 = this.pos.x - handleWidth/2;
|
|
294
|
+
const p2 = this.pos.x + handleWidth/2;
|
|
295
|
+
const handlePos = vec2(lerp(this.value, p1, p2), this.pos.y);
|
|
296
|
+
const barColor = this.mouseIsHeld ? this.color : this.handleColor;
|
|
297
|
+
drawUIRect(handlePos, handleSize, barColor, this.lineWidth, this.lineColor);
|
|
298
|
+
|
|
299
|
+
const textSize = vec2(this.size.x, this.size.y*.8);
|
|
300
|
+
drawUIText(this.text, this.pos, textSize,
|
|
301
|
+
this.textColor, 0, undefined, this.align, this.font);
|
|
302
|
+
}
|
|
303
|
+
}
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {String}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.10.
|
|
33
|
+
const engineVersion = '1.10.7';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {Number}
|
|
@@ -93,6 +93,8 @@ const pluginUpdateList = [], pluginRenderList = [];
|
|
|
93
93
|
* @memberof Engine */
|
|
94
94
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
95
95
|
{
|
|
96
|
+
ASSERT(!pluginUpdateList.includes(updateFunction));
|
|
97
|
+
ASSERT(!pluginRenderList.includes(renderFunction));
|
|
96
98
|
updateFunction && pluginUpdateList.push(updateFunction);
|
|
97
99
|
renderFunction && pluginRenderList.push(renderFunction);
|
|
98
100
|
}
|
|
@@ -101,12 +103,12 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
101
103
|
// Main engine functions
|
|
102
104
|
|
|
103
105
|
/** Startup LittleJS engine with your callback functions
|
|
104
|
-
* @param {Function} gameInit
|
|
105
|
-
* @param {Function} gameUpdate
|
|
106
|
-
* @param {Function} gameUpdatePost - Called after physics and objects are updated,
|
|
107
|
-
* @param {Function} gameRender
|
|
108
|
-
* @param {Function} gameRenderPost - Called after objects are rendered,
|
|
109
|
-
* @param {Array} [imageSources=[]] -
|
|
106
|
+
* @param {Function|function():Promise} gameInit - Called once after the engine starts up
|
|
107
|
+
* @param {Function} gameUpdate - Called every frame before objects are updated
|
|
108
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
|
|
109
|
+
* @param {Function} gameRender - Called before objects are rendered, for drawing the background
|
|
110
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
|
|
111
|
+
* @param {Array} [imageSources=[]] - List of images to load
|
|
110
112
|
* @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
|
|
111
113
|
* @memberof Engine */
|
|
112
114
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -256,8 +258,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
256
258
|
|
|
257
259
|
function startEngine()
|
|
258
260
|
{
|
|
259
|
-
gameInit();
|
|
260
|
-
engineUpdate();
|
|
261
|
+
new Promise((resolve) => resolve(gameInit())).then(engineUpdate);
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
if (headlessMode)
|
|
@@ -272,7 +273,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
272
273
|
'width:100vw;height:100vh;' + // fill the window
|
|
273
274
|
'display:flex;' + // use flexbox
|
|
274
275
|
'align-items:center;' + // horizontal center
|
|
275
|
-
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
276
276
|
'justify-content:center;' + // vertical center
|
|
277
277
|
'background:#000;' + // set background color
|
|
278
278
|
'user-select:none;' + // prevent hold to select
|
|
@@ -610,4 +610,4 @@ function drawEngineSplashScreen(t)
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
x.restore();
|
|
613
|
-
}
|
|
613
|
+
}
|
package/src/engineAudio.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
/** Audio context used by the engine
|
|
15
15
|
* @type {AudioContext}
|
|
16
16
|
* @memberof Audio */
|
|
17
|
-
let audioContext;
|
|
17
|
+
let audioContext = new AudioContext;
|
|
18
18
|
|
|
19
19
|
/** Master gain node for all audio to pass through
|
|
20
20
|
* @type {GainNode}
|
|
@@ -25,14 +25,10 @@ function audioInit()
|
|
|
25
25
|
{
|
|
26
26
|
if (!soundEnable || headlessMode) return;
|
|
27
27
|
|
|
28
|
-
// create audio context
|
|
29
|
-
audioContext = new AudioContext;
|
|
30
|
-
|
|
31
|
-
// create and connect gain node
|
|
32
28
|
// (createGain is more widely spported then GainNode construtor)
|
|
33
29
|
audioGainNode = audioContext.createGain();
|
|
34
30
|
audioGainNode.connect(audioContext.destination);
|
|
35
|
-
|
|
31
|
+
audioGainNode.gain.value = soundVolume; // set starting value
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -67,12 +63,15 @@ class Sound
|
|
|
67
63
|
|
|
68
64
|
/** @property {Number} - How much to randomize frequency each time sound plays */
|
|
69
65
|
this.randomness = 0;
|
|
66
|
+
|
|
67
|
+
/** @property {GainNode} - Gain node for this sound */
|
|
68
|
+
this.gainNode = audioContext.createGain();
|
|
70
69
|
|
|
71
70
|
if (zzfxSound)
|
|
72
71
|
{
|
|
73
72
|
// generate zzfx sound now for fast playback
|
|
74
73
|
const defaultRandomness = .05;
|
|
75
|
-
this.randomness = zzfxSound[1]
|
|
74
|
+
this.randomness = zzfxSound[1] != undefined ? zzfxSound[1] : defaultRandomness;
|
|
76
75
|
zzfxSound[1] = 0; // generate without randomness
|
|
77
76
|
this.sampleChannels = [zzfxG(...zzfxSound)];
|
|
78
77
|
this.sampleRate = zzfxR;
|
|
@@ -113,18 +112,13 @@ class Sound
|
|
|
113
112
|
|
|
114
113
|
// play the sound
|
|
115
114
|
const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
|
|
116
|
-
this.gainNode = audioContext.createGain();
|
|
117
115
|
return this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
|
|
118
116
|
}
|
|
119
117
|
|
|
120
118
|
/** Set the sound volume
|
|
121
119
|
* @param {Number} [volume] - How much to scale volume by
|
|
122
120
|
*/
|
|
123
|
-
setVolume(volume=1)
|
|
124
|
-
{
|
|
125
|
-
if (this.gainNode)
|
|
126
|
-
this.gainNode.gain.value = volume;
|
|
127
|
-
}
|
|
121
|
+
setVolume(volume=1) { this.gainNode.gain.value = volume; }
|
|
128
122
|
|
|
129
123
|
/** Stop the last instance of this sound that was played */
|
|
130
124
|
stop()
|
package/src/engineDraw.js
CHANGED
|
@@ -70,7 +70,7 @@ let drawCount;
|
|
|
70
70
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
71
71
|
* tile(5, 8) // a tile at index 5 using a tile size of 8
|
|
72
72
|
* tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
|
|
73
|
-
* tile(vec2(4,8), vec2(30,10)) // a tile at
|
|
73
|
+
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
74
74
|
* @memberof Draw
|
|
75
75
|
*/
|
|
76
76
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
package/src/engineInput.js
CHANGED
|
@@ -45,7 +45,7 @@ function keyWasReleased(key, device=0)
|
|
|
45
45
|
|
|
46
46
|
/** Clears all input
|
|
47
47
|
* @memberof Input */
|
|
48
|
-
function clearInput() { inputData = [[]]; }
|
|
48
|
+
function clearInput() { inputData = [[]]; touchGamepadButtons = []; }
|
|
49
49
|
|
|
50
50
|
/** Returns true if mouse button is down
|
|
51
51
|
* @function
|
|
@@ -210,6 +210,7 @@ function inputInit()
|
|
|
210
210
|
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
211
211
|
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
212
212
|
oncontextmenu = (e)=> false; // prevent right click menu
|
|
213
|
+
onblur = (e) => clearInput(); // reset input when focus is lost
|
|
213
214
|
|
|
214
215
|
// init touch input
|
|
215
216
|
if (isTouchDevice && touchInputEnable)
|
|
@@ -408,10 +409,13 @@ function touchInputInit()
|
|
|
408
409
|
if (touching)
|
|
409
410
|
{
|
|
410
411
|
touchGamepadTimer.set();
|
|
411
|
-
if (paused)
|
|
412
|
+
if (paused && !wasTouching)
|
|
412
413
|
{
|
|
413
414
|
// touch anywhere to press start when paused
|
|
414
415
|
touchGamepadButtons[9] = 1;
|
|
416
|
+
|
|
417
|
+
// call default touch handler so normal touch events still work
|
|
418
|
+
handleTouchDefault(e);
|
|
415
419
|
return;
|
|
416
420
|
}
|
|
417
421
|
}
|
|
@@ -436,7 +440,7 @@ function touchInputInit()
|
|
|
436
440
|
const button = touchPos.subtract(buttonCenter).direction();
|
|
437
441
|
touchGamepadButtons[button] = 1;
|
|
438
442
|
}
|
|
439
|
-
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
443
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize && !wasTouching)
|
|
440
444
|
{
|
|
441
445
|
// virtual start button in center
|
|
442
446
|
touchGamepadButtons[9] = 1;
|
package/src/engineMedals.js
CHANGED
|
@@ -28,7 +28,7 @@ function medalsInit(saveName)
|
|
|
28
28
|
// check if medals are unlocked
|
|
29
29
|
medalsSaveName = saveName;
|
|
30
30
|
if (!debugMedals)
|
|
31
|
-
medalsForEach(medal=> medal.unlocked =
|
|
31
|
+
medalsForEach(medal=> medal.unlocked = !!localStorage[medal.storageKey()]);
|
|
32
32
|
|
|
33
33
|
// engine automatically renders medals
|
|
34
34
|
engineAddPlugin(undefined, medalsRender);
|
|
@@ -91,14 +91,28 @@ class Medal
|
|
|
91
91
|
constructor(id, name, description='', icon='🏆', src)
|
|
92
92
|
{
|
|
93
93
|
ASSERT(id >= 0 && !medals[id]);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
|
|
95
|
+
/** @property {Number} - The unique identifier of the medal */
|
|
96
|
+
this.id = id;
|
|
97
|
+
|
|
98
|
+
/** @property {String} - Name of the medal */
|
|
97
99
|
this.name = name;
|
|
100
|
+
|
|
101
|
+
/** @property {String} - Description of the medal */
|
|
98
102
|
this.description = description;
|
|
103
|
+
|
|
104
|
+
/** @property {String} - Icon for the medal */
|
|
99
105
|
this.icon = icon;
|
|
106
|
+
|
|
107
|
+
/** @property {Boolean} - Is the medal unlocked? */
|
|
108
|
+
this.unlocked = false;
|
|
109
|
+
|
|
110
|
+
// load the source image if provided
|
|
100
111
|
if (src)
|
|
101
112
|
(this.image = new Image).src = src;
|
|
113
|
+
|
|
114
|
+
// add this to list of medals
|
|
115
|
+
medals[id] = this;
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
/** Unlocks a medal if not already unlocked */
|
|
@@ -109,7 +123,7 @@ class Medal
|
|
|
109
123
|
|
|
110
124
|
// save the medal
|
|
111
125
|
ASSERT(medalsSaveName, 'save name must be set');
|
|
112
|
-
localStorage[this.storageKey()] = this.unlocked =
|
|
126
|
+
localStorage[this.storageKey()] = this.unlocked = true;
|
|
113
127
|
medalsDisplayQueue.push(this);
|
|
114
128
|
}
|
|
115
129
|
|
package/src/engineObject.js
CHANGED
|
@@ -39,7 +39,7 @@ class EngineObject
|
|
|
39
39
|
* @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
|
|
40
40
|
* @param {Number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
41
41
|
*/
|
|
42
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
|
|
42
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
|
|
43
43
|
{
|
|
44
44
|
// set passed in params
|
|
45
45
|
ASSERT(isVector2(pos) && isVector2(size), 'ensure pos and size are vec2s');
|
|
@@ -153,15 +153,18 @@ class EngineObject
|
|
|
153
153
|
|
|
154
154
|
// apply physics
|
|
155
155
|
const oldPos = this.pos.copy();
|
|
156
|
-
this.
|
|
157
|
-
this.
|
|
158
|
-
|
|
156
|
+
this.velocity.x *= this.damping;
|
|
157
|
+
this.velocity.y *= this.damping;
|
|
158
|
+
if (this.mass) // dont apply gravity to static objects
|
|
159
|
+
this.velocity.y += gravity * this.gravityScale;
|
|
160
|
+
this.pos.x += this.velocity.x;
|
|
161
|
+
this.pos.y += this.velocity.y;
|
|
159
162
|
this.angle += this.angleVelocity *= this.angleDamping;
|
|
160
163
|
|
|
161
164
|
// physics sanity checks
|
|
162
165
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
163
166
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
164
|
-
if (!enablePhysicsSolver || !this.mass) // dont do collision for
|
|
167
|
+
if (!enablePhysicsSolver || !this.mass) // dont do collision for static objects
|
|
165
168
|
return;
|
|
166
169
|
|
|
167
170
|
const wasMovingDown = this.velocity.y < 0;
|
package/src/engineTileLayer.js
CHANGED
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
-
/** The tile collision layer
|
|
15
|
+
/** The tile collision layer grid, use setTileCollisionData and getTileCollisionData to access
|
|
16
16
|
* @type {Array}
|
|
17
17
|
* @memberof TileCollision */
|
|
18
18
|
let tileCollision = [];
|
|
19
19
|
|
|
20
|
-
/** Size of the tile collision layer
|
|
20
|
+
/** Size of the tile collision layer 2d grid
|
|
21
21
|
* @type {Vector2}
|
|
22
22
|
* @memberof TileCollision */
|
|
23
23
|
let tileCollisionSize = vec2();
|
|
24
24
|
|
|
25
25
|
/** Clear and initialize tile collision
|
|
26
|
-
* @param {Vector2} size
|
|
26
|
+
* @param {Vector2} size - width and height of tile collision 2d grid
|
|
27
27
|
* @memberof TileCollision */
|
|
28
28
|
function initTileCollision(size)
|
|
29
29
|
{
|
|
@@ -33,7 +33,7 @@ function initTileCollision(size)
|
|
|
33
33
|
tileCollision[i] = 0;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/** Set tile collision data
|
|
36
|
+
/** Set tile collision data for a given cell in the grid
|
|
37
37
|
* @param {Vector2} pos
|
|
38
38
|
* @param {Number} [data]
|
|
39
39
|
* @memberof TileCollision */
|
|
@@ -42,7 +42,7 @@ function setTileCollisionData(pos, data=0)
|
|
|
42
42
|
pos.arrayCheck(tileCollisionSize) && (tileCollision[(pos.y|0)*tileCollisionSize.x+pos.x|0] = data);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/** Get tile collision data
|
|
45
|
+
/** Get tile collision data for a given cell in the grid
|
|
46
46
|
* @param {Vector2} pos
|
|
47
47
|
* @return {Number}
|
|
48
48
|
* @memberof TileCollision */
|
|
@@ -73,7 +73,8 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/** Return the center of first tile hit
|
|
76
|
+
/** Return the center of first tile hit, undefined if nothing was hit.
|
|
77
|
+
* This does not return the exact intersection, but the center of the tile hit.
|
|
77
78
|
* @param {Vector2} posStart
|
|
78
79
|
* @param {Vector2} posEnd
|
|
79
80
|
* @param {EngineObject} [object]
|
|
@@ -318,8 +319,8 @@ class TileLayer extends EngineObject
|
|
|
318
319
|
const d = this.getData(layerPos);
|
|
319
320
|
if (d.tile != undefined)
|
|
320
321
|
{
|
|
321
|
-
const pos = this.pos.add(layerPos).add(vec2(.5));
|
|
322
322
|
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
323
|
+
const pos = layerPos.add(vec2(.5));
|
|
323
324
|
const tileInfo = tile(d.tile, s, this.tileInfo.textureIndex);
|
|
324
325
|
drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
325
326
|
}
|
package/src/engineUtilities.js
CHANGED
|
@@ -201,7 +201,8 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
201
201
|
* @memberof Random */
|
|
202
202
|
function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
|
|
203
203
|
|
|
204
|
-
/** Returns a floored random value the two values passed in
|
|
204
|
+
/** Returns a floored random value between the two values passed in
|
|
205
|
+
* The upper bound is exclusive. (If 2 is passed in, result will be 0 or 1)
|
|
205
206
|
* @param {Number} valueA
|
|
206
207
|
* @param {Number} [valueB]
|
|
207
208
|
* @return {Number}
|
|
@@ -330,18 +331,24 @@ class Vector2
|
|
|
330
331
|
* @param {Number} [y] - Y axis location */
|
|
331
332
|
constructor(x=0, y=0)
|
|
332
333
|
{
|
|
333
|
-
ASSERT(typeof x == 'number' && typeof y == 'number');
|
|
334
334
|
/** @property {Number} - X axis location */
|
|
335
335
|
this.x = x;
|
|
336
336
|
/** @property {Number} - Y axis location */
|
|
337
337
|
this.y = y;
|
|
338
|
+
ASSERT(this.isValid());
|
|
338
339
|
}
|
|
339
340
|
|
|
340
341
|
/** Sets values of this vector and returns self
|
|
341
342
|
* @param {Number} [x] - X axis location
|
|
342
343
|
* @param {Number} [y] - Y axis location
|
|
343
344
|
* @return {Vector2} */
|
|
344
|
-
set(x=0, y=0)
|
|
345
|
+
set(x=0, y=0)
|
|
346
|
+
{
|
|
347
|
+
this.x = x;
|
|
348
|
+
this.y = y;
|
|
349
|
+
ASSERT(this.isValid());
|
|
350
|
+
return this;
|
|
351
|
+
}
|
|
345
352
|
|
|
346
353
|
/** Returns a new vector that is a copy of this
|
|
347
354
|
* @return {Vector2} */
|
|
@@ -533,6 +540,14 @@ class Vector2
|
|
|
533
540
|
if (debug)
|
|
534
541
|
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
535
542
|
}
|
|
543
|
+
|
|
544
|
+
/** Checks if this is a valid vector
|
|
545
|
+
* @return {Boolean} */
|
|
546
|
+
isValid()
|
|
547
|
+
{
|
|
548
|
+
return typeof this.x == 'number' && !isNaN(this.x)
|
|
549
|
+
&& typeof this.y == 'number' && !isNaN(this.y);
|
|
550
|
+
}
|
|
536
551
|
}
|
|
537
552
|
|
|
538
553
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -593,6 +608,7 @@ class Color
|
|
|
593
608
|
this.b = b;
|
|
594
609
|
/** @property {Number} - Alpha */
|
|
595
610
|
this.a = a;
|
|
611
|
+
ASSERT(this.isValid());
|
|
596
612
|
}
|
|
597
613
|
|
|
598
614
|
/** Sets values of this color and returns self
|
|
@@ -602,7 +618,14 @@ class Color
|
|
|
602
618
|
* @param {Number} [a] - alpha
|
|
603
619
|
* @return {Color} */
|
|
604
620
|
set(r=1, g=1, b=1, a=1)
|
|
605
|
-
{
|
|
621
|
+
{
|
|
622
|
+
this.r = r;
|
|
623
|
+
this.g = g;
|
|
624
|
+
this.b = b;
|
|
625
|
+
this.a = a;
|
|
626
|
+
ASSERT(this.isValid());
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
606
629
|
|
|
607
630
|
/** Returns a new color that is a copy of this
|
|
608
631
|
* @return {Color} */
|
|
@@ -685,6 +708,7 @@ class Color
|
|
|
685
708
|
this.g = f(p, q, h);
|
|
686
709
|
this.b = f(p, q, h - 1/3);
|
|
687
710
|
this.a = a;
|
|
711
|
+
ASSERT(this.isValid());
|
|
688
712
|
return this;
|
|
689
713
|
}
|
|
690
714
|
|
|
@@ -712,7 +736,6 @@ class Color
|
|
|
712
736
|
else if (b == max)
|
|
713
737
|
h = (r - g) / d + 4;
|
|
714
738
|
}
|
|
715
|
-
|
|
716
739
|
return [h / 6, s, l, a];
|
|
717
740
|
}
|
|
718
741
|
|
|
@@ -745,11 +768,27 @@ class Color
|
|
|
745
768
|
* @return {Color} */
|
|
746
769
|
setHex(hex)
|
|
747
770
|
{
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
771
|
+
ASSERT(typeof hex == 'string' && hex[0] == '#');
|
|
772
|
+
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
773
|
+
|
|
774
|
+
if (hex.length < 6)
|
|
775
|
+
{
|
|
776
|
+
const fromHex = (c)=> clamp(parseInt(hex[c],16)/15);
|
|
777
|
+
this.r = fromHex(1);
|
|
778
|
+
this.g = fromHex(2),
|
|
779
|
+
this.b = fromHex(3);
|
|
780
|
+
this.a = hex.length == 5 ? fromHex(4) : 1;
|
|
781
|
+
}
|
|
782
|
+
else
|
|
783
|
+
{
|
|
784
|
+
const fromHex = (c)=> clamp(parseInt(hex.slice(c,c+2),16)/255);
|
|
785
|
+
this.r = fromHex(1);
|
|
786
|
+
this.g = fromHex(3),
|
|
787
|
+
this.b = fromHex(5);
|
|
788
|
+
this.a = hex.length == 9 ? fromHex(7) : 1;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
ASSERT(this.isValid());
|
|
753
792
|
return this;
|
|
754
793
|
}
|
|
755
794
|
|
|
@@ -763,6 +802,16 @@ class Color
|
|
|
763
802
|
const a = clamp(this.a)*255<<24;
|
|
764
803
|
return r + g + b + a;
|
|
765
804
|
}
|
|
805
|
+
|
|
806
|
+
/** Checks if this is a valid color
|
|
807
|
+
* @return {Boolean} */
|
|
808
|
+
isValid()
|
|
809
|
+
{
|
|
810
|
+
return typeof this.r == 'number' && !isNaN(this.r)
|
|
811
|
+
&& typeof this.g == 'number' && !isNaN(this.g)
|
|
812
|
+
&& typeof this.b == 'number' && !isNaN(this.b)
|
|
813
|
+
&& typeof this.a == 'number' && !isNaN(this.a);
|
|
814
|
+
}
|
|
766
815
|
}
|
|
767
816
|
|
|
768
817
|
///////////////////////////////////////////////////////////////////////////////
|