littlejsengine 1.14.10 → 1.14.16
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/dist/littlejs.d.ts +264 -120
- package/dist/littlejs.esm.js +603 -288
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +600 -287
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +521 -232
- package/examples/box2d/gameObjects.js +2 -2
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakoutTutorial/README.md +32 -32
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/electron/game.js +3 -3
- package/examples/electron/index.html +2 -2
- package/examples/electron/package.json +1 -8
- package/examples/index.html +59 -51
- package/examples/module/game.js +3 -3
- package/examples/platformer/gameCharacter.js +6 -2
- package/examples/platformer/gameEffects.js +4 -4
- package/examples/platformer/gameLevel.js +1 -1
- package/examples/platformer/gameObjects.js +11 -9
- package/examples/puzzle/game.js +1 -1
- package/examples/shorts/animation.js +1 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/blending.js +8 -14
- package/examples/shorts/box2d.js +7 -3
- package/examples/shorts/box2dCar.js +2 -1
- package/examples/shorts/empty.js +30 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +10 -4
- package/examples/shorts/landerGame.js +30 -3
- package/examples/shorts/medals.js +4 -4
- package/examples/shorts/music.js +29 -55
- package/examples/shorts/musicPlayer.js +134 -0
- package/examples/shorts/nineSlice.js +34 -15
- package/examples/shorts/parallax.js +4 -3
- package/examples/shorts/particles.js +16 -16
- package/examples/shorts/piano.js +15 -21
- package/examples/shorts/pongGame.js +6 -4
- package/examples/shorts/raycasting.js +13 -5
- package/examples/shorts/sequencer.js +122 -0
- package/examples/shorts/shapes.js +7 -4
- package/examples/shorts/slidingPuzzle.js +16 -6
- package/examples/shorts/sound.js +18 -9
- package/examples/shorts/spaceGame.js +12 -8
- package/examples/shorts/spriteAtlas.js +7 -7
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/systemFont.js +2 -2
- package/examples/shorts/texture.js +2 -2
- package/examples/shorts/tileLayer.js +10 -10
- package/examples/shorts/tiltedView.js +14 -3
- package/examples/shorts/timers.js +10 -0
- package/examples/shorts/topDown.js +4 -1
- package/examples/shorts/uiSystem.js +8 -5
- package/examples/starter/game.js +3 -3
- package/examples/starter/index.html +2 -2
- package/examples/typescript/game.js +3 -3
- package/examples/typescript/game.ts +3 -3
- package/examples/uiSystem/game.js +6 -6
- package/package.json +4 -2
- package/plugins/box2d.js +18 -3
- package/plugins/newgrounds.js +11 -9
- package/plugins/postProcess.js +5 -2
- package/plugins/uiSystem.js +120 -34
- package/plugins/zzfxm.js +5 -1
- package/reference.md +1 -3
- package/src/engine.js +52 -21
- package/src/engineAudio.js +31 -14
- package/src/engineDebug.js +80 -55
- package/src/engineDraw.js +60 -31
- package/src/engineExport.js +3 -1
- package/src/engineMedals.js +10 -2
- package/src/engineObject.js +14 -10
- package/src/engineParticles.js +59 -46
- package/src/engineRelease.js +1 -0
- package/src/engineSettings.js +7 -7
- package/src/engineTileLayer.js +49 -18
- package/src/engineUtilities.js +79 -34
package/dist/littlejs.esm.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.16';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -96,9 +96,14 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
96
96
|
|
|
97
97
|
const pluginUpdateList = [], pluginRenderList = [];
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @callback PluginCallback - Update or render function for a plugin
|
|
101
|
+
* @memberof Engine
|
|
102
|
+
*/
|
|
103
|
+
|
|
99
104
|
/** Add a new update function for a plugin
|
|
100
|
-
* @param {
|
|
101
|
-
* @param {
|
|
105
|
+
* @param {PluginCallback} [updateFunction]
|
|
106
|
+
* @param {PluginCallback} [renderFunction]
|
|
102
107
|
* @memberof Engine */
|
|
103
108
|
function engineAddPlugin(updateFunction, renderFunction)
|
|
104
109
|
{
|
|
@@ -111,23 +116,33 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
111
116
|
///////////////////////////////////////////////////////////////////////////////
|
|
112
117
|
// Main Engine Functions
|
|
113
118
|
|
|
119
|
+
/**
|
|
120
|
+
* @callback GameInitCallback - Called after the engine starts, can be async
|
|
121
|
+
* @returns {void|Promise<void>}
|
|
122
|
+
* @memberof Engine
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* @callback GameCallback - Update or render function for the game
|
|
126
|
+
* @memberof Engine
|
|
127
|
+
*/
|
|
128
|
+
|
|
114
129
|
/** Startup LittleJS engine with your callback functions
|
|
115
|
-
* @param {
|
|
116
|
-
* @param {
|
|
117
|
-
* @param {
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
130
|
+
* @param {GameInitCallback} gameInit - Called once after the engine starts up, can be async for loading
|
|
131
|
+
* @param {GameCallback} gameUpdate - Called every frame before objects are updated (60fps), use for game logic
|
|
132
|
+
* @param {GameCallback} gameUpdatePost - Called after physics and objects are updated, even when paused, use for UI updates
|
|
133
|
+
* @param {GameCallback} gameRender - Called before objects are rendered, use for drawing backgrounds/world elements
|
|
134
|
+
* @param {GameCallback} gameRenderPost - Called after objects are rendered, use for drawing UI/overlays
|
|
120
135
|
* @param {Array<string>} [imageSources=[]] - List of image file paths to preload (e.g., ['player.png', 'tiles.png'])
|
|
121
136
|
* @param {HTMLElement} [rootElement] - Root DOM element to attach canvas to, defaults to document.body
|
|
122
137
|
* @example
|
|
123
138
|
* // Basic engine startup
|
|
124
139
|
* engineInit(
|
|
125
|
-
* () => {
|
|
126
|
-
* () => { updateGameLogic(); },
|
|
127
|
-
* () => { updateUI(); },
|
|
128
|
-
* () => { drawBackground(); },
|
|
129
|
-
* () => { drawHUD(); },
|
|
130
|
-
* ['tiles.png', 'tilesLevel.png']
|
|
140
|
+
* () => { LOG('Game initialized!'); }, // gameInit
|
|
141
|
+
* () => { updateGameLogic(); }, // gameUpdate
|
|
142
|
+
* () => { updateUI(); }, // gameUpdatePost
|
|
143
|
+
* () => { drawBackground(); }, // gameRender
|
|
144
|
+
* () => { drawHUD(); }, // gameRenderPost
|
|
145
|
+
* ['tiles.png', 'tilesLevel.png'] // images to load
|
|
131
146
|
* );
|
|
132
147
|
* @memberof Engine */
|
|
133
148
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
@@ -173,16 +188,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
173
188
|
if (!debugSpeedUp)
|
|
174
189
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
175
190
|
|
|
191
|
+
let wasUpdated = false;
|
|
176
192
|
if (paused)
|
|
177
193
|
{
|
|
194
|
+
// update everything except the game and objects
|
|
195
|
+
wasUpdated = true;
|
|
178
196
|
updateCanvas();
|
|
197
|
+
inputUpdate();
|
|
198
|
+
pluginUpdateList.forEach(f=>f());
|
|
179
199
|
|
|
180
200
|
// update object transforms even when paused
|
|
181
201
|
for (const o of engineObjects)
|
|
182
202
|
o.parent || o.updateTransforms();
|
|
183
203
|
|
|
184
|
-
|
|
185
|
-
pluginUpdateList.forEach(f=>f());
|
|
204
|
+
// do post update
|
|
186
205
|
debugUpdate();
|
|
187
206
|
gameUpdatePost();
|
|
188
207
|
inputUpdatePost();
|
|
@@ -199,12 +218,13 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
199
218
|
}
|
|
200
219
|
|
|
201
220
|
// update multiple frames if necessary in case of slow framerate
|
|
202
|
-
for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
221
|
+
for (; frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
203
222
|
{
|
|
204
223
|
// increment frame and update time
|
|
205
224
|
time = frame++ / frameRate;
|
|
206
225
|
|
|
207
226
|
// update game and objects
|
|
227
|
+
wasUpdated = true;
|
|
208
228
|
updateCanvas();
|
|
209
229
|
inputUpdate();
|
|
210
230
|
gameUpdate();
|
|
@@ -215,7 +235,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
215
235
|
debugUpdate();
|
|
216
236
|
gameUpdatePost();
|
|
217
237
|
inputUpdatePost();
|
|
218
|
-
|
|
219
238
|
if (debugVideoCaptureIsActive())
|
|
220
239
|
renderFrame();
|
|
221
240
|
}
|
|
@@ -232,6 +251,10 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
232
251
|
{
|
|
233
252
|
if (headlessMode) return;
|
|
234
253
|
|
|
254
|
+
// canvas must be updated before rendering
|
|
255
|
+
if (!wasUpdated)
|
|
256
|
+
updateCanvas();
|
|
257
|
+
|
|
235
258
|
// render sort then render while removing destroyed objects
|
|
236
259
|
enginePreRender();
|
|
237
260
|
gameRender();
|
|
@@ -360,6 +383,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
360
383
|
const promises = imageSources.map((src, textureIndex)=>
|
|
361
384
|
new Promise(resolve =>
|
|
362
385
|
{
|
|
386
|
+
ASSERT(isString(src), 'imageSources must be an array of strings');
|
|
387
|
+
|
|
363
388
|
const image = new Image;
|
|
364
389
|
image.onerror = image.onload = ()=>
|
|
365
390
|
{
|
|
@@ -475,10 +500,16 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
475
500
|
return collectedObjects;
|
|
476
501
|
}
|
|
477
502
|
|
|
503
|
+
/**
|
|
504
|
+
* @callback ObjectCallbackFunction - Function that processes an object
|
|
505
|
+
* @param {EngineObject} uiObjects
|
|
506
|
+
* @memberof Engine
|
|
507
|
+
*/
|
|
508
|
+
|
|
478
509
|
/** Triggers a callback for each object within a given area
|
|
479
|
-
* @param {Vector2} [pos]
|
|
480
|
-
* @param {Vector2|number} [size]
|
|
481
|
-
* @param {
|
|
510
|
+
* @param {Vector2} [pos] - Center of test area, or undefined for all objects
|
|
511
|
+
* @param {Vector2|number} [size] - Radius of circle if float, rectangle size if Vector2
|
|
512
|
+
* @param {ObjectCallbackFunction} [callbackFunction] - Calls this function on every object that passes the test
|
|
482
513
|
* @param {Array<EngineObject>} [objects=engineObjects] - List of objects to check
|
|
483
514
|
* @memberof Engine */
|
|
484
515
|
function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
|
|
@@ -688,12 +719,6 @@ function drawEngineSplashScreen(t)
|
|
|
688
719
|
* @memberof Debug */
|
|
689
720
|
const debug = true;
|
|
690
721
|
|
|
691
|
-
/** True if asserts are enabled
|
|
692
|
-
* @type {boolean}
|
|
693
|
-
* @default
|
|
694
|
-
* @memberof Debug */
|
|
695
|
-
const enableAsserts = true;
|
|
696
|
-
|
|
697
722
|
/** Size to render debug points by default
|
|
698
723
|
* @type {number}
|
|
699
724
|
* @default
|
|
@@ -724,77 +749,92 @@ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParti
|
|
|
724
749
|
///////////////////////////////////////////////////////////////////////////////
|
|
725
750
|
// Debug helper functions
|
|
726
751
|
|
|
727
|
-
/** Asserts if the expression is false, does
|
|
752
|
+
/** Asserts if the expression is false, does nothing in release builds
|
|
753
|
+
* Halts execution if the assert fails and throws an error
|
|
728
754
|
* @param {boolean} assert
|
|
729
755
|
* @param {...Object} [output] - error message output
|
|
730
756
|
* @memberof Debug */
|
|
731
757
|
function ASSERT(assert, ...output)
|
|
732
758
|
{
|
|
733
|
-
if (
|
|
734
|
-
|
|
759
|
+
if (assert) return;
|
|
760
|
+
console.assert(assert, ...output)
|
|
761
|
+
throw new Error('Assert failed!'); // halt execution
|
|
735
762
|
}
|
|
736
763
|
|
|
764
|
+
/** Log to console if debug is enabled, does nothing in release builds
|
|
765
|
+
* @param {...Object} [output] - message output
|
|
766
|
+
* @memberof Debug */
|
|
767
|
+
function LOG(...output) { console.log(...output); }
|
|
768
|
+
|
|
737
769
|
/** Draw a debug rectangle in world space
|
|
738
770
|
* @param {Vector2} pos
|
|
739
771
|
* @param {Vector2} [size=Vector2()]
|
|
740
|
-
* @param {string}
|
|
741
|
-
* @param {number}
|
|
742
|
-
* @param {number}
|
|
772
|
+
* @param {Color|string} [color]
|
|
773
|
+
* @param {number} [time]
|
|
774
|
+
* @param {number} [angle]
|
|
743
775
|
* @param {boolean} [fill]
|
|
744
776
|
* @memberof Debug */
|
|
745
|
-
function debugRect(pos, size=vec2(), color=
|
|
777
|
+
function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
|
|
746
778
|
{
|
|
747
779
|
if (typeof size === 'number')
|
|
748
780
|
size = vec2(size); // allow passing in floats
|
|
749
|
-
|
|
750
|
-
|
|
781
|
+
if (isColor(color))
|
|
782
|
+
color = color.toString();
|
|
783
|
+
pos = pos.copy();
|
|
784
|
+
size = size.copy();
|
|
785
|
+
const timer = new Timer(time);
|
|
786
|
+
debugPrimitives.push({pos:pos.copy(), size:size.copy(), color, timer, angle, fill});
|
|
751
787
|
}
|
|
752
788
|
|
|
753
789
|
/** Draw a debug poly in world space
|
|
754
790
|
* @param {Vector2} pos
|
|
755
791
|
* @param {Array<Vector2>} points
|
|
756
|
-
* @param {string}
|
|
757
|
-
* @param {number}
|
|
758
|
-
* @param {number}
|
|
792
|
+
* @param {Color|string} [color]
|
|
793
|
+
* @param {number} [time]
|
|
794
|
+
* @param {number} [angle]
|
|
759
795
|
* @param {boolean} [fill]
|
|
760
796
|
* @memberof Debug */
|
|
761
|
-
function debugPoly(pos, points, color=
|
|
797
|
+
function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
|
|
762
798
|
{
|
|
763
|
-
|
|
764
|
-
|
|
799
|
+
if (isColor(color))
|
|
800
|
+
color = color.toString();
|
|
801
|
+
pos = pos.copy();
|
|
802
|
+
points = points.map(p=>p.copy());
|
|
803
|
+
const timer = new Timer(time);
|
|
804
|
+
debugPrimitives.push({pos, points, color, timer, angle, fill});
|
|
765
805
|
}
|
|
766
806
|
|
|
767
807
|
/** Draw a debug circle in world space
|
|
768
808
|
* @param {Vector2} pos
|
|
769
|
-
* @param {number}
|
|
770
|
-
* @param {string}
|
|
771
|
-
* @param {number}
|
|
809
|
+
* @param {number} [size] - diameter
|
|
810
|
+
* @param {Color|string} [color]
|
|
811
|
+
* @param {number} [time]
|
|
772
812
|
* @param {boolean} [fill]
|
|
773
813
|
* @memberof Debug */
|
|
774
|
-
function debugCircle(pos, size=0, color=
|
|
814
|
+
function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
|
|
775
815
|
{
|
|
776
|
-
|
|
777
|
-
|
|
816
|
+
if (isColor(color))
|
|
817
|
+
color = color.toString();
|
|
818
|
+
pos = pos.copy();
|
|
819
|
+
const timer = new Timer(time);
|
|
820
|
+
debugPrimitives.push({pos, size, color, timer, angle:0, fill});
|
|
778
821
|
}
|
|
779
822
|
|
|
780
823
|
/** Draw a debug point in world space
|
|
781
824
|
* @param {Vector2} pos
|
|
782
|
-
* @param {string}
|
|
783
|
-
* @param {number}
|
|
784
|
-
* @param {number}
|
|
825
|
+
* @param {Color|string} [color]
|
|
826
|
+
* @param {number} [time]
|
|
827
|
+
* @param {number} [angle]
|
|
785
828
|
* @memberof Debug */
|
|
786
829
|
function debugPoint(pos, color, time, angle)
|
|
787
|
-
{
|
|
788
|
-
ASSERT(typeof color === 'string', 'pass in css color strings');
|
|
789
|
-
debugRect(pos, undefined, color, time, angle);
|
|
790
|
-
}
|
|
830
|
+
{ debugRect(pos, undefined, color, time, angle); }
|
|
791
831
|
|
|
792
832
|
/** Draw a debug line in world space
|
|
793
833
|
* @param {Vector2} posA
|
|
794
834
|
* @param {Vector2} posB
|
|
795
|
-
* @param {string}
|
|
796
|
-
* @param {number}
|
|
797
|
-
* @param {number}
|
|
835
|
+
* @param {Color|string} [color]
|
|
836
|
+
* @param {number} [width]
|
|
837
|
+
* @param {number} [time]
|
|
798
838
|
* @memberof Debug */
|
|
799
839
|
function debugLine(posA, posB, color, width=.1, time)
|
|
800
840
|
{
|
|
@@ -808,7 +848,7 @@ function debugLine(posA, posB, color, width=.1, time)
|
|
|
808
848
|
* @param {Vector2} sizeA
|
|
809
849
|
* @param {Vector2} posB
|
|
810
850
|
* @param {Vector2} sizeB
|
|
811
|
-
* @param {string}
|
|
851
|
+
* @param {Color|string} [color]
|
|
812
852
|
* @memberof Debug */
|
|
813
853
|
function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
814
854
|
{
|
|
@@ -824,18 +864,21 @@ function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
|
824
864
|
}
|
|
825
865
|
|
|
826
866
|
/** Draw a debug axis aligned bounding box in world space
|
|
827
|
-
* @param {string}
|
|
867
|
+
* @param {string} text
|
|
828
868
|
* @param {Vector2} pos
|
|
829
|
-
* @param {number}
|
|
830
|
-
* @param {string}
|
|
831
|
-
* @param {number}
|
|
832
|
-
* @param {number}
|
|
833
|
-
* @param {string}
|
|
869
|
+
* @param {number} [size]
|
|
870
|
+
* @param {Color|string} [color]
|
|
871
|
+
* @param {number} [time]
|
|
872
|
+
* @param {number} [angle]
|
|
873
|
+
* @param {string} [font]
|
|
834
874
|
* @memberof Debug */
|
|
835
|
-
function debugText(text, pos, size=1, color=
|
|
875
|
+
function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monospace')
|
|
836
876
|
{
|
|
837
|
-
|
|
838
|
-
|
|
877
|
+
if (isColor(color))
|
|
878
|
+
color = color.toString();
|
|
879
|
+
pos = pos.copy();
|
|
880
|
+
const timer = new Timer(time);
|
|
881
|
+
debugPrimitives.push({text, pos, size, color, timer, angle, font});
|
|
839
882
|
}
|
|
840
883
|
|
|
841
884
|
/** Clear all debug primitives in the list
|
|
@@ -886,17 +929,30 @@ function debugSaveDataURL(dataURL, filename)
|
|
|
886
929
|
downloadLink.click();
|
|
887
930
|
}
|
|
888
931
|
|
|
889
|
-
/**
|
|
932
|
+
/** Breaks on all asserts/errors, hides the canvas, and shows message in plain text
|
|
933
|
+
* This is a good function to call at the start of your game to catch all errors
|
|
934
|
+
* In release builds this function has no effect
|
|
890
935
|
* @memberof Debug */
|
|
891
936
|
function debugShowErrors()
|
|
892
937
|
{
|
|
893
938
|
const showError = (message)=>
|
|
894
939
|
{
|
|
895
940
|
// replace entire page with error message
|
|
896
|
-
document.body.style
|
|
897
|
-
document.body.
|
|
898
|
-
document.body.innerHTML = `<pre style=color:#f00;font-size:50px;white-space:pre-wrap>` + message;
|
|
941
|
+
document.body.style = 'background-color:#111;margin:8px';
|
|
942
|
+
document.body.innerHTML = `<pre style=color:#f00;font-size:28px;white-space:pre-wrap>` + message;
|
|
899
943
|
}
|
|
944
|
+
|
|
945
|
+
const originalAssert = console.assert;
|
|
946
|
+
console.assert = (assertion, ...output)=>
|
|
947
|
+
{
|
|
948
|
+
originalAssert(assertion, ...output);
|
|
949
|
+
if (!assertion)
|
|
950
|
+
{
|
|
951
|
+
const message = output.join(' ');
|
|
952
|
+
const stack = new Error().stack;
|
|
953
|
+
throw 'Assertion failed!\n' + message + '\n' + stack;
|
|
954
|
+
}
|
|
955
|
+
};
|
|
900
956
|
onunhandledrejection = (event)=>
|
|
901
957
|
showError(event.reason.stack || event.reason);
|
|
902
958
|
onerror = (message, source, lineno, colno)=>
|
|
@@ -1064,7 +1120,7 @@ function debugRender()
|
|
|
1064
1120
|
p.fill && overlayContext.fill();
|
|
1065
1121
|
overlayContext.stroke();
|
|
1066
1122
|
}
|
|
1067
|
-
else if (p.size === 0 || p.size.x === 0 && p.size.y === 0)
|
|
1123
|
+
else if (p.size === 0 || (p.size.x === 0 && p.size.y === 0))
|
|
1068
1124
|
{
|
|
1069
1125
|
// point
|
|
1070
1126
|
overlayContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
@@ -1091,7 +1147,7 @@ function debugRender()
|
|
|
1091
1147
|
});
|
|
1092
1148
|
|
|
1093
1149
|
// remove expired primitives
|
|
1094
|
-
debugPrimitives = debugPrimitives.filter(r=>r.
|
|
1150
|
+
debugPrimitives = debugPrimitives.filter(r=>r.timer<0);
|
|
1095
1151
|
}
|
|
1096
1152
|
|
|
1097
1153
|
if (debugObject)
|
|
@@ -1226,7 +1282,7 @@ function debugVideoCaptureStart()
|
|
|
1226
1282
|
}
|
|
1227
1283
|
|
|
1228
1284
|
// start recording
|
|
1229
|
-
|
|
1285
|
+
LOG('Video capture started.');
|
|
1230
1286
|
debugVideoCapture.start();
|
|
1231
1287
|
debugVideoCaptureTimer = new Timer(0);
|
|
1232
1288
|
|
|
@@ -1253,7 +1309,7 @@ function debugVideoCaptureStop()
|
|
|
1253
1309
|
return; // not recording
|
|
1254
1310
|
|
|
1255
1311
|
// stop recording
|
|
1256
|
-
|
|
1312
|
+
LOG(`Video capture ended. ${debugVideoCaptureTimer.get().toFixed(2)} seconds recorded.`);
|
|
1257
1313
|
debugVideoCapture.stop();
|
|
1258
1314
|
debugVideoCapture = 0;
|
|
1259
1315
|
debugVideoCaptureIcon.style.display = 'none';
|
|
@@ -1487,7 +1543,12 @@ function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
|
1487
1543
|
* @param {number} t - time in seconds
|
|
1488
1544
|
* @return {string}
|
|
1489
1545
|
* @memberof Utilities */
|
|
1490
|
-
function formatTime(t)
|
|
1546
|
+
function formatTime(t)
|
|
1547
|
+
{
|
|
1548
|
+
const sign = t < 0 ? '-' : '';
|
|
1549
|
+
t = abs(t)|0;
|
|
1550
|
+
return sign + (t/60|0) + ':' + (t%60<10?'0':'') + t%60;
|
|
1551
|
+
}
|
|
1491
1552
|
|
|
1492
1553
|
/** Fetches a JSON file from a URL and returns the parsed JSON object. Must be used with await!
|
|
1493
1554
|
* @param {string} url - URL of JSON file
|
|
@@ -1496,6 +1557,8 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
|
|
|
1496
1557
|
async function fetchJSON(url)
|
|
1497
1558
|
{
|
|
1498
1559
|
const response = await fetch(url);
|
|
1560
|
+
if (!response.ok)
|
|
1561
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
1499
1562
|
return response.json();
|
|
1500
1563
|
}
|
|
1501
1564
|
|
|
@@ -1506,6 +1569,13 @@ async function fetchJSON(url)
|
|
|
1506
1569
|
* @memberof Utilities */
|
|
1507
1570
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
1508
1571
|
|
|
1572
|
+
/**
|
|
1573
|
+
* Check if object is a valid string or can be converted to one
|
|
1574
|
+
* @param {any} s
|
|
1575
|
+
* @return {boolean}
|
|
1576
|
+
* @memberof Utilities */
|
|
1577
|
+
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
1578
|
+
|
|
1509
1579
|
///////////////////////////////////////////////////////////////////////////////
|
|
1510
1580
|
|
|
1511
1581
|
/** Random global functions
|
|
@@ -1568,6 +1638,7 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
1568
1638
|
/**
|
|
1569
1639
|
* Seeded random number generator
|
|
1570
1640
|
* - Can be used to create a deterministic random number sequence
|
|
1641
|
+
* @memberof Engine
|
|
1571
1642
|
* @example
|
|
1572
1643
|
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
1573
1644
|
* let a = r.float(); // random value between 0 and 1
|
|
@@ -1664,6 +1735,7 @@ function ASSERT_VECTOR2_NORMAL(v)
|
|
|
1664
1735
|
/**
|
|
1665
1736
|
* 2D Vector object with vector math library
|
|
1666
1737
|
* - Functions do not change this so they can be chained together
|
|
1738
|
+
* @memberof Engine
|
|
1667
1739
|
* @example
|
|
1668
1740
|
* let a = new Vector2(2, 3); // vector with coordinates (2, 3)
|
|
1669
1741
|
* let b = new Vector2; // vector with coordinates (0, 0)
|
|
@@ -1804,9 +1876,10 @@ class Vector2
|
|
|
1804
1876
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
1805
1877
|
}
|
|
1806
1878
|
|
|
1807
|
-
/**
|
|
1879
|
+
/** Sets this this vector to point in the specified integer direction (0-3), corresponding to multiples of 90 degree rotation
|
|
1808
1880
|
* @param {number} [direction]
|
|
1809
|
-
* @param {number} [length]
|
|
1881
|
+
* @param {number} [length]
|
|
1882
|
+
* @return {Vector2} */
|
|
1810
1883
|
setDirection(direction, length=1)
|
|
1811
1884
|
{
|
|
1812
1885
|
ASSERT_NUMBER_VALID(direction);
|
|
@@ -1814,8 +1887,10 @@ class Vector2
|
|
|
1814
1887
|
direction = mod(direction, 4);
|
|
1815
1888
|
ASSERT(direction===0 || direction===1 || direction===2 || direction===3,
|
|
1816
1889
|
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
1817
|
-
|
|
1818
|
-
|
|
1890
|
+
|
|
1891
|
+
this.x = direction%2 ? direction-1 ? -length : length : 0;
|
|
1892
|
+
this.y = direction%2 ? 0 : direction ? -length : length;
|
|
1893
|
+
return this;
|
|
1819
1894
|
}
|
|
1820
1895
|
|
|
1821
1896
|
/** Returns the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
@@ -1823,11 +1898,7 @@ class Vector2
|
|
|
1823
1898
|
direction()
|
|
1824
1899
|
{ return abs(this.x) > abs(this.y) ? this.x < 0 ? 3 : 1 : this.y < 0 ? 2 : 0; }
|
|
1825
1900
|
|
|
1826
|
-
/** Returns a copy of this vector
|
|
1827
|
-
* @return {Vector2} */
|
|
1828
|
-
invert() { return new Vector2(this.y, -this.x); }
|
|
1829
|
-
|
|
1830
|
-
/** Returns a copy of this vector absolute values
|
|
1901
|
+
/** Returns a copy of this vector with absolute values
|
|
1831
1902
|
* @return {Vector2} */
|
|
1832
1903
|
abs() { return new Vector2(abs(this.x), abs(this.y)); }
|
|
1833
1904
|
|
|
@@ -1869,13 +1940,10 @@ class Vector2
|
|
|
1869
1940
|
toString(digits=3)
|
|
1870
1941
|
{
|
|
1871
1942
|
ASSERT_NUMBER_VALID(digits);
|
|
1872
|
-
if (
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
else
|
|
1877
|
-
return `(${this.x}, ${this.y})`;
|
|
1878
|
-
}
|
|
1943
|
+
if (this.isValid())
|
|
1944
|
+
return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`;
|
|
1945
|
+
else
|
|
1946
|
+
return `(${this.x}, ${this.y})`;
|
|
1879
1947
|
}
|
|
1880
1948
|
|
|
1881
1949
|
/** Checks if this is a valid vector
|
|
@@ -1918,6 +1986,7 @@ function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
|
|
|
1918
1986
|
|
|
1919
1987
|
/**
|
|
1920
1988
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
1989
|
+
* @memberof Engine
|
|
1921
1990
|
* @example
|
|
1922
1991
|
* let a = new Color; // white
|
|
1923
1992
|
* let b = new Color(1, 0, 0); // red
|
|
@@ -2095,7 +2164,8 @@ class Color
|
|
|
2095
2164
|
* @return {Color} */
|
|
2096
2165
|
setHex(hex)
|
|
2097
2166
|
{
|
|
2098
|
-
ASSERT(
|
|
2167
|
+
ASSERT(isString(hex), 'Color hex code must be a string');
|
|
2168
|
+
ASSERT(hex[0] === '#', 'Color hex code must start with #');
|
|
2099
2169
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
2100
2170
|
|
|
2101
2171
|
if (hex.length < 6)
|
|
@@ -2137,77 +2207,78 @@ class Color
|
|
|
2137
2207
|
}
|
|
2138
2208
|
|
|
2139
2209
|
///////////////////////////////////////////////////////////////////////////////
|
|
2140
|
-
//
|
|
2210
|
+
// Default Colors
|
|
2141
2211
|
|
|
2142
2212
|
/** Color - White #ffffff
|
|
2143
2213
|
* @type {Color}
|
|
2144
2214
|
* @memberof Utilities */
|
|
2145
|
-
const WHITE = rgb();
|
|
2215
|
+
const WHITE = protectEngineConstant(rgb());
|
|
2146
2216
|
|
|
2147
|
-
/** Color - Clear White #
|
|
2217
|
+
/** Color - Clear White #757474ff with 0 alpha
|
|
2148
2218
|
* @type {Color}
|
|
2149
2219
|
* @memberof Utilities */
|
|
2150
|
-
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
2220
|
+
const CLEAR_WHITE = protectEngineConstant(rgb(1,1,1,0));
|
|
2151
2221
|
|
|
2152
2222
|
/** Color - Black #000000
|
|
2153
2223
|
* @type {Color}
|
|
2154
2224
|
* @memberof Utilities */
|
|
2155
|
-
const BLACK = rgb(0,0,0);
|
|
2225
|
+
const BLACK = protectEngineConstant(rgb(0,0,0));
|
|
2156
2226
|
|
|
2157
2227
|
/** Color - Clear Black #000000 with 0 alpha
|
|
2158
2228
|
* @type {Color}
|
|
2159
2229
|
* @memberof Utilities */
|
|
2160
|
-
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
2230
|
+
const CLEAR_BLACK = protectEngineConstant(rgb(0,0,0,0));
|
|
2161
2231
|
|
|
2162
2232
|
/** Color - Gray #808080
|
|
2163
2233
|
* @type {Color}
|
|
2164
2234
|
* @memberof Utilities */
|
|
2165
|
-
const GRAY = rgb(.5,.5,.5);
|
|
2235
|
+
const GRAY = protectEngineConstant(rgb(.5,.5,.5));
|
|
2166
2236
|
|
|
2167
2237
|
/** Color - Red #ff0000
|
|
2168
2238
|
* @type {Color}
|
|
2169
2239
|
* @memberof Utilities */
|
|
2170
|
-
const RED = rgb(1,0,0);
|
|
2240
|
+
const RED = protectEngineConstant(rgb(1,0,0));
|
|
2171
2241
|
|
|
2172
2242
|
/** Color - Orange #ff8000
|
|
2173
2243
|
* @type {Color}
|
|
2174
2244
|
* @memberof Utilities */
|
|
2175
|
-
const ORANGE = rgb(1,.5,0);
|
|
2245
|
+
const ORANGE = protectEngineConstant(rgb(1,.5,0));
|
|
2176
2246
|
|
|
2177
2247
|
/** Color - Yellow #ffff00
|
|
2178
2248
|
* @type {Color}
|
|
2179
2249
|
* @memberof Utilities */
|
|
2180
|
-
const YELLOW = rgb(1,1,0);
|
|
2250
|
+
const YELLOW = protectEngineConstant(rgb(1,1,0));
|
|
2181
2251
|
|
|
2182
2252
|
/** Color - Green #00ff00
|
|
2183
2253
|
* @type {Color}
|
|
2184
2254
|
* @memberof Utilities */
|
|
2185
|
-
const GREEN = rgb(0,1,0);
|
|
2255
|
+
const GREEN = protectEngineConstant(rgb(0,1,0));
|
|
2186
2256
|
|
|
2187
2257
|
/** Color - Cyan #00ffff
|
|
2188
2258
|
* @type {Color}
|
|
2189
2259
|
* @memberof Utilities */
|
|
2190
|
-
const CYAN = rgb(0,1,1);
|
|
2260
|
+
const CYAN = protectEngineConstant(rgb(0,1,1));
|
|
2191
2261
|
|
|
2192
2262
|
/** Color - Blue #0000ff
|
|
2193
2263
|
* @type {Color}
|
|
2194
2264
|
* @memberof Utilities */
|
|
2195
|
-
const BLUE = rgb(0,0,1);
|
|
2265
|
+
const BLUE = protectEngineConstant(rgb(0,0,1));
|
|
2196
2266
|
|
|
2197
2267
|
/** Color - Purple #8000ff
|
|
2198
2268
|
* @type {Color}
|
|
2199
2269
|
* @memberof Utilities */
|
|
2200
|
-
const PURPLE = rgb(.5,0,1);
|
|
2270
|
+
const PURPLE = protectEngineConstant(rgb(.5,0,1));
|
|
2201
2271
|
|
|
2202
2272
|
/** Color - Magenta #ff00ff
|
|
2203
2273
|
* @type {Color}
|
|
2204
2274
|
* @memberof Utilities */
|
|
2205
|
-
const MAGENTA = rgb(1,0,1);
|
|
2275
|
+
const MAGENTA = protectEngineConstant(rgb(1,0,1));
|
|
2206
2276
|
|
|
2207
2277
|
///////////////////////////////////////////////////////////////////////////////
|
|
2208
2278
|
|
|
2209
2279
|
/**
|
|
2210
2280
|
* Timer object tracks how long has passed since it was set
|
|
2281
|
+
* @memberof Engine
|
|
2211
2282
|
* @example
|
|
2212
2283
|
* let a = new Timer; // creates a timer that is not set
|
|
2213
2284
|
* a.set(3); // sets the timer to 3 seconds
|
|
@@ -2264,11 +2335,41 @@ class Timer
|
|
|
2264
2335
|
|
|
2265
2336
|
/** Returns this timer expressed as a string
|
|
2266
2337
|
* @return {string} */
|
|
2267
|
-
toString() {
|
|
2338
|
+
toString() { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
2268
2339
|
|
|
2269
2340
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
2270
2341
|
* @return {number} */
|
|
2271
2342
|
valueOf() { return this.get(); }
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2346
|
+
// Helper functions used by the engine
|
|
2347
|
+
|
|
2348
|
+
// make color constants immutable with debug assertions
|
|
2349
|
+
function protectEngineConstant(obj)
|
|
2350
|
+
{
|
|
2351
|
+
if (debug)
|
|
2352
|
+
{
|
|
2353
|
+
// get properties and store original values
|
|
2354
|
+
const props = Object.keys(obj), values = {};
|
|
2355
|
+
props.forEach(prop => values[prop] = obj[prop]);
|
|
2356
|
+
|
|
2357
|
+
// replace with getters/setters that assert
|
|
2358
|
+
props.forEach(prop =>
|
|
2359
|
+
{
|
|
2360
|
+
Object.defineProperty(obj, prop, {
|
|
2361
|
+
get: () => values[prop],
|
|
2362
|
+
set: (value) =>
|
|
2363
|
+
{
|
|
2364
|
+
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
2365
|
+
},
|
|
2366
|
+
enumerable: true
|
|
2367
|
+
});
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// freeze the object to prevent adding new properties
|
|
2372
|
+
return Object.freeze(obj);
|
|
2272
2373
|
}
|
|
2273
2374
|
/**
|
|
2274
2375
|
* LittleJS Engine Settings
|
|
@@ -2576,7 +2677,7 @@ let medalsPreventUnlock = false;
|
|
|
2576
2677
|
/** Set position of camera in world space
|
|
2577
2678
|
* @param {Vector2} pos
|
|
2578
2679
|
* @memberof Settings */
|
|
2579
|
-
function setCameraPos(pos) { cameraPos = pos; }
|
|
2680
|
+
function setCameraPos(pos) { cameraPos = pos.copy(); }
|
|
2580
2681
|
|
|
2581
2682
|
/** Set angle of camera in world space
|
|
2582
2683
|
* @param {number} angle
|
|
@@ -2599,17 +2700,17 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
|
2599
2700
|
/** Set color to clear the canvas to before render
|
|
2600
2701
|
* @param {Color} color
|
|
2601
2702
|
* @memberof Settings */
|
|
2602
|
-
function setCanvasClearColor(color) { canvasClearColor = color; }
|
|
2703
|
+
function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
2603
2704
|
|
|
2604
2705
|
/** Set max size of the canvas
|
|
2605
2706
|
* @param {Vector2} size
|
|
2606
2707
|
* @memberof Settings */
|
|
2607
|
-
function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
2708
|
+
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2608
2709
|
|
|
2609
2710
|
/** Set fixed size of the canvas
|
|
2610
2711
|
* @param {Vector2} size
|
|
2611
2712
|
* @memberof Settings */
|
|
2612
|
-
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
2713
|
+
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
2613
2714
|
|
|
2614
2715
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2615
2716
|
* - If enabled sets css image-rendering:pixelated
|
|
@@ -2674,7 +2775,7 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
2674
2775
|
/** Set default size of tiles in pixels
|
|
2675
2776
|
* @param {Vector2} size
|
|
2676
2777
|
* @memberof Settings */
|
|
2677
|
-
function setTileSizeDefault(size) { tileSizeDefault = size; }
|
|
2778
|
+
function setTileSizeDefault(size) { tileSizeDefault = size.copy(); }
|
|
2678
2779
|
|
|
2679
2780
|
/** Set to prevent tile bleeding from neighbors in pixels
|
|
2680
2781
|
* @param {number} scale
|
|
@@ -2719,7 +2820,7 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
|
2719
2820
|
/** Set how much gravity to apply to objects
|
|
2720
2821
|
* @param {Vector2} newGravity
|
|
2721
2822
|
* @memberof Settings */
|
|
2722
|
-
function setGravity(newGravity) { gravity = newGravity; }
|
|
2823
|
+
function setGravity(newGravity) { gravity = newGravity.copy(); }
|
|
2723
2824
|
|
|
2724
2825
|
/** Set to scales emit rate of particles
|
|
2725
2826
|
* @param {number} scale
|
|
@@ -2809,7 +2910,7 @@ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
|
|
|
2809
2910
|
/** Set size of medal display
|
|
2810
2911
|
* @param {Vector2} size
|
|
2811
2912
|
* @memberof Settings */
|
|
2812
|
-
function setMedalDisplaySize(size) { medalDisplaySize = size; }
|
|
2913
|
+
function setMedalDisplaySize(size) { medalDisplaySize = size.copy(); }
|
|
2813
2914
|
|
|
2814
2915
|
/** Set to stop medals from being unlockable
|
|
2815
2916
|
* @param {boolean} preventUnlock
|
|
@@ -2849,6 +2950,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2849
2950
|
* - Collision for objects can be set to be solid to block other objects
|
|
2850
2951
|
* - Objects may get pushed into overlapping other solid objects, if so they will push away
|
|
2851
2952
|
* - Solid objects are more performance intensive and should be used sparingly
|
|
2953
|
+
* @memberof Engine
|
|
2852
2954
|
* @example
|
|
2853
2955
|
* // create an engine object, normally you would first extend the class with your own
|
|
2854
2956
|
* const pos = vec2(2,3);
|
|
@@ -2857,18 +2959,18 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2857
2959
|
class EngineObject
|
|
2858
2960
|
{
|
|
2859
2961
|
/** Create an engine object and adds it to the list of objects
|
|
2860
|
-
* @param {Vector2} [pos=(0,0)]
|
|
2861
|
-
* @param {Vector2} [size=(1,1)]
|
|
2862
|
-
* @param {TileInfo} [tileInfo]
|
|
2863
|
-
* @param {number} [angle]
|
|
2864
|
-
* @param {Color} [color=
|
|
2865
|
-
* @param {number} [renderOrder]
|
|
2962
|
+
* @param {Vector2} [pos=(0,0)] - World space position of the object
|
|
2963
|
+
* @param {Vector2} [size=(1,1)] - World space size of the object
|
|
2964
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
2965
|
+
* @param {number} [angle] - Angle the object is rotated by
|
|
2966
|
+
* @param {Color} [color=WHITE] - Color to apply to tile when rendered
|
|
2967
|
+
* @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
2866
2968
|
*/
|
|
2867
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=
|
|
2969
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=WHITE, renderOrder=0)
|
|
2868
2970
|
{
|
|
2869
2971
|
// check passed in params
|
|
2870
|
-
ASSERT(isVector2(pos), 'object pos
|
|
2871
|
-
ASSERT(isVector2(size), 'object size
|
|
2972
|
+
ASSERT(isVector2(pos), 'object pos must be a vec2');
|
|
2973
|
+
ASSERT(isVector2(size), 'object size must be a vec2');
|
|
2872
2974
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
2873
2975
|
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
2874
2976
|
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
@@ -3153,7 +3255,7 @@ class EngineObject
|
|
|
3153
3255
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
3154
3256
|
}
|
|
3155
3257
|
|
|
3156
|
-
/** Destroy this object, destroy its children, detach
|
|
3258
|
+
/** Destroy this object, destroy its children, detach its parent, and mark it for removal */
|
|
3157
3259
|
destroy()
|
|
3158
3260
|
{
|
|
3159
3261
|
if (this.destroyed)
|
|
@@ -3227,6 +3329,8 @@ class EngineObject
|
|
|
3227
3329
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3228
3330
|
{
|
|
3229
3331
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
3332
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
3333
|
+
ASSERT(child !== this, 'cannot add self as child');
|
|
3230
3334
|
this.children.push(child);
|
|
3231
3335
|
child.parent = this;
|
|
3232
3336
|
child.localPos = localPos.copy();
|
|
@@ -3238,6 +3342,7 @@ class EngineObject
|
|
|
3238
3342
|
removeChild(child)
|
|
3239
3343
|
{
|
|
3240
3344
|
ASSERT(child.parent === this && this.children.includes(child));
|
|
3345
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
3241
3346
|
this.children.splice(this.children.indexOf(child), 1);
|
|
3242
3347
|
child.parent = 0;
|
|
3243
3348
|
}
|
|
@@ -3376,7 +3481,7 @@ let drawCount;
|
|
|
3376
3481
|
* Create a tile info object using a grid based system
|
|
3377
3482
|
* - This can take vecs or floats for easier use and conversion
|
|
3378
3483
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3379
|
-
* @param {Vector2|number} [pos=0] -
|
|
3484
|
+
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3380
3485
|
* @param {Vector2|number} [size=tileSizeDefault] - Size of tile in pixels
|
|
3381
3486
|
* @param {number} [textureIndex] - Texture index to use
|
|
3382
3487
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
@@ -3421,6 +3526,7 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3421
3526
|
|
|
3422
3527
|
/**
|
|
3423
3528
|
* Tile Info - Stores info about how to draw a tile
|
|
3529
|
+
* @memberof Draw
|
|
3424
3530
|
*/
|
|
3425
3531
|
class TileInfo
|
|
3426
3532
|
{
|
|
@@ -3481,7 +3587,10 @@ class TileInfo
|
|
|
3481
3587
|
}
|
|
3482
3588
|
}
|
|
3483
3589
|
|
|
3484
|
-
/**
|
|
3590
|
+
/**
|
|
3591
|
+
* Tile Info - Stores info about each texture
|
|
3592
|
+
* @memberof Draw
|
|
3593
|
+
*/
|
|
3485
3594
|
class TextureInfo
|
|
3486
3595
|
{
|
|
3487
3596
|
/**
|
|
@@ -3527,10 +3636,11 @@ class TextureInfo
|
|
|
3527
3636
|
function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
3528
3637
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
3529
3638
|
{
|
|
3530
|
-
ASSERT(isVector2(pos), '
|
|
3531
|
-
ASSERT(isVector2(size), '
|
|
3532
|
-
ASSERT(isColor(color)
|
|
3533
|
-
ASSERT(isNumber(angle), '
|
|
3639
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3640
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3641
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3642
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3643
|
+
ASSERT(!additiveColor || isColor(additiveColor), 'additiveColor must be a color');
|
|
3534
3644
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3535
3645
|
|
|
3536
3646
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
@@ -3625,10 +3735,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3625
3735
|
* @memberof Draw */
|
|
3626
3736
|
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3627
3737
|
{
|
|
3628
|
-
ASSERT(isVector2(pos), '
|
|
3629
|
-
ASSERT(isVector2(size), '
|
|
3630
|
-
ASSERT(isColor(colorTop) && isColor(colorBottom), '
|
|
3631
|
-
ASSERT(isNumber(angle), '
|
|
3738
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3739
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3740
|
+
ASSERT(isColor(colorTop) && isColor(colorBottom), 'color is invalid');
|
|
3741
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3632
3742
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3633
3743
|
if (useWebGL)
|
|
3634
3744
|
{
|
|
@@ -3686,11 +3796,11 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3686
3796
|
* @memberof Draw */
|
|
3687
3797
|
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
3688
3798
|
{
|
|
3689
|
-
ASSERT(Array.isArray(points), '
|
|
3690
|
-
ASSERT(isNumber(width), '
|
|
3691
|
-
ASSERT(isColor(color), '
|
|
3692
|
-
ASSERT(isVector2(pos), '
|
|
3693
|
-
ASSERT(isNumber(angle), '
|
|
3799
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3800
|
+
ASSERT(isNumber(width), 'width must be a number');
|
|
3801
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3802
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3803
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3694
3804
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3695
3805
|
if (useWebGL)
|
|
3696
3806
|
{
|
|
@@ -3761,8 +3871,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
3761
3871
|
* @memberof Draw */
|
|
3762
3872
|
function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3763
3873
|
{
|
|
3764
|
-
ASSERT(isVector2(size), '
|
|
3765
|
-
ASSERT(isNumber(sides), '
|
|
3874
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3875
|
+
ASSERT(isNumber(sides), 'sides must be a number');
|
|
3766
3876
|
|
|
3767
3877
|
// build regular polygon points
|
|
3768
3878
|
const points = [];
|
|
@@ -3788,12 +3898,13 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3788
3898
|
* @memberof Draw */
|
|
3789
3899
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
3790
3900
|
{
|
|
3791
|
-
ASSERT(isVector2(pos), '
|
|
3792
|
-
ASSERT(Array.isArray(points), '
|
|
3793
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3794
|
-
ASSERT(isNumber(lineWidth), '
|
|
3795
|
-
ASSERT(isNumber(angle), '
|
|
3901
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3902
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3903
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3904
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3905
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3796
3906
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3907
|
+
|
|
3797
3908
|
if (useWebGL)
|
|
3798
3909
|
{
|
|
3799
3910
|
let scale = 1;
|
|
@@ -3840,13 +3951,14 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3840
3951
|
* @memberof Draw */
|
|
3841
3952
|
function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3842
3953
|
{
|
|
3843
|
-
ASSERT(isVector2(pos), '
|
|
3844
|
-
ASSERT(isVector2(size), '
|
|
3845
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3846
|
-
ASSERT(isNumber(angle), '
|
|
3847
|
-
ASSERT(isNumber(lineWidth), '
|
|
3848
|
-
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, '
|
|
3954
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3955
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3956
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3957
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3958
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3959
|
+
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, 'invalid lineWidth');
|
|
3849
3960
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3961
|
+
|
|
3850
3962
|
if (useWebGL)
|
|
3851
3963
|
{
|
|
3852
3964
|
// draw as a regular polygon
|
|
@@ -3883,21 +3995,32 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3883
3995
|
* @memberof Draw */
|
|
3884
3996
|
function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3885
3997
|
{
|
|
3886
|
-
ASSERT(isNumber(size), '
|
|
3998
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3887
3999
|
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
3888
4000
|
}
|
|
3889
4001
|
|
|
4002
|
+
/**
|
|
4003
|
+
* @callback Canvas2DDrawFunction - A function that draws to a 2D canvas context
|
|
4004
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
4005
|
+
* @memberof Draw
|
|
4006
|
+
*/
|
|
4007
|
+
|
|
3890
4008
|
/** Draw directly to a 2d canvas context in world space
|
|
3891
4009
|
* @param {Vector2} pos
|
|
3892
4010
|
* @param {Vector2} size
|
|
3893
4011
|
* @param {number} angle
|
|
3894
4012
|
* @param {boolean} [mirror]
|
|
3895
|
-
* @param {
|
|
4013
|
+
* @param {Canvas2DDrawFunction} [drawFunction]
|
|
3896
4014
|
* @param {boolean} [screenSpace=false]
|
|
3897
4015
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3898
4016
|
* @memberof Draw */
|
|
3899
4017
|
function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpace=false, context=drawContext)
|
|
3900
4018
|
{
|
|
4019
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4020
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
4021
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
4022
|
+
ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
|
|
4023
|
+
|
|
3901
4024
|
if (!screenSpace)
|
|
3902
4025
|
{
|
|
3903
4026
|
// transform from world space to screen space
|
|
@@ -3965,6 +4088,16 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3965
4088
|
* @memberof Draw */
|
|
3966
4089
|
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
3967
4090
|
{
|
|
4091
|
+
ASSERT(isString(text), 'text must be a string');
|
|
4092
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4093
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
4094
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
4095
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
4096
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
4097
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
4098
|
+
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
4099
|
+
ASSERT(isString(font), 'font must be a string');
|
|
4100
|
+
|
|
3968
4101
|
context.fillStyle = color.toString();
|
|
3969
4102
|
context.strokeStyle = lineColor.toString();
|
|
3970
4103
|
context.lineWidth = lineWidth;
|
|
@@ -4166,6 +4299,7 @@ let engineFontImage;
|
|
|
4166
4299
|
* - 96 characters (from space to tilde) are stored in an image
|
|
4167
4300
|
* - Uses a default 8x8 font if none is supplied
|
|
4168
4301
|
* - You can also use fonts from the main tile sheet
|
|
4302
|
+
* @memberof Draw
|
|
4169
4303
|
* @example
|
|
4170
4304
|
* // use built in font
|
|
4171
4305
|
* const font = new FontImage;
|
|
@@ -4222,7 +4356,7 @@ class FontImage
|
|
|
4222
4356
|
* @param {boolean} [center]
|
|
4223
4357
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4224
4358
|
*/
|
|
4225
|
-
drawTextScreen(text, pos, scale=4, center, context=overlayContext)
|
|
4359
|
+
drawTextScreen(text, pos, scale=4, center=true, context=overlayContext)
|
|
4226
4360
|
{
|
|
4227
4361
|
context.save();
|
|
4228
4362
|
const size = this.tileSize;
|
|
@@ -4916,6 +5050,7 @@ function audioInit()
|
|
|
4916
5050
|
* Sound Object - Stores a sound for later use and can be played positionally
|
|
4917
5051
|
*
|
|
4918
5052
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
5053
|
+
* @memberof Audio
|
|
4919
5054
|
* @example
|
|
4920
5055
|
* // create a sound
|
|
4921
5056
|
* const sound_example = new Sound([.5,.5]);
|
|
@@ -5010,12 +5145,12 @@ class Sound
|
|
|
5010
5145
|
|
|
5011
5146
|
/** Play the sound as a musical note with a semitone offset
|
|
5012
5147
|
* This can be used to play music with chromatic scales
|
|
5013
|
-
* @param {number} semitoneOffset - How many semitones to offset pitch
|
|
5148
|
+
* @param {number} [semitoneOffset=0] - How many semitones to offset pitch
|
|
5014
5149
|
* @param {Vector2} [pos] - World space position to play the sound if any
|
|
5015
5150
|
* @param {number} [volume=1] - How much to scale volume by
|
|
5016
5151
|
* @return {SoundInstance} - The audio source node
|
|
5017
5152
|
*/
|
|
5018
|
-
playNote(semitoneOffset, pos, volume)
|
|
5153
|
+
playNote(semitoneOffset=0, pos, volume)
|
|
5019
5154
|
{
|
|
5020
5155
|
const pitch = getNoteFrequency(semitoneOffset, 1);
|
|
5021
5156
|
return this.play(pos, volume, pitch, 0);
|
|
@@ -5038,6 +5173,8 @@ class Sound
|
|
|
5038
5173
|
/**
|
|
5039
5174
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
5040
5175
|
* - this can be used to play wave, mp3, and ogg files
|
|
5176
|
+
* @extends Sound
|
|
5177
|
+
* @memberof Audio
|
|
5041
5178
|
* @example
|
|
5042
5179
|
* // create a sound
|
|
5043
5180
|
* const sound_example = new SoundWave('sound.mp3');
|
|
@@ -5047,22 +5184,29 @@ class Sound
|
|
|
5047
5184
|
*/
|
|
5048
5185
|
class SoundWave extends Sound
|
|
5049
5186
|
{
|
|
5187
|
+
/**
|
|
5188
|
+
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
5189
|
+
* @param {SoundWave} sound
|
|
5190
|
+
* @memberof Audio
|
|
5191
|
+
*/
|
|
5192
|
+
|
|
5050
5193
|
/** Create a sound object and cache the wave file for later use
|
|
5051
5194
|
* @param {string} filename - Filename of audio file to load
|
|
5052
5195
|
* @param {number} [randomness] - How much to randomize frequency each time sound plays
|
|
5053
5196
|
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
5054
5197
|
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
5055
|
-
* @param {
|
|
5198
|
+
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
5056
5199
|
*/
|
|
5057
5200
|
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
5058
5201
|
{
|
|
5059
5202
|
super(undefined, range, taper);
|
|
5060
5203
|
if (!soundEnable || headlessMode) return;
|
|
5204
|
+
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
5061
5205
|
|
|
5062
|
-
/** @property {
|
|
5206
|
+
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
5063
5207
|
this.onloadCallback = onloadCallback;
|
|
5064
5208
|
this.randomness = randomness;
|
|
5065
|
-
this.loadSound(filename);
|
|
5209
|
+
filename && this.loadSound(filename);
|
|
5066
5210
|
}
|
|
5067
5211
|
|
|
5068
5212
|
/** Loads a sound from a URL and decodes it into sample data. Must be used with await!
|
|
@@ -5071,6 +5215,8 @@ class SoundWave extends Sound
|
|
|
5071
5215
|
async loadSound(filename)
|
|
5072
5216
|
{
|
|
5073
5217
|
const response = await fetch(filename);
|
|
5218
|
+
if (!response.ok)
|
|
5219
|
+
throw new Error(`Failed to load sound from ${filename}: ${response.status} ${response.statusText}`);
|
|
5074
5220
|
const arrayBuffer = await response.arrayBuffer();
|
|
5075
5221
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
5076
5222
|
|
|
@@ -5106,7 +5252,7 @@ class SoundWave extends Sound
|
|
|
5106
5252
|
this.sampleChannels = sampleChannels;
|
|
5107
5253
|
this.loadedPercent = 1;
|
|
5108
5254
|
if (this.onloadCallback)
|
|
5109
|
-
this.onloadCallback();
|
|
5255
|
+
this.onloadCallback(this);
|
|
5110
5256
|
}
|
|
5111
5257
|
}
|
|
5112
5258
|
|
|
@@ -5115,6 +5261,7 @@ class SoundWave extends Sound
|
|
|
5115
5261
|
/**
|
|
5116
5262
|
* Sound Instance - Wraps an AudioBufferSourceNode for individual sound control
|
|
5117
5263
|
* Represents a single playing instance of a sound with pause/resume capabilities
|
|
5264
|
+
* @memberof Audio
|
|
5118
5265
|
* @example
|
|
5119
5266
|
* // Play a sound and get an instance for control
|
|
5120
5267
|
* const jumpSound = new Sound([.5,.5,220]);
|
|
@@ -5314,6 +5461,12 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
5314
5461
|
|
|
5315
5462
|
///////////////////////////////////////////////////////////////////////////////
|
|
5316
5463
|
|
|
5464
|
+
/**
|
|
5465
|
+
* @callback AudioEndedCallback - Function called when a sound ends
|
|
5466
|
+
* @param {AudioBufferSourceNode} source
|
|
5467
|
+
* @memberof Audio
|
|
5468
|
+
*/
|
|
5469
|
+
|
|
5317
5470
|
/** Play cached audio samples with given settings
|
|
5318
5471
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
5319
5472
|
* @param {number} [volume] - How much to scale volume by
|
|
@@ -5323,7 +5476,7 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
5323
5476
|
* @param {number} [sampleRate=44100] - Sample rate for the sound
|
|
5324
5477
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
5325
5478
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
5326
|
-
* @param {
|
|
5479
|
+
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
5327
5480
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
5328
5481
|
* @memberof Audio */
|
|
5329
5482
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
@@ -5355,16 +5508,14 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
5355
5508
|
if (onended)
|
|
5356
5509
|
source.addEventListener('ended', ()=> onended(source));
|
|
5357
5510
|
|
|
5511
|
+
const startOffset = offset * rate;
|
|
5358
5512
|
if (!audioIsRunning())
|
|
5359
5513
|
{
|
|
5360
|
-
// fix stalled audio
|
|
5361
|
-
audioContext.resume();
|
|
5362
|
-
return;
|
|
5514
|
+
// fix stalled audio and start
|
|
5515
|
+
audioContext.resume().then(()=>source.start(0, startOffset));
|
|
5363
5516
|
}
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
const startOffset = offset * rate;
|
|
5367
|
-
source.start(0, startOffset);
|
|
5517
|
+
else
|
|
5518
|
+
source.start(0, startOffset);
|
|
5368
5519
|
return source;
|
|
5369
5520
|
}
|
|
5370
5521
|
|
|
@@ -5529,7 +5680,7 @@ function zzfxG
|
|
|
5529
5680
|
* - Unlimited numbers of layers, allocates canvases as needed
|
|
5530
5681
|
* - Tile layers can be drawn to using their context with canvas2d
|
|
5531
5682
|
* - Tile layers can also have collision with EngineObjects
|
|
5532
|
-
* @namespace
|
|
5683
|
+
* @namespace TileLayers
|
|
5533
5684
|
*/
|
|
5534
5685
|
|
|
5535
5686
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -5537,13 +5688,13 @@ function zzfxG
|
|
|
5537
5688
|
|
|
5538
5689
|
/** Keep track of all tile layers with collision
|
|
5539
5690
|
* @type {Array<TileCollisionLayer>}
|
|
5540
|
-
* @memberof
|
|
5691
|
+
* @memberof TileLayers */
|
|
5541
5692
|
const tileCollisionLayers = [];
|
|
5542
5693
|
|
|
5543
5694
|
/** Get tile collision data for a given cell in the grid
|
|
5544
5695
|
* @param {Vector2} pos
|
|
5545
5696
|
* @return {number}
|
|
5546
|
-
* @memberof
|
|
5697
|
+
* @memberof TileLayers */
|
|
5547
5698
|
function tileCollisionGetData(pos)
|
|
5548
5699
|
{
|
|
5549
5700
|
// check all tile collision layers
|
|
@@ -5559,7 +5710,7 @@ function tileCollisionGetData(pos)
|
|
|
5559
5710
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5560
5711
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
5561
5712
|
* @return {TileCollisionLayer}
|
|
5562
|
-
* @memberof
|
|
5713
|
+
* @memberof TileLayers */
|
|
5563
5714
|
function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
5564
5715
|
{
|
|
5565
5716
|
for (const layer of tileCollisionLayers)
|
|
@@ -5577,7 +5728,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
5577
5728
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5578
5729
|
* @param {boolean} [solidOnly=true] - Only check solid layers if true
|
|
5579
5730
|
* @return {Vector2}
|
|
5580
|
-
* @memberof
|
|
5731
|
+
* @memberof TileLayers */
|
|
5581
5732
|
function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
5582
5733
|
{
|
|
5583
5734
|
for (const layer of tileCollisionLayers)
|
|
@@ -5600,8 +5751,8 @@ function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
|
5600
5751
|
* @param {number} [collisionLayer] - Layer to use for collision if any
|
|
5601
5752
|
* @param {boolean} [draw] - Should the layer be drawn automatically
|
|
5602
5753
|
* @return {Array<TileCollisionLayer>}
|
|
5603
|
-
* @memberof
|
|
5604
|
-
function
|
|
5754
|
+
* @memberof TileLayers */
|
|
5755
|
+
function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
|
|
5605
5756
|
{
|
|
5606
5757
|
if (!tileMapData)
|
|
5607
5758
|
{
|
|
@@ -5631,6 +5782,12 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5631
5782
|
const tileLayer = new TileCollisionLayer(vec2(), levelSize, tileInfo, layerRenderOrder);
|
|
5632
5783
|
tileLayers[layerIndex] = tileLayer;
|
|
5633
5784
|
|
|
5785
|
+
// apply layer color
|
|
5786
|
+
const layerColor = dataLayer.tintcolor ?
|
|
5787
|
+
new Color().setHex(dataLayer.tintcolor) :
|
|
5788
|
+
dataLayer.color || WHITE;
|
|
5789
|
+
ASSERT(isColor(layerColor), 'layer color is not a color');
|
|
5790
|
+
|
|
5634
5791
|
for (let x=levelSize.x; x--;)
|
|
5635
5792
|
for (let y=levelSize.y; y--;)
|
|
5636
5793
|
{
|
|
@@ -5638,7 +5795,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5638
5795
|
const data = dataLayer.data[x + y*levelSize.x];
|
|
5639
5796
|
if (data)
|
|
5640
5797
|
{
|
|
5641
|
-
const layerData = new TileLayerData(data-1);
|
|
5798
|
+
const layerData = new TileLayerData(data-1, 0, false, layerColor);
|
|
5642
5799
|
tileLayer.setData(pos, layerData);
|
|
5643
5800
|
|
|
5644
5801
|
// set collision for top layer
|
|
@@ -5655,6 +5812,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5655
5812
|
///////////////////////////////////////////////////////////////////////////////
|
|
5656
5813
|
/**
|
|
5657
5814
|
* Tile layer data object stores info about how to draw a tile
|
|
5815
|
+
* @memberof TileLayers
|
|
5658
5816
|
* @example
|
|
5659
5817
|
* // create tile layer data with tile index 0 and random orientation and color
|
|
5660
5818
|
* const tileIndex = 0;
|
|
@@ -5692,6 +5850,7 @@ class TileLayerData
|
|
|
5692
5850
|
* - Contains an offscreen canvas that can be rendered to
|
|
5693
5851
|
* - WebGL rendering is optional, call useWebGL to enable
|
|
5694
5852
|
* @extends EngineObject
|
|
5853
|
+
* @memberof TileLayers
|
|
5695
5854
|
* @example
|
|
5696
5855
|
* const canvasLayer = new CanvasLayer(vec2(), vec2(200,100));
|
|
5697
5856
|
*/
|
|
@@ -5706,6 +5865,7 @@ class CanvasLayer extends EngineObject
|
|
|
5706
5865
|
*/
|
|
5707
5866
|
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
5708
5867
|
{
|
|
5868
|
+
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
5709
5869
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
5710
5870
|
|
|
5711
5871
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
@@ -5753,12 +5913,18 @@ class CanvasLayer extends EngineObject
|
|
|
5753
5913
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5754
5914
|
}
|
|
5755
5915
|
|
|
5916
|
+
/**
|
|
5917
|
+
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
5918
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
5919
|
+
* @memberof TileLayers
|
|
5920
|
+
*/
|
|
5921
|
+
|
|
5756
5922
|
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
5757
5923
|
* @param {Vector2} pos
|
|
5758
5924
|
* @param {Vector2} size
|
|
5759
5925
|
* @param {number} angle
|
|
5760
5926
|
* @param {boolean} mirror
|
|
5761
|
-
* @param {
|
|
5927
|
+
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5762
5928
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5763
5929
|
{
|
|
5764
5930
|
const context = this.context;
|
|
@@ -5795,7 +5961,7 @@ class CanvasLayer extends EngineObject
|
|
|
5795
5961
|
else
|
|
5796
5962
|
{
|
|
5797
5963
|
// untextured
|
|
5798
|
-
context.fillStyle = color;
|
|
5964
|
+
context.fillStyle = color.toString();
|
|
5799
5965
|
context.fillRect(-.5, -.5, 1, 1);
|
|
5800
5966
|
}
|
|
5801
5967
|
});
|
|
@@ -5832,7 +5998,9 @@ class CanvasLayer extends EngineObject
|
|
|
5832
5998
|
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
5833
5999
|
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
5834
6000
|
* - For with 16x16 tiles this limits layers to 256x256 on mobile devices
|
|
6001
|
+
* - Tile layers are centered on their corner, so normal levels are at (0,0)
|
|
5835
6002
|
* @extends CanvasLayer
|
|
6003
|
+
* @memberof TileLayers
|
|
5836
6004
|
* @example
|
|
5837
6005
|
* const tileLayer = new TileLayer(vec2(), vec2(200,100));
|
|
5838
6006
|
*/
|
|
@@ -5842,15 +6010,14 @@ class TileLayer extends CanvasLayer
|
|
|
5842
6010
|
* @param {Vector2} position - World space position
|
|
5843
6011
|
* @param {Vector2} size - World space size
|
|
5844
6012
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
5845
|
-
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
5846
6013
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5847
6014
|
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5848
6015
|
*/
|
|
5849
|
-
constructor(position, size, tileInfo=tile(),
|
|
6016
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
5850
6017
|
{
|
|
5851
6018
|
super(position, size, 0, renderOrder, size);
|
|
6019
|
+
|
|
5852
6020
|
this.tileInfo = tileInfo;
|
|
5853
|
-
|
|
5854
6021
|
const canvasSize = size.multiply(tileInfo.size);
|
|
5855
6022
|
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
5856
6023
|
this.canvas = new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
@@ -5887,6 +6054,8 @@ class TileLayer extends CanvasLayer
|
|
|
5887
6054
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
5888
6055
|
setData(layerPos, data, redraw=false)
|
|
5889
6056
|
{
|
|
6057
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6058
|
+
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
5890
6059
|
if (layerPos.arrayCheck(this.size))
|
|
5891
6060
|
{
|
|
5892
6061
|
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
@@ -5898,7 +6067,10 @@ class TileLayer extends CanvasLayer
|
|
|
5898
6067
|
* @param {Vector2} layerPos - Local position in array
|
|
5899
6068
|
* @return {TileLayerData} */
|
|
5900
6069
|
getData(layerPos)
|
|
5901
|
-
{
|
|
6070
|
+
{
|
|
6071
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6072
|
+
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
6073
|
+
}
|
|
5902
6074
|
|
|
5903
6075
|
// Render the tile layer, called automatically by the engine
|
|
5904
6076
|
render()
|
|
@@ -5907,9 +6079,10 @@ class TileLayer extends CanvasLayer
|
|
|
5907
6079
|
|
|
5908
6080
|
// draw the tile layer as a single tile
|
|
5909
6081
|
const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
|
|
5910
|
-
const
|
|
6082
|
+
const size = this.drawSize || this.size;
|
|
6083
|
+
const pos = this.pos.add(size.scale(.5));
|
|
5911
6084
|
const useWebGL = glEnable && this.glTexture !== undefined;
|
|
5912
|
-
drawTile(pos,
|
|
6085
|
+
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
5913
6086
|
}
|
|
5914
6087
|
|
|
5915
6088
|
/** Draw all the tile data to an offscreen canvas
|
|
@@ -6001,6 +6174,7 @@ class TileLayer extends CanvasLayer
|
|
|
6001
6174
|
* - there can be multiple tile collision layers
|
|
6002
6175
|
* - tile collision layers should not overlap each other
|
|
6003
6176
|
* @extends TileLayer
|
|
6177
|
+
* @memberof TileLayers
|
|
6004
6178
|
*/
|
|
6005
6179
|
class TileCollisionLayer extends TileLayer
|
|
6006
6180
|
{
|
|
@@ -6013,8 +6187,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6013
6187
|
*/
|
|
6014
6188
|
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
6015
6189
|
{
|
|
6016
|
-
|
|
6017
|
-
super(position, size.floor(), tileInfo, scale, renderOrder, useWebGL);
|
|
6190
|
+
super(position, size.floor(), tileInfo, renderOrder, useWebGL);
|
|
6018
6191
|
|
|
6019
6192
|
/** @property {Array<number>} - The tile collision grid */
|
|
6020
6193
|
this.collisionData = [];
|
|
@@ -6044,6 +6217,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6044
6217
|
* @param {Vector2} size - width and height of tile collision 2d grid */
|
|
6045
6218
|
initCollision(size)
|
|
6046
6219
|
{
|
|
6220
|
+
ASSERT(isVector2(size), 'size must be a Vector2');
|
|
6047
6221
|
this.size = size.floor();
|
|
6048
6222
|
this.collisionData = [];
|
|
6049
6223
|
this.collisionData.length = size.area();
|
|
@@ -6055,6 +6229,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6055
6229
|
* @param {number} [data] */
|
|
6056
6230
|
setCollisionData(gridPos, data=1)
|
|
6057
6231
|
{
|
|
6232
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
6058
6233
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
6059
6234
|
gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
6060
6235
|
}
|
|
@@ -6064,6 +6239,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6064
6239
|
* @return {number} */
|
|
6065
6240
|
getCollisionData(gridPos)
|
|
6066
6241
|
{
|
|
6242
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
6067
6243
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
6068
6244
|
return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
6069
6245
|
}
|
|
@@ -6075,6 +6251,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
6075
6251
|
* @return {boolean} */
|
|
6076
6252
|
collisionTest(pos, size=new Vector2, object)
|
|
6077
6253
|
{
|
|
6254
|
+
ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
|
|
6255
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
6256
|
+
|
|
6078
6257
|
// transform to local layer space
|
|
6079
6258
|
const posX = pos.x - this.pos.x;
|
|
6080
6259
|
const posY = pos.y - this.pos.y;
|
|
@@ -6105,6 +6284,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
6105
6284
|
* @return {Vector2} */
|
|
6106
6285
|
collisionRaycast(posStart, posEnd, object)
|
|
6107
6286
|
{
|
|
6287
|
+
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
6288
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
6289
|
+
|
|
6108
6290
|
// transform to local layer space
|
|
6109
6291
|
const posStartX = posStart.x - this.pos.x;
|
|
6110
6292
|
const posStartY = posStart.y - this.pos.y;
|
|
@@ -6154,9 +6336,16 @@ class TileCollisionLayer extends TileLayer
|
|
|
6154
6336
|
* LittleJS Particle System
|
|
6155
6337
|
*/
|
|
6156
6338
|
|
|
6339
|
+
/**
|
|
6340
|
+
* @callback ParticleCallbackFunction - Function that processes a particle
|
|
6341
|
+
* @param {Particle} particle
|
|
6342
|
+
* @memberof Engine
|
|
6343
|
+
*/
|
|
6344
|
+
|
|
6157
6345
|
/**
|
|
6158
6346
|
* Particle Emitter - Spawns particles with the given settings
|
|
6159
6347
|
* @extends EngineObject
|
|
6348
|
+
* @memberof Engine
|
|
6160
6349
|
* @example
|
|
6161
6350
|
* // create a particle emitter
|
|
6162
6351
|
* let pos = vec2(2,3);
|
|
@@ -6181,10 +6370,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6181
6370
|
* @param {number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
6182
6371
|
* @param {number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
6183
6372
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
6184
|
-
* @param {Color} [colorStartA=
|
|
6185
|
-
* @param {Color} [colorStartB=
|
|
6186
|
-
* @param {Color} [colorEndA=
|
|
6187
|
-
* @param {Color} [colorEndB=
|
|
6373
|
+
* @param {Color} [colorStartA=WHITE] - Color at start of life 1, randomized between start colors
|
|
6374
|
+
* @param {Color} [colorStartB=WHITE] - Color at start of life 2, randomized between start colors
|
|
6375
|
+
* @param {Color} [colorEndA=CLEAR_WHITE] - Color at end of life 1, randomized between end colors
|
|
6376
|
+
* @param {Color} [colorEndB=CLEAR_WHITE] - Color at end of life 2, randomized between end colors
|
|
6188
6377
|
* @param {number} [particleTime] - How long particles live
|
|
6189
6378
|
* @param {number} [sizeStart] - How big are particles at start
|
|
6190
6379
|
* @param {number} [sizeEnd] - How big are particles at end
|
|
@@ -6211,10 +6400,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6211
6400
|
emitRate = 100,
|
|
6212
6401
|
emitConeAngle = PI,
|
|
6213
6402
|
tileInfo,
|
|
6214
|
-
colorStartA =
|
|
6215
|
-
colorStartB =
|
|
6216
|
-
colorEndA =
|
|
6217
|
-
colorEndB =
|
|
6403
|
+
colorStartA = WHITE,
|
|
6404
|
+
colorStartB = WHITE,
|
|
6405
|
+
colorEndA = CLEAR_WHITE,
|
|
6406
|
+
colorEndB = CLEAR_WHITE,
|
|
6218
6407
|
particleTime = .5,
|
|
6219
6408
|
sizeStart = .1,
|
|
6220
6409
|
sizeEnd = 1,
|
|
@@ -6237,7 +6426,8 @@ class ParticleEmitter extends EngineObject
|
|
|
6237
6426
|
|
|
6238
6427
|
// emitter settings
|
|
6239
6428
|
/** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
6240
|
-
this.emitSize = emitSize
|
|
6429
|
+
this.emitSize = emitSize instanceof Vector2 ?
|
|
6430
|
+
emitSize.copy() : emitSize;
|
|
6241
6431
|
/** @property {number} - How long to stay alive (0 is forever) */
|
|
6242
6432
|
this.emitTime = emitTime;
|
|
6243
6433
|
/** @property {number} - How many particles per second to spawn, does not emit if 0 */
|
|
@@ -6247,13 +6437,13 @@ class ParticleEmitter extends EngineObject
|
|
|
6247
6437
|
|
|
6248
6438
|
// color settings
|
|
6249
6439
|
/** @property {Color} - Color at start of life 1, randomized between start colors */
|
|
6250
|
-
this.colorStartA = colorStartA;
|
|
6440
|
+
this.colorStartA = colorStartA.copy();
|
|
6251
6441
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
6252
|
-
this.colorStartB = colorStartB;
|
|
6442
|
+
this.colorStartB = colorStartB.copy();
|
|
6253
6443
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
6254
|
-
this.colorEndA = colorEndA;
|
|
6444
|
+
this.colorEndA = colorEndA.copy();
|
|
6255
6445
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
6256
|
-
this.colorEndB = colorEndB;
|
|
6446
|
+
this.colorEndB = colorEndB.copy();
|
|
6257
6447
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
6258
6448
|
this.randomColorLinear = randomColorLinear;
|
|
6259
6449
|
|
|
@@ -6288,9 +6478,9 @@ class ParticleEmitter extends EngineObject
|
|
|
6288
6478
|
this.localSpace = localSpace;
|
|
6289
6479
|
/** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
6290
6480
|
this.trailScale = 0;
|
|
6291
|
-
/** @property {
|
|
6481
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is destroyed */
|
|
6292
6482
|
this.particleDestroyCallback = undefined;
|
|
6293
|
-
/** @property {
|
|
6483
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is created */
|
|
6294
6484
|
this.particleCreateCallback = undefined;
|
|
6295
6485
|
/** @property {number} - Track particle emit time */
|
|
6296
6486
|
this.emitTimeBuffer = 0;
|
|
@@ -6391,6 +6581,7 @@ class ParticleEmitter extends EngineObject
|
|
|
6391
6581
|
/**
|
|
6392
6582
|
* Particle Object - Created automatically by Particle Emitters
|
|
6393
6583
|
* @extends EngineObject
|
|
6584
|
+
* @memberof Engine
|
|
6394
6585
|
*/
|
|
6395
6586
|
class Particle extends EngineObject
|
|
6396
6587
|
{
|
|
@@ -6409,7 +6600,7 @@ class Particle extends EngineObject
|
|
|
6409
6600
|
* @param {boolean} additive - Does it use additive blend mode
|
|
6410
6601
|
* @param {number} trailScale - If a trail, how long to make it
|
|
6411
6602
|
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
6412
|
-
* @param {
|
|
6603
|
+
* @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
|
|
6413
6604
|
*/
|
|
6414
6605
|
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
6415
6606
|
)
|
|
@@ -6418,14 +6609,14 @@ class Particle extends EngineObject
|
|
|
6418
6609
|
|
|
6419
6610
|
/** @property {Color} - Color at start of life */
|
|
6420
6611
|
this.colorStart = colorStart;
|
|
6421
|
-
/** @property {Color} -
|
|
6422
|
-
this.
|
|
6612
|
+
/** @property {Color} - Color at end of life */
|
|
6613
|
+
this.colorEnd = colorEnd;
|
|
6423
6614
|
/** @property {number} - How long to live for */
|
|
6424
6615
|
this.lifeTime = lifeTime;
|
|
6425
6616
|
/** @property {number} - Size at start of life */
|
|
6426
6617
|
this.sizeStart = sizeStart;
|
|
6427
|
-
/** @property {number} -
|
|
6428
|
-
this.
|
|
6618
|
+
/** @property {number} - Size at end of life */
|
|
6619
|
+
this.sizeEnd = sizeEnd;
|
|
6429
6620
|
/** @property {number} - How quick to fade in/out */
|
|
6430
6621
|
this.fadeRate = fadeRate;
|
|
6431
6622
|
/** @property {boolean} - Is it additive */
|
|
@@ -6434,7 +6625,7 @@ class Particle extends EngineObject
|
|
|
6434
6625
|
this.trailScale = trailScale;
|
|
6435
6626
|
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
6436
6627
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
6437
|
-
/** @property {
|
|
6628
|
+
/** @property {ParticleCallbackFunction} - Called when particle dies */
|
|
6438
6629
|
this.destroyCallback = destroyCallback;
|
|
6439
6630
|
// particles do not clamp speed by default
|
|
6440
6631
|
this.clampSpeed = false;
|
|
@@ -6461,54 +6652,58 @@ class Particle extends EngineObject
|
|
|
6461
6652
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
6462
6653
|
render()
|
|
6463
6654
|
{
|
|
6464
|
-
//
|
|
6465
|
-
const
|
|
6466
|
-
const radius = this.sizeStart +
|
|
6467
|
-
|
|
6655
|
+
// lerp color and size
|
|
6656
|
+
const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
|
|
6657
|
+
const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
|
|
6658
|
+
this.size.x = this.size.y = radius;
|
|
6659
|
+
this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
|
|
6660
|
+
this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
|
|
6661
|
+
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
6662
|
+
this.color.a = p2 * this.colorStart.a + p1 * this.colorEnd.a;
|
|
6663
|
+
|
|
6664
|
+
// fade alpha
|
|
6468
6665
|
const fadeRate = this.fadeRate/2;
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
this.colorStart.g + p * this.colorEndDelta.g,
|
|
6472
|
-
this.colorStart.b + p * this.colorEndDelta.b,
|
|
6473
|
-
(this.colorStart.a + p * this.colorEndDelta.a) *
|
|
6474
|
-
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
6666
|
+
this.color.a *= p1 < fadeRate ? p1/fadeRate :
|
|
6667
|
+
p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
|
|
6475
6668
|
|
|
6476
6669
|
// draw the particle
|
|
6477
6670
|
this.additive && setBlendMode(true);
|
|
6478
6671
|
|
|
6672
|
+
// update the position and angle for drawing
|
|
6479
6673
|
let pos = this.pos, angle = this.angle;
|
|
6480
6674
|
if (this.localSpaceEmitter)
|
|
6481
6675
|
{
|
|
6482
6676
|
// in local space of emitter
|
|
6483
|
-
|
|
6677
|
+
const a = this.localSpaceEmitter.angle;
|
|
6678
|
+
const c = Math.cos(a), s = Math.sin(a);
|
|
6679
|
+
pos = this.localSpaceEmitter.pos.add(
|
|
6680
|
+
new Vector2(pos.x*c - pos.y*s, pos.x*s + pos.y*c));
|
|
6484
6681
|
angle += this.localSpaceEmitter.angle;
|
|
6485
6682
|
}
|
|
6486
6683
|
if (this.trailScale)
|
|
6487
6684
|
{
|
|
6488
6685
|
// trail style particles
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
const speed =
|
|
6686
|
+
const direction = this.localSpaceEmitter ?
|
|
6687
|
+
this.velocity.rotate(-this.localSpaceEmitter.angle) :
|
|
6688
|
+
this.velocity;
|
|
6689
|
+
const speed = direction.length();
|
|
6493
6690
|
if (speed)
|
|
6494
6691
|
{
|
|
6495
|
-
|
|
6692
|
+
// stretch in direction of motion
|
|
6496
6693
|
const trailLength = speed * this.trailScale;
|
|
6497
|
-
size.y = max(size.x, trailLength);
|
|
6498
|
-
angle = direction.
|
|
6499
|
-
drawTile(pos
|
|
6694
|
+
this.size.y = max(this.size.x, trailLength);
|
|
6695
|
+
angle = Math.atan2(direction.x, direction.y);
|
|
6696
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
6500
6697
|
}
|
|
6501
6698
|
}
|
|
6502
6699
|
else
|
|
6503
|
-
drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
|
|
6700
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
6504
6701
|
this.additive && setBlendMode();
|
|
6505
|
-
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
6702
|
+
debugParticles && debugRect(pos, this.size, '#f005', 0, angle);
|
|
6506
6703
|
|
|
6507
|
-
if (
|
|
6704
|
+
if (p1 === 1)
|
|
6508
6705
|
{
|
|
6509
|
-
// destroy particle when
|
|
6510
|
-
this.color = color;
|
|
6511
|
-
this.size = size;
|
|
6706
|
+
// destroy particle when its time runs out
|
|
6512
6707
|
this.destroyCallback && this.destroyCallback(this);
|
|
6513
6708
|
this.destroyed = 1;
|
|
6514
6709
|
}
|
|
@@ -6573,8 +6768,14 @@ function medalsInit(saveName)
|
|
|
6573
6768
|
}
|
|
6574
6769
|
}
|
|
6575
6770
|
|
|
6771
|
+
/**
|
|
6772
|
+
* @callback MedalCallbackFunction - Function that processes a medal
|
|
6773
|
+
* @param {Medal} medal
|
|
6774
|
+
* @memberof Medals
|
|
6775
|
+
*/
|
|
6776
|
+
|
|
6576
6777
|
/** Calls a function for each medal
|
|
6577
|
-
* @param {
|
|
6778
|
+
* @param {MedalCallbackFunction} callback
|
|
6578
6779
|
* @memberof Medals */
|
|
6579
6780
|
function medalsForEach(callback)
|
|
6580
6781
|
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
@@ -6583,6 +6784,7 @@ function medalsForEach(callback)
|
|
|
6583
6784
|
|
|
6584
6785
|
/**
|
|
6585
6786
|
* Medal - Tracks an unlockable medal
|
|
6787
|
+
* @memberof Medals
|
|
6586
6788
|
* @example
|
|
6587
6789
|
* // create a medal
|
|
6588
6790
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
@@ -6651,11 +6853,12 @@ class Medal
|
|
|
6651
6853
|
const height = medalDisplaySize.y;
|
|
6652
6854
|
const x = overlayCanvas.width - width;
|
|
6653
6855
|
const y = -height*hidePercent;
|
|
6856
|
+
const backgroundColor = hsl(0,0,.9);
|
|
6654
6857
|
|
|
6655
6858
|
// draw containing rect and clip to that region
|
|
6656
6859
|
context.save();
|
|
6657
6860
|
context.beginPath();
|
|
6658
|
-
context.fillStyle =
|
|
6861
|
+
context.fillStyle = backgroundColor.toString();
|
|
6659
6862
|
context.strokeStyle = BLACK.toString();
|
|
6660
6863
|
context.lineWidth = 3;
|
|
6661
6864
|
context.rect(x, y, width, height);
|
|
@@ -7457,24 +7660,25 @@ function glPolyStrip(points)
|
|
|
7457
7660
|
return strip;
|
|
7458
7661
|
}
|
|
7459
7662
|
/**
|
|
7460
|
-
* LittleJS Newgrounds
|
|
7663
|
+
* LittleJS Newgrounds Plugin
|
|
7461
7664
|
* - NewgroundsMedal extends Medal with Newgrounds API functionality
|
|
7462
|
-
* - Call new NewgroundsPlugin() to setup Newgrounds
|
|
7665
|
+
* - Call new NewgroundsPlugin(app_id) to setup Newgrounds
|
|
7463
7666
|
* - Uses CryptoJS for encryption if optional cipher is provided
|
|
7667
|
+
* - provides functions to interact with medals scoreboards
|
|
7464
7668
|
* - Keeps connection alive and logs views
|
|
7465
|
-
*
|
|
7466
|
-
* - Functions to unlock medals
|
|
7669
|
+
* @namespace Newgrounds
|
|
7467
7670
|
*/
|
|
7468
7671
|
|
|
7469
7672
|
/** Global Newgrounds object
|
|
7470
7673
|
* @type {NewgroundsPlugin}
|
|
7471
|
-
* @memberof
|
|
7674
|
+
* @memberof Newgrounds */
|
|
7472
7675
|
let newgrounds;
|
|
7473
7676
|
|
|
7474
7677
|
///////////////////////////////////////////////////////////////////////////////
|
|
7475
7678
|
/**
|
|
7476
7679
|
* Newgrounds medal auto unlocks in newgrounds API
|
|
7477
7680
|
* @extends Medal
|
|
7681
|
+
* @memberof Newgrounds
|
|
7478
7682
|
*/
|
|
7479
7683
|
class NewgroundsMedal extends Medal
|
|
7480
7684
|
{
|
|
@@ -7499,6 +7703,7 @@ class NewgroundsMedal extends Medal
|
|
|
7499
7703
|
///////////////////////////////////////////////////////////////////////////////
|
|
7500
7704
|
/**
|
|
7501
7705
|
* Newgrounds API object
|
|
7706
|
+
* @memberof Newgrounds
|
|
7502
7707
|
*/
|
|
7503
7708
|
class NewgroundsPlugin
|
|
7504
7709
|
{
|
|
@@ -7532,7 +7737,7 @@ class NewgroundsPlugin
|
|
|
7532
7737
|
// get medals
|
|
7533
7738
|
const medalsResult = this.call('Medal.getList');
|
|
7534
7739
|
this.medals = medalsResult ? medalsResult.result.data['medals'] : [];
|
|
7535
|
-
debugMedals &&
|
|
7740
|
+
debugMedals && LOG(this.medals);
|
|
7536
7741
|
for (const newgroundsMedal of this.medals)
|
|
7537
7742
|
{
|
|
7538
7743
|
const medal = medals[newgroundsMedal['id']];
|
|
@@ -7555,7 +7760,7 @@ class NewgroundsPlugin
|
|
|
7555
7760
|
// get scoreboards
|
|
7556
7761
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
7557
7762
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
7558
|
-
debugMedals &&
|
|
7763
|
+
debugMedals && LOG(this.scoreboards);
|
|
7559
7764
|
|
|
7560
7765
|
// keep the session alive with a ping every minute
|
|
7561
7766
|
const keepAliveMS = 60 * 1e3;
|
|
@@ -7624,29 +7829,32 @@ class NewgroundsPlugin
|
|
|
7624
7829
|
try { xmlHttp.send(formData); }
|
|
7625
7830
|
catch(e)
|
|
7626
7831
|
{
|
|
7627
|
-
debugMedals &&
|
|
7832
|
+
debugMedals && LOG('newgrounds call failed', e);
|
|
7628
7833
|
return;
|
|
7629
7834
|
}
|
|
7630
|
-
debugMedals &&
|
|
7835
|
+
debugMedals && LOG(xmlHttp.responseText);
|
|
7631
7836
|
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
7632
7837
|
}
|
|
7633
7838
|
}
|
|
7634
7839
|
/**
|
|
7635
7840
|
* LittleJS Post Processing Plugin
|
|
7636
7841
|
* - Supports shadertoy style post processing shaders
|
|
7637
|
-
* - call new
|
|
7842
|
+
* - call new PostProcessPlugin() to setup post processing
|
|
7638
7843
|
* - can be enabled to pass other canvases through a final shader
|
|
7844
|
+
* @namespace PostProcess
|
|
7639
7845
|
*/
|
|
7640
7846
|
|
|
7641
7847
|
///////////////////////////////////////////////////////////////////////////////
|
|
7642
7848
|
|
|
7643
7849
|
/** Global Post Process plugin object
|
|
7644
|
-
* @type {PostProcessPlugin}
|
|
7850
|
+
* @type {PostProcessPlugin}
|
|
7851
|
+
* @memberof PostProcess */
|
|
7645
7852
|
let postProcess;
|
|
7646
7853
|
|
|
7647
7854
|
/////////////////////////////////////////////////////////////////////////
|
|
7648
7855
|
/**
|
|
7649
7856
|
* UI System Global Object
|
|
7857
|
+
* @memberof PostProcess
|
|
7650
7858
|
*/
|
|
7651
7859
|
class PostProcessPlugin
|
|
7652
7860
|
{
|
|
@@ -7754,12 +7962,15 @@ class PostProcessPlugin
|
|
|
7754
7962
|
}
|
|
7755
7963
|
/**
|
|
7756
7964
|
* LittleJS ZzFXM Plugin
|
|
7965
|
+
* @namespace ZzFXM
|
|
7757
7966
|
*/
|
|
7758
7967
|
|
|
7759
7968
|
/**
|
|
7760
7969
|
* Music Object - Stores a zzfx music track for later use
|
|
7761
7970
|
*
|
|
7762
7971
|
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
7972
|
+
* @extends Sound
|
|
7973
|
+
* @memberof ZzFXM
|
|
7763
7974
|
* @example
|
|
7764
7975
|
* // create some music
|
|
7765
7976
|
* const music_example = new Music(
|
|
@@ -7818,7 +8029,8 @@ class ZzFXMusic extends Sound
|
|
|
7818
8029
|
* @param {Array} patterns - Array of pattern data
|
|
7819
8030
|
* @param {Array} sequence - Array of pattern indexes
|
|
7820
8031
|
* @param {number} [BPM] - Playback speed of the song in BPM
|
|
7821
|
-
* @return {Array} - Left and right channel sample data
|
|
8032
|
+
* @return {Array} - Left and right channel sample data
|
|
8033
|
+
* @memberof ZzFXM */
|
|
7822
8034
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
7823
8035
|
{
|
|
7824
8036
|
let i, j, k;
|
|
@@ -7921,17 +8133,20 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
7921
8133
|
* - Buttons
|
|
7922
8134
|
* - Checkboxes
|
|
7923
8135
|
* - Images
|
|
8136
|
+
* @namespace UISystem
|
|
7924
8137
|
*/
|
|
7925
8138
|
|
|
7926
8139
|
///////////////////////////////////////////////////////////////////////////////
|
|
7927
8140
|
|
|
7928
8141
|
/** Global UI system plugin object
|
|
7929
|
-
* @type {UISystemPlugin}
|
|
8142
|
+
* @type {UISystemPlugin}
|
|
8143
|
+
* @memberof UISystem */
|
|
7930
8144
|
let uiSystem;
|
|
7931
8145
|
|
|
7932
8146
|
///////////////////////////////////////////////////////////////////////////////
|
|
7933
8147
|
/**
|
|
7934
8148
|
* UI System Global Object
|
|
8149
|
+
* @memberof UISystem
|
|
7935
8150
|
*/
|
|
7936
8151
|
class UISystemPlugin
|
|
7937
8152
|
{
|
|
@@ -7976,10 +8191,12 @@ class UISystemPlugin
|
|
|
7976
8191
|
this.uiObjects = [];
|
|
7977
8192
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
7978
8193
|
this.uiContext = context;
|
|
7979
|
-
/** @property {UIObject} - Top most object user is over */
|
|
7980
|
-
this.hoverObject = undefined;
|
|
7981
8194
|
/** @property {UIObject} - Object user is currently interacting with */
|
|
7982
8195
|
this.activeObject = undefined;
|
|
8196
|
+
/** @property {UIObject} - Top most object user is over */
|
|
8197
|
+
this.hoverObject = undefined;
|
|
8198
|
+
/** @property {UIObject} - Hover object at start of update */
|
|
8199
|
+
this.lastHoverObject = undefined;
|
|
7983
8200
|
|
|
7984
8201
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7985
8202
|
|
|
@@ -8010,6 +8227,7 @@ class UISystemPlugin
|
|
|
8010
8227
|
updateInvisibleObject(o);
|
|
8011
8228
|
}
|
|
8012
8229
|
// reset hover object at start of update
|
|
8230
|
+
uiSystem.lastHoverObject = uiSystem.hoverObject;
|
|
8013
8231
|
uiSystem.hoverObject = undefined;
|
|
8014
8232
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
8015
8233
|
{
|
|
@@ -8042,6 +8260,13 @@ class UISystemPlugin
|
|
|
8042
8260
|
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius] */
|
|
8043
8261
|
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius)
|
|
8044
8262
|
{
|
|
8263
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
8264
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
8265
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
8266
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
8267
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
8268
|
+
ASSERT(isNumber(cornerRadius), 'cornerRadius must be a number');
|
|
8269
|
+
|
|
8045
8270
|
const context = uiSystem.uiContext;
|
|
8046
8271
|
context.fillStyle = color.toString();
|
|
8047
8272
|
context.beginPath();
|
|
@@ -8065,6 +8290,11 @@ class UISystemPlugin
|
|
|
8065
8290
|
* @param {Color} [lineColor=uiSystem.defaultLineColor] */
|
|
8066
8291
|
drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
|
|
8067
8292
|
{
|
|
8293
|
+
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
8294
|
+
ASSERT(isVector2(posB), 'posB must be a vec2');
|
|
8295
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
8296
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
8297
|
+
|
|
8068
8298
|
const context = uiSystem.uiContext;
|
|
8069
8299
|
context.strokeStyle = lineColor.toString();
|
|
8070
8300
|
context.lineWidth = lineWidth;
|
|
@@ -8100,12 +8330,37 @@ class UISystemPlugin
|
|
|
8100
8330
|
{
|
|
8101
8331
|
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
8102
8332
|
}
|
|
8333
|
+
|
|
8334
|
+
/**
|
|
8335
|
+
* @callback DragAndDropCallback - Callback for drag and drop events
|
|
8336
|
+
* @param {DragEvent} event - The drag event
|
|
8337
|
+
* @memberof UISystem
|
|
8338
|
+
*/
|
|
8339
|
+
|
|
8340
|
+
/** Setup drag and drop event handlers
|
|
8341
|
+
* Automatically prevents defaults and calls the given functions
|
|
8342
|
+
* @param {DragAndDropCallback} [onDrop] - when a file is dropped
|
|
8343
|
+
* @param {DragAndDropCallback} [onDragEnter] - when a file is dragged onto the window
|
|
8344
|
+
* @param {DragAndDropCallback} [onDragLeave] - when a file is dragged off the window
|
|
8345
|
+
* @param {DragAndDropCallback} [onDragOver] - continously when dragging over */
|
|
8346
|
+
setupDragAndDrop(onDrop, onDragEnter, onDragLeave, onDragOver)
|
|
8347
|
+
{
|
|
8348
|
+
function setCallback(callback, listenerType)
|
|
8349
|
+
{
|
|
8350
|
+
function listener(e) { e.preventDefault(); callback && callback(e); }
|
|
8351
|
+
document.addEventListener(listenerType, listener);
|
|
8352
|
+
}
|
|
8353
|
+
setCallback(onDrop, 'drop');
|
|
8354
|
+
setCallback(onDragEnter, 'dragenter');
|
|
8355
|
+
setCallback(onDragLeave, 'dragleave');
|
|
8356
|
+
setCallback(onDragOver, 'dragover');
|
|
8357
|
+
}
|
|
8103
8358
|
}
|
|
8104
8359
|
|
|
8105
8360
|
///////////////////////////////////////////////////////////////////////////////
|
|
8106
8361
|
/**
|
|
8107
8362
|
* UI Object - Base level object for all UI elements
|
|
8108
|
-
*/
|
|
8363
|
+
* @memberof UISystem */
|
|
8109
8364
|
class UIObject
|
|
8110
8365
|
{
|
|
8111
8366
|
/** Create a UIObject
|
|
@@ -8114,6 +8369,9 @@ class UIObject
|
|
|
8114
8369
|
*/
|
|
8115
8370
|
constructor(pos=vec2(), size=vec2())
|
|
8116
8371
|
{
|
|
8372
|
+
ASSERT(isVector2(pos), 'ui object pos must be a vec2');
|
|
8373
|
+
ASSERT(isVector2(size), 'ui object size must be a vec2');
|
|
8374
|
+
|
|
8117
8375
|
/** @property {Vector2} - Local position of the object */
|
|
8118
8376
|
this.localPos = pos.copy();
|
|
8119
8377
|
/** @property {Vector2} - Screen space position of the object */
|
|
@@ -8121,21 +8379,21 @@ class UIObject
|
|
|
8121
8379
|
/** @property {Vector2} - Screen space size of the object */
|
|
8122
8380
|
this.size = size.copy();
|
|
8123
8381
|
/** @property {Color} - Color of the object */
|
|
8124
|
-
this.color = uiSystem.defaultColor;
|
|
8382
|
+
this.color = uiSystem.defaultColor.copy();
|
|
8125
8383
|
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
8126
8384
|
this.activeColor = undefined;
|
|
8127
8385
|
/** @property {string} - Text for this ui object */
|
|
8128
8386
|
this.text = undefined;
|
|
8129
8387
|
/** @property {Color} - Color when disabled */
|
|
8130
|
-
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
8388
|
+
this.disabledColor = uiSystem.defaultDisabledColor.copy();
|
|
8131
8389
|
/** @property {boolean} - Is this object disabled? */
|
|
8132
8390
|
this.disabled = false;
|
|
8133
8391
|
/** @property {Color} - Color for text */
|
|
8134
|
-
this.textColor = uiSystem.defaultTextColor
|
|
8392
|
+
this.textColor = uiSystem.defaultTextColor.copy()
|
|
8135
8393
|
/** @property {Color} - Color used when hovering over the object */
|
|
8136
|
-
this.hoverColor = uiSystem.defaultHoverColor
|
|
8394
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy()
|
|
8137
8395
|
/** @property {Color} - Color for line drawing */
|
|
8138
|
-
this.lineColor = uiSystem.defaultLineColor
|
|
8396
|
+
this.lineColor = uiSystem.defaultLineColor.copy()
|
|
8139
8397
|
/** @property {number} - Width for line drawing */
|
|
8140
8398
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
8141
8399
|
/** @property {number} - Corner radius for rounded rects */
|
|
@@ -8192,7 +8450,7 @@ class UIObject
|
|
|
8192
8450
|
/** Update the object, called automatically by plugin once each frame */
|
|
8193
8451
|
update()
|
|
8194
8452
|
{
|
|
8195
|
-
const wasHover = this
|
|
8453
|
+
const wasHover = uiSystem.lastHoverObject === this;
|
|
8196
8454
|
const isActive = this.isActiveObject();
|
|
8197
8455
|
const mouseDown = mouseIsDown(0);
|
|
8198
8456
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
@@ -8205,15 +8463,14 @@ class UIObject
|
|
|
8205
8463
|
}
|
|
8206
8464
|
if (this.isHoverObject())
|
|
8207
8465
|
{
|
|
8208
|
-
if (mousePress)
|
|
8209
|
-
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
8210
8466
|
if (!this.disabled)
|
|
8211
8467
|
{
|
|
8212
8468
|
if (mousePress)
|
|
8213
8469
|
{
|
|
8214
8470
|
if (this.interactive)
|
|
8215
8471
|
{
|
|
8216
|
-
this.
|
|
8472
|
+
if (!this.dragActivate || (!wasHover || mouseWasPressed(0)))
|
|
8473
|
+
this.onPress();
|
|
8217
8474
|
if (this.soundPress)
|
|
8218
8475
|
this.soundPress.play();
|
|
8219
8476
|
if (uiSystem.activeObject && !isActive)
|
|
@@ -8221,13 +8478,15 @@ class UIObject
|
|
|
8221
8478
|
uiSystem.activeObject = this;
|
|
8222
8479
|
}
|
|
8223
8480
|
}
|
|
8224
|
-
if (!mouseDown &&
|
|
8481
|
+
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
8225
8482
|
{
|
|
8226
8483
|
this.onClick();
|
|
8227
8484
|
if (this.soundClick)
|
|
8228
8485
|
this.soundClick.play();
|
|
8229
8486
|
}
|
|
8230
8487
|
}
|
|
8488
|
+
// clear mouse was pressed state even when disabled
|
|
8489
|
+
mousePress && inputClearKey(0,0,0,1,0);
|
|
8231
8490
|
}
|
|
8232
8491
|
if (isActive)
|
|
8233
8492
|
if (!mouseDown || (this.dragActivate && !this.isHoverObject()))
|
|
@@ -8238,6 +8497,7 @@ class UIObject
|
|
|
8238
8497
|
uiSystem.activeObject = undefined;
|
|
8239
8498
|
}
|
|
8240
8499
|
|
|
8500
|
+
// call enter/leave events
|
|
8241
8501
|
if (this.isHoverObject() !== wasHover)
|
|
8242
8502
|
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
8243
8503
|
}
|
|
@@ -8248,7 +8508,7 @@ class UIObject
|
|
|
8248
8508
|
if (!this.size.x || !this.size.y) return;
|
|
8249
8509
|
|
|
8250
8510
|
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
8251
|
-
const color = this.
|
|
8511
|
+
const color = this.disabled ? this.disabledColor : this.interactive ? this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
8252
8512
|
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
8253
8513
|
}
|
|
8254
8514
|
|
|
@@ -8299,6 +8559,7 @@ class UIObject
|
|
|
8299
8559
|
/**
|
|
8300
8560
|
* UIText - A UI object that displays text
|
|
8301
8561
|
* @extends UIObject
|
|
8562
|
+
* @memberof UISystem
|
|
8302
8563
|
*/
|
|
8303
8564
|
class UIText extends UIObject
|
|
8304
8565
|
{
|
|
@@ -8313,6 +8574,10 @@ class UIText extends UIObject
|
|
|
8313
8574
|
{
|
|
8314
8575
|
super(pos, size);
|
|
8315
8576
|
|
|
8577
|
+
ASSERT(isString(text), 'ui text must be a string');
|
|
8578
|
+
ASSERT(['left','center','right'].includes(align), 'ui text align must be left, center, or right');
|
|
8579
|
+
ASSERT(isString(font), 'ui text font must be a string');
|
|
8580
|
+
|
|
8316
8581
|
// set properties
|
|
8317
8582
|
this.text = text;
|
|
8318
8583
|
this.align = align;
|
|
@@ -8332,6 +8597,7 @@ class UIText extends UIObject
|
|
|
8332
8597
|
/**
|
|
8333
8598
|
* UITile - A UI object that displays a tile image
|
|
8334
8599
|
* @extends UIObject
|
|
8600
|
+
* @memberof UISystem
|
|
8335
8601
|
*/
|
|
8336
8602
|
class UITile extends UIObject
|
|
8337
8603
|
{
|
|
@@ -8346,15 +8612,19 @@ class UITile extends UIObject
|
|
|
8346
8612
|
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false)
|
|
8347
8613
|
{
|
|
8348
8614
|
super(pos, size);
|
|
8615
|
+
|
|
8616
|
+
ASSERT(tileInfo instanceof TileInfo, 'ui tile tileInfo must be a TileInfo');
|
|
8617
|
+
ASSERT(isColor(color), 'ui tile color must be a color');
|
|
8618
|
+
ASSERT(isNumber(angle), 'ui tile angle must be a number');
|
|
8619
|
+
|
|
8349
8620
|
/** @property {TileInfo} - Tile image to use */
|
|
8350
8621
|
this.tileInfo = tileInfo;
|
|
8351
8622
|
/** @property {number} - Angle to rotate in radians */
|
|
8352
8623
|
this.angle = angle;
|
|
8353
8624
|
/** @property {boolean} - Should it be mirrored? */
|
|
8354
8625
|
this.mirror = mirror;
|
|
8355
|
-
|
|
8356
8626
|
// set properties
|
|
8357
|
-
this.color = color;
|
|
8627
|
+
this.color = color.copy();
|
|
8358
8628
|
}
|
|
8359
8629
|
render()
|
|
8360
8630
|
{
|
|
@@ -8366,6 +8636,7 @@ class UITile extends UIObject
|
|
|
8366
8636
|
/**
|
|
8367
8637
|
* UIButton - A UI object that acts as a button
|
|
8368
8638
|
* @extends UIObject
|
|
8639
|
+
* @memberof UISystem
|
|
8369
8640
|
*/
|
|
8370
8641
|
class UIButton extends UIObject
|
|
8371
8642
|
{
|
|
@@ -8379,9 +8650,12 @@ class UIButton extends UIObject
|
|
|
8379
8650
|
{
|
|
8380
8651
|
super(pos, size);
|
|
8381
8652
|
|
|
8653
|
+
ASSERT(isString(text), 'ui button must be a string');
|
|
8654
|
+
ASSERT(isColor(color), 'ui button color must be a color');
|
|
8655
|
+
|
|
8382
8656
|
// set properties
|
|
8383
8657
|
this.text = text;
|
|
8384
|
-
this.color = color
|
|
8658
|
+
this.color = color.copy()
|
|
8385
8659
|
this.interactive = true;
|
|
8386
8660
|
}
|
|
8387
8661
|
render()
|
|
@@ -8399,6 +8673,7 @@ class UIButton extends UIObject
|
|
|
8399
8673
|
/**
|
|
8400
8674
|
* UICheckbox - A UI object that acts as a checkbox
|
|
8401
8675
|
* @extends UIObject
|
|
8676
|
+
* @memberof UISystem
|
|
8402
8677
|
*/
|
|
8403
8678
|
class UICheckbox extends UIObject
|
|
8404
8679
|
{
|
|
@@ -8412,12 +8687,15 @@ class UICheckbox extends UIObject
|
|
|
8412
8687
|
constructor(pos, size, checked=false, text='', color=uiSystem.defaultButtonColor)
|
|
8413
8688
|
{
|
|
8414
8689
|
super(pos, size);
|
|
8690
|
+
|
|
8691
|
+
ASSERT(isString(text), 'ui checkbox must be a string');
|
|
8692
|
+
ASSERT(isColor(color), 'ui checkbox color must be a color');
|
|
8693
|
+
|
|
8415
8694
|
/** @property {boolean} - Current percentage value of this scrollbar 0-1 */
|
|
8416
8695
|
this.checked = checked;
|
|
8417
|
-
|
|
8418
8696
|
// set properties
|
|
8419
8697
|
this.text = text;
|
|
8420
|
-
this.color = color;
|
|
8698
|
+
this.color = color.copy();
|
|
8421
8699
|
this.interactive = true;
|
|
8422
8700
|
}
|
|
8423
8701
|
onClick()
|
|
@@ -8449,6 +8727,7 @@ class UICheckbox extends UIObject
|
|
|
8449
8727
|
/**
|
|
8450
8728
|
* UIScrollbar - A UI object that acts as a scrollbar
|
|
8451
8729
|
* @extends UIObject
|
|
8730
|
+
* @memberof UISystem
|
|
8452
8731
|
*/
|
|
8453
8732
|
class UIScrollbar extends UIObject
|
|
8454
8733
|
{
|
|
@@ -8464,14 +8743,19 @@ class UIScrollbar extends UIObject
|
|
|
8464
8743
|
{
|
|
8465
8744
|
super(pos, size);
|
|
8466
8745
|
|
|
8746
|
+
ASSERT(isNumber(value), 'ui scrollbar value must be a number');
|
|
8747
|
+
ASSERT(isString(text), 'ui scrollbar must be a string');
|
|
8748
|
+
ASSERT(isColor(color), 'ui scrollbar color must be a color');
|
|
8749
|
+
ASSERT(isColor(handleColor), 'ui scrollbar handleColor must be a color');
|
|
8750
|
+
|
|
8467
8751
|
/** @property {number} - Current percentage value of this scrollbar 0-1 */
|
|
8468
8752
|
this.value = value;
|
|
8469
8753
|
/** @property {Color} - Color for the handle part of the scrollbar */
|
|
8470
|
-
this.handleColor = handleColor;
|
|
8754
|
+
this.handleColor = handleColor.copy();
|
|
8471
8755
|
|
|
8472
8756
|
// set properties
|
|
8473
8757
|
this.text = text;
|
|
8474
|
-
this.color = color;
|
|
8758
|
+
this.color = color.copy();
|
|
8475
8759
|
this.interactive = true;
|
|
8476
8760
|
}
|
|
8477
8761
|
update()
|
|
@@ -8479,29 +8763,43 @@ class UIScrollbar extends UIObject
|
|
|
8479
8763
|
super.update();
|
|
8480
8764
|
if (this.isActiveObject() && this.interactive)
|
|
8481
8765
|
{
|
|
8766
|
+
// handle horizontal or vertical scrollbar
|
|
8767
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8768
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8769
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8770
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8771
|
+
|
|
8482
8772
|
// check if value changed
|
|
8483
|
-
const
|
|
8484
|
-
const
|
|
8485
|
-
const
|
|
8486
|
-
const p2 = this.pos.x + handleWidth/2;
|
|
8773
|
+
const handleWidth = barSize - handleSize;
|
|
8774
|
+
const p1 = centerPos - handleWidth/2;
|
|
8775
|
+
const p2 = centerPos + handleWidth/2;
|
|
8487
8776
|
const oldValue = this.value;
|
|
8488
|
-
this.value =
|
|
8777
|
+
this.value = isHorizontal ?
|
|
8778
|
+
percent(mousePosScreen.x, p1, p2) :
|
|
8779
|
+
percent(mousePosScreen.y, p2, p1);
|
|
8489
8780
|
this.value === oldValue || this.onChange();
|
|
8490
8781
|
}
|
|
8491
8782
|
}
|
|
8492
8783
|
render()
|
|
8493
8784
|
{
|
|
8494
8785
|
super.render();
|
|
8495
|
-
|
|
8786
|
+
|
|
8787
|
+
// handle horizontal or vertical scrollbar
|
|
8788
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8789
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8790
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8791
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8792
|
+
|
|
8496
8793
|
// draw the scrollbar handle
|
|
8497
|
-
const
|
|
8498
|
-
const
|
|
8499
|
-
const
|
|
8500
|
-
const
|
|
8501
|
-
|
|
8794
|
+
const handleWidth = barSize - handleSize;
|
|
8795
|
+
const p1 = centerPos - handleWidth/2;
|
|
8796
|
+
const p2 = centerPos + handleWidth/2;
|
|
8797
|
+
const handlePos = isHorizontal ?
|
|
8798
|
+
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
8799
|
+
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
8502
8800
|
const handleColor = this.disabled ? this.disabledColor :
|
|
8503
8801
|
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
8504
|
-
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8802
|
+
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8505
8803
|
|
|
8506
8804
|
// draw the text scaled to fit on the scrollbar
|
|
8507
8805
|
const textSize = this.getTextSize();
|
|
@@ -8548,6 +8846,7 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
|
|
|
8548
8846
|
* - Each object has a Box2D body which can have multiple fixtures and joints
|
|
8549
8847
|
* - Provides interface for Box2D body and fixture functions
|
|
8550
8848
|
* @extends EngineObject
|
|
8849
|
+
* @memberof Box2D
|
|
8551
8850
|
*/
|
|
8552
8851
|
class Box2dObject extends EngineObject
|
|
8553
8852
|
{
|
|
@@ -8559,7 +8858,7 @@ class Box2dObject extends EngineObject
|
|
|
8559
8858
|
* @param {Color} [color]
|
|
8560
8859
|
* @param {number} [bodyType]
|
|
8561
8860
|
* @param {number} [renderOrder] */
|
|
8562
|
-
constructor(pos
|
|
8861
|
+
constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
8563
8862
|
{
|
|
8564
8863
|
super(pos, size, tileInfo, angle, color, renderOrder);
|
|
8565
8864
|
|
|
@@ -8573,7 +8872,7 @@ class Box2dObject extends EngineObject
|
|
|
8573
8872
|
this.lineColor = BLACK;
|
|
8574
8873
|
}
|
|
8575
8874
|
|
|
8576
|
-
/** Destroy this object and
|
|
8875
|
+
/** Destroy this object and its physics body */
|
|
8577
8876
|
destroy()
|
|
8578
8877
|
{
|
|
8579
8878
|
// destroy physics body, fixtures, and joints
|
|
@@ -9057,6 +9356,7 @@ class Box2dRaycastResult
|
|
|
9057
9356
|
* Box2D Joint
|
|
9058
9357
|
* - Base class for Box2D joints
|
|
9059
9358
|
* - A joint is used to connect objects together
|
|
9359
|
+
* @memberof Box2D
|
|
9060
9360
|
*/
|
|
9061
9361
|
class Box2dJoint
|
|
9062
9362
|
{
|
|
@@ -9112,6 +9412,7 @@ class Box2dJoint
|
|
|
9112
9412
|
* - This a soft constraint with a max force
|
|
9113
9413
|
* - This allows the constraint to stretch and without applying huge forces
|
|
9114
9414
|
* @extends Box2dJoint
|
|
9415
|
+
* @memberof Box2D
|
|
9115
9416
|
*/
|
|
9116
9417
|
class Box2dTargetJoint extends Box2dJoint
|
|
9117
9418
|
{
|
|
@@ -9161,6 +9462,7 @@ class Box2dTargetJoint extends Box2dJoint
|
|
|
9161
9462
|
* - Constrains two points on two objects to remain at a fixed distance
|
|
9162
9463
|
* - You can view this as a massless, rigid rod
|
|
9163
9464
|
* @extends Box2dJoint
|
|
9465
|
+
* @memberof Box2D
|
|
9164
9466
|
*/
|
|
9165
9467
|
class Box2dDistanceJoint extends Box2dJoint
|
|
9166
9468
|
{
|
|
@@ -9224,6 +9526,7 @@ class Box2dDistanceJoint extends Box2dJoint
|
|
|
9224
9526
|
* Box2D Pin Joint
|
|
9225
9527
|
* - Pins two objects together at a point
|
|
9226
9528
|
* @extends Box2dDistanceJoint
|
|
9529
|
+
* @memberof Box2D
|
|
9227
9530
|
*/
|
|
9228
9531
|
class Box2dPinJoint extends Box2dDistanceJoint
|
|
9229
9532
|
{
|
|
@@ -9243,6 +9546,7 @@ class Box2dPinJoint extends Box2dDistanceJoint
|
|
|
9243
9546
|
* Box2D Rope Joint
|
|
9244
9547
|
* - Enforces a maximum distance between two points on two objects
|
|
9245
9548
|
* @extends Box2dJoint
|
|
9549
|
+
* @memberof Box2D
|
|
9246
9550
|
*/
|
|
9247
9551
|
class Box2dRopeJoint extends Box2dJoint
|
|
9248
9552
|
{
|
|
@@ -9295,6 +9599,7 @@ class Box2dRopeJoint extends Box2dJoint
|
|
|
9295
9599
|
* - You can use a motor to drive the relative rotation about the shared point
|
|
9296
9600
|
* - A maximum motor torque is provided so that infinite forces are not generated
|
|
9297
9601
|
* @extends Box2dJoint
|
|
9602
|
+
* @memberof Box2D
|
|
9298
9603
|
*/
|
|
9299
9604
|
class Box2dRevoluteJoint extends Box2dJoint
|
|
9300
9605
|
{
|
|
@@ -9396,6 +9701,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
9396
9701
|
* - Either joint can be a revolute or prismatic joint
|
|
9397
9702
|
* - You specify a gear ratio to bind the motions together
|
|
9398
9703
|
* @extends Box2dJoint
|
|
9704
|
+
* @memberof Box2D
|
|
9399
9705
|
*/
|
|
9400
9706
|
class Box2dGearJoint extends Box2dJoint
|
|
9401
9707
|
{
|
|
@@ -9444,6 +9750,7 @@ class Box2dGearJoint extends Box2dJoint
|
|
|
9444
9750
|
* - You can use a joint limit to restrict the range of motion
|
|
9445
9751
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
9446
9752
|
* @extends Box2dJoint
|
|
9753
|
+
* @memberof Box2D
|
|
9447
9754
|
*/
|
|
9448
9755
|
class Box2dPrismaticJoint extends Box2dJoint
|
|
9449
9756
|
{
|
|
@@ -9553,6 +9860,7 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
9553
9860
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
9554
9861
|
* - This joint is designed for vehicle suspensions
|
|
9555
9862
|
* @extends Box2dJoint
|
|
9863
|
+
* @memberof Box2D
|
|
9556
9864
|
*/
|
|
9557
9865
|
class Box2dWheelJoint extends Box2dJoint
|
|
9558
9866
|
{
|
|
@@ -9648,6 +9956,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
9648
9956
|
* Box2D Weld Joint
|
|
9649
9957
|
* - Glues two objects together
|
|
9650
9958
|
* @extends Box2dJoint
|
|
9959
|
+
* @memberof Box2D
|
|
9651
9960
|
*/
|
|
9652
9961
|
class Box2dWeldJoint extends Box2dJoint
|
|
9653
9962
|
{
|
|
@@ -9706,6 +10015,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
9706
10015
|
* - Used to apply top-down friction
|
|
9707
10016
|
* - Provides 2D translational friction and angular friction
|
|
9708
10017
|
* @extends Box2dJoint
|
|
10018
|
+
* @memberof Box2D
|
|
9709
10019
|
*/
|
|
9710
10020
|
class Box2dFrictionJoint extends Box2dJoint
|
|
9711
10021
|
{
|
|
@@ -9760,6 +10070,7 @@ class Box2dFrictionJoint extends Box2dJoint
|
|
|
9760
10070
|
* - The pulley supports a ratio such that: length1 + ratio * length2 <= constant
|
|
9761
10071
|
* - The force transmitted is scaled by the ratio
|
|
9762
10072
|
* @extends Box2dJoint
|
|
10073
|
+
* @memberof Box2D
|
|
9763
10074
|
*/
|
|
9764
10075
|
class Box2dPulleyJoint extends Box2dJoint
|
|
9765
10076
|
{
|
|
@@ -9827,6 +10138,7 @@ class Box2dPulleyJoint extends Box2dJoint
|
|
|
9827
10138
|
* - Controls the relative motion between two objects
|
|
9828
10139
|
* - Typical usage is to control the movement of a object with respect to the ground
|
|
9829
10140
|
* @extends Box2dJoint
|
|
10141
|
+
* @memberof Box2D
|
|
9830
10142
|
*/
|
|
9831
10143
|
class Box2dMotorJoint extends Box2dJoint
|
|
9832
10144
|
{
|
|
@@ -9890,6 +10202,7 @@ class Box2dMotorJoint extends Box2dJoint
|
|
|
9890
10202
|
/**
|
|
9891
10203
|
* Box2D Global Object
|
|
9892
10204
|
* - Wraps Box2d world and provides global functions
|
|
10205
|
+
* @memberof Box2D
|
|
9893
10206
|
*/
|
|
9894
10207
|
class Box2dPlugin
|
|
9895
10208
|
{
|
|
@@ -10224,7 +10537,7 @@ async function box2dInit()
|
|
|
10224
10537
|
}
|
|
10225
10538
|
function box2dRender()
|
|
10226
10539
|
{
|
|
10227
|
-
if (box2dDebug || debugPhysics
|
|
10540
|
+
if (box2dDebug || debugPhysics)
|
|
10228
10541
|
box2d.world.DrawDebugData();
|
|
10229
10542
|
}
|
|
10230
10543
|
|
|
@@ -10461,6 +10774,7 @@ export
|
|
|
10461
10774
|
|
|
10462
10775
|
// Debug
|
|
10463
10776
|
ASSERT,
|
|
10777
|
+
LOG,
|
|
10464
10778
|
debugRect,
|
|
10465
10779
|
debugPoly,
|
|
10466
10780
|
debugCircle,
|
|
@@ -10607,6 +10921,7 @@ export
|
|
|
10607
10921
|
isColor,
|
|
10608
10922
|
isVector2,
|
|
10609
10923
|
isNumber,
|
|
10924
|
+
isString,
|
|
10610
10925
|
|
|
10611
10926
|
// Default Colors
|
|
10612
10927
|
WHITE,
|
|
@@ -10742,7 +11057,7 @@ export
|
|
|
10742
11057
|
tileCollisionGetData,
|
|
10743
11058
|
tileCollisionTest,
|
|
10744
11059
|
tileCollisionRaycast,
|
|
10745
|
-
|
|
11060
|
+
tileLayersLoad,
|
|
10746
11061
|
TileLayerData,
|
|
10747
11062
|
CanvasLayer,
|
|
10748
11063
|
TileLayer,
|