littlejsengine 1.14.11 → 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 +252 -109
- package/dist/littlejs.esm.js +559 -252
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +557 -251
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +482 -206
- 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 +10 -7
- package/examples/module/game.js +3 -3
- package/examples/platformer/gameEffects.js +4 -4
- package/examples/platformer/gameLevel.js +1 -1
- package/examples/platformer/gameObjects.js +4 -4
- 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 +11 -8
- package/examples/shorts/medals.js +4 -4
- package/examples/shorts/music.js +29 -56
- 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 +15 -15
- package/examples/shorts/piano.js +15 -21
- package/examples/shorts/pongGame.js +6 -4
- package/examples/shorts/raycasting.js +9 -4
- package/examples/shorts/sequencer.js +122 -0
- package/examples/shorts/shapes.js +7 -4
- package/examples/shorts/slidingPuzzle.js +4 -2
- 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 +3 -3
- package/package.json +4 -2
- package/plugins/box2d.js +17 -2
- package/plugins/newgrounds.js +7 -5
- package/plugins/postProcess.js +5 -2
- package/plugins/uiSystem.js +120 -34
- package/plugins/zzfxm.js +5 -1
- package/reference.md +1 -1
- package/src/engine.js +35 -12
- package/src/engineAudio.js +31 -14
- package/src/engineDebug.js +75 -45
- package/src/engineDraw.js +60 -31
- package/src/engineExport.js +2 -1
- package/src/engineMedals.js +10 -2
- package/src/engineObject.js +14 -10
- package/src/engineParticles.js +59 -46
- package/src/engineSettings.js +7 -7
- package/src/engineTileLayer.js +45 -20
- package/src/engineUtilities.js +67 -20
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,12 +116,22 @@ 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
|
|
@@ -368,6 +383,8 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
368
383
|
const promises = imageSources.map((src, textureIndex)=>
|
|
369
384
|
new Promise(resolve =>
|
|
370
385
|
{
|
|
386
|
+
ASSERT(isString(src), 'imageSources must be an array of strings');
|
|
387
|
+
|
|
371
388
|
const image = new Image;
|
|
372
389
|
image.onerror = image.onload = ()=>
|
|
373
390
|
{
|
|
@@ -399,7 +416,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
399
416
|
promises.push(new Promise(resolve =>
|
|
400
417
|
{
|
|
401
418
|
let t = 0;
|
|
402
|
-
|
|
419
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
403
420
|
updateSplash();
|
|
404
421
|
function updateSplash()
|
|
405
422
|
{
|
|
@@ -483,10 +500,16 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
483
500
|
return collectedObjects;
|
|
484
501
|
}
|
|
485
502
|
|
|
503
|
+
/**
|
|
504
|
+
* @callback ObjectCallbackFunction - Function that processes an object
|
|
505
|
+
* @param {EngineObject} uiObjects
|
|
506
|
+
* @memberof Engine
|
|
507
|
+
*/
|
|
508
|
+
|
|
486
509
|
/** Triggers a callback for each object within a given area
|
|
487
|
-
* @param {Vector2} [pos]
|
|
488
|
-
* @param {Vector2|number} [size]
|
|
489
|
-
* @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
|
|
490
513
|
* @param {Array<EngineObject>} [objects=engineObjects] - List of objects to check
|
|
491
514
|
* @memberof Engine */
|
|
492
515
|
function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
|
|
@@ -727,10 +750,16 @@ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParti
|
|
|
727
750
|
// Debug helper functions
|
|
728
751
|
|
|
729
752
|
/** Asserts if the expression is false, does nothing in release builds
|
|
753
|
+
* Halts execution if the assert fails and throws an error
|
|
730
754
|
* @param {boolean} assert
|
|
731
755
|
* @param {...Object} [output] - error message output
|
|
732
756
|
* @memberof Debug */
|
|
733
|
-
function ASSERT(assert, ...output)
|
|
757
|
+
function ASSERT(assert, ...output)
|
|
758
|
+
{
|
|
759
|
+
if (assert) return;
|
|
760
|
+
console.assert(assert, ...output)
|
|
761
|
+
throw new Error('Assert failed!'); // halt execution
|
|
762
|
+
}
|
|
734
763
|
|
|
735
764
|
/** Log to console if debug is enabled, does nothing in release builds
|
|
736
765
|
* @param {...Object} [output] - message output
|
|
@@ -740,64 +769,72 @@ function LOG(...output) { console.log(...output); }
|
|
|
740
769
|
/** Draw a debug rectangle in world space
|
|
741
770
|
* @param {Vector2} pos
|
|
742
771
|
* @param {Vector2} [size=Vector2()]
|
|
743
|
-
* @param {string}
|
|
744
|
-
* @param {number}
|
|
745
|
-
* @param {number}
|
|
772
|
+
* @param {Color|string} [color]
|
|
773
|
+
* @param {number} [time]
|
|
774
|
+
* @param {number} [angle]
|
|
746
775
|
* @param {boolean} [fill]
|
|
747
776
|
* @memberof Debug */
|
|
748
|
-
function debugRect(pos, size=vec2(), color=
|
|
777
|
+
function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
|
|
749
778
|
{
|
|
750
779
|
if (typeof size === 'number')
|
|
751
780
|
size = vec2(size); // allow passing in floats
|
|
752
|
-
|
|
753
|
-
|
|
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});
|
|
754
787
|
}
|
|
755
788
|
|
|
756
789
|
/** Draw a debug poly in world space
|
|
757
790
|
* @param {Vector2} pos
|
|
758
791
|
* @param {Array<Vector2>} points
|
|
759
|
-
* @param {string}
|
|
760
|
-
* @param {number}
|
|
761
|
-
* @param {number}
|
|
792
|
+
* @param {Color|string} [color]
|
|
793
|
+
* @param {number} [time]
|
|
794
|
+
* @param {number} [angle]
|
|
762
795
|
* @param {boolean} [fill]
|
|
763
796
|
* @memberof Debug */
|
|
764
|
-
function debugPoly(pos, points, color=
|
|
797
|
+
function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
|
|
765
798
|
{
|
|
766
|
-
|
|
767
|
-
|
|
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});
|
|
768
805
|
}
|
|
769
806
|
|
|
770
807
|
/** Draw a debug circle in world space
|
|
771
808
|
* @param {Vector2} pos
|
|
772
|
-
* @param {number}
|
|
773
|
-
* @param {string}
|
|
774
|
-
* @param {number}
|
|
809
|
+
* @param {number} [size] - diameter
|
|
810
|
+
* @param {Color|string} [color]
|
|
811
|
+
* @param {number} [time]
|
|
775
812
|
* @param {boolean} [fill]
|
|
776
813
|
* @memberof Debug */
|
|
777
|
-
function debugCircle(pos, size=0, color=
|
|
814
|
+
function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
|
|
778
815
|
{
|
|
779
|
-
|
|
780
|
-
|
|
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});
|
|
781
821
|
}
|
|
782
822
|
|
|
783
823
|
/** Draw a debug point in world space
|
|
784
824
|
* @param {Vector2} pos
|
|
785
|
-
* @param {string}
|
|
786
|
-
* @param {number}
|
|
787
|
-
* @param {number}
|
|
825
|
+
* @param {Color|string} [color]
|
|
826
|
+
* @param {number} [time]
|
|
827
|
+
* @param {number} [angle]
|
|
788
828
|
* @memberof Debug */
|
|
789
829
|
function debugPoint(pos, color, time, angle)
|
|
790
|
-
{
|
|
791
|
-
ASSERT(typeof color === 'string', 'pass in css color strings');
|
|
792
|
-
debugRect(pos, undefined, color, time, angle);
|
|
793
|
-
}
|
|
830
|
+
{ debugRect(pos, undefined, color, time, angle); }
|
|
794
831
|
|
|
795
832
|
/** Draw a debug line in world space
|
|
796
833
|
* @param {Vector2} posA
|
|
797
834
|
* @param {Vector2} posB
|
|
798
|
-
* @param {string}
|
|
799
|
-
* @param {number}
|
|
800
|
-
* @param {number}
|
|
835
|
+
* @param {Color|string} [color]
|
|
836
|
+
* @param {number} [width]
|
|
837
|
+
* @param {number} [time]
|
|
801
838
|
* @memberof Debug */
|
|
802
839
|
function debugLine(posA, posB, color, width=.1, time)
|
|
803
840
|
{
|
|
@@ -811,7 +848,7 @@ function debugLine(posA, posB, color, width=.1, time)
|
|
|
811
848
|
* @param {Vector2} sizeA
|
|
812
849
|
* @param {Vector2} posB
|
|
813
850
|
* @param {Vector2} sizeB
|
|
814
|
-
* @param {string}
|
|
851
|
+
* @param {Color|string} [color]
|
|
815
852
|
* @memberof Debug */
|
|
816
853
|
function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
817
854
|
{
|
|
@@ -827,18 +864,21 @@ function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
|
827
864
|
}
|
|
828
865
|
|
|
829
866
|
/** Draw a debug axis aligned bounding box in world space
|
|
830
|
-
* @param {string}
|
|
867
|
+
* @param {string} text
|
|
831
868
|
* @param {Vector2} pos
|
|
832
|
-
* @param {number}
|
|
833
|
-
* @param {string}
|
|
834
|
-
* @param {number}
|
|
835
|
-
* @param {number}
|
|
836
|
-
* @param {string}
|
|
869
|
+
* @param {number} [size]
|
|
870
|
+
* @param {Color|string} [color]
|
|
871
|
+
* @param {number} [time]
|
|
872
|
+
* @param {number} [angle]
|
|
873
|
+
* @param {string} [font]
|
|
837
874
|
* @memberof Debug */
|
|
838
|
-
function debugText(text, pos, size=1, color=
|
|
875
|
+
function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monospace')
|
|
839
876
|
{
|
|
840
|
-
|
|
841
|
-
|
|
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});
|
|
842
882
|
}
|
|
843
883
|
|
|
844
884
|
/** Clear all debug primitives in the list
|
|
@@ -889,17 +929,30 @@ function debugSaveDataURL(dataURL, filename)
|
|
|
889
929
|
downloadLink.click();
|
|
890
930
|
}
|
|
891
931
|
|
|
892
|
-
/**
|
|
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
|
|
893
935
|
* @memberof Debug */
|
|
894
936
|
function debugShowErrors()
|
|
895
937
|
{
|
|
896
938
|
const showError = (message)=>
|
|
897
939
|
{
|
|
898
940
|
// replace entire page with error message
|
|
899
|
-
document.body.style
|
|
900
|
-
document.body.
|
|
901
|
-
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;
|
|
902
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
|
+
};
|
|
903
956
|
onunhandledrejection = (event)=>
|
|
904
957
|
showError(event.reason.stack || event.reason);
|
|
905
958
|
onerror = (message, source, lineno, colno)=>
|
|
@@ -1067,7 +1120,7 @@ function debugRender()
|
|
|
1067
1120
|
p.fill && overlayContext.fill();
|
|
1068
1121
|
overlayContext.stroke();
|
|
1069
1122
|
}
|
|
1070
|
-
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))
|
|
1071
1124
|
{
|
|
1072
1125
|
// point
|
|
1073
1126
|
overlayContext.fillRect(-pointSize/2, -1, pointSize, 3);
|
|
@@ -1094,7 +1147,7 @@ function debugRender()
|
|
|
1094
1147
|
});
|
|
1095
1148
|
|
|
1096
1149
|
// remove expired primitives
|
|
1097
|
-
debugPrimitives = debugPrimitives.filter(r=>r.
|
|
1150
|
+
debugPrimitives = debugPrimitives.filter(r=>r.timer<0);
|
|
1098
1151
|
}
|
|
1099
1152
|
|
|
1100
1153
|
if (debugObject)
|
|
@@ -1504,6 +1557,8 @@ function formatTime(t)
|
|
|
1504
1557
|
async function fetchJSON(url)
|
|
1505
1558
|
{
|
|
1506
1559
|
const response = await fetch(url);
|
|
1560
|
+
if (!response.ok)
|
|
1561
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
1507
1562
|
return response.json();
|
|
1508
1563
|
}
|
|
1509
1564
|
|
|
@@ -1514,6 +1569,13 @@ async function fetchJSON(url)
|
|
|
1514
1569
|
* @memberof Utilities */
|
|
1515
1570
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
1516
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
|
+
|
|
1517
1579
|
///////////////////////////////////////////////////////////////////////////////
|
|
1518
1580
|
|
|
1519
1581
|
/** Random global functions
|
|
@@ -1576,6 +1638,7 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
1576
1638
|
/**
|
|
1577
1639
|
* Seeded random number generator
|
|
1578
1640
|
* - Can be used to create a deterministic random number sequence
|
|
1641
|
+
* @memberof Engine
|
|
1579
1642
|
* @example
|
|
1580
1643
|
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
1581
1644
|
* let a = r.float(); // random value between 0 and 1
|
|
@@ -1672,6 +1735,7 @@ function ASSERT_VECTOR2_NORMAL(v)
|
|
|
1672
1735
|
/**
|
|
1673
1736
|
* 2D Vector object with vector math library
|
|
1674
1737
|
* - Functions do not change this so they can be chained together
|
|
1738
|
+
* @memberof Engine
|
|
1675
1739
|
* @example
|
|
1676
1740
|
* let a = new Vector2(2, 3); // vector with coordinates (2, 3)
|
|
1677
1741
|
* let b = new Vector2; // vector with coordinates (0, 0)
|
|
@@ -1812,9 +1876,10 @@ class Vector2
|
|
|
1812
1876
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
1813
1877
|
}
|
|
1814
1878
|
|
|
1815
|
-
/**
|
|
1879
|
+
/** Sets this this vector to point in the specified integer direction (0-3), corresponding to multiples of 90 degree rotation
|
|
1816
1880
|
* @param {number} [direction]
|
|
1817
|
-
* @param {number} [length]
|
|
1881
|
+
* @param {number} [length]
|
|
1882
|
+
* @return {Vector2} */
|
|
1818
1883
|
setDirection(direction, length=1)
|
|
1819
1884
|
{
|
|
1820
1885
|
ASSERT_NUMBER_VALID(direction);
|
|
@@ -1822,8 +1887,10 @@ class Vector2
|
|
|
1822
1887
|
direction = mod(direction, 4);
|
|
1823
1888
|
ASSERT(direction===0 || direction===1 || direction===2 || direction===3,
|
|
1824
1889
|
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
1825
|
-
|
|
1826
|
-
|
|
1890
|
+
|
|
1891
|
+
this.x = direction%2 ? direction-1 ? -length : length : 0;
|
|
1892
|
+
this.y = direction%2 ? 0 : direction ? -length : length;
|
|
1893
|
+
return this;
|
|
1827
1894
|
}
|
|
1828
1895
|
|
|
1829
1896
|
/** Returns the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
@@ -1919,6 +1986,7 @@ function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
|
|
|
1919
1986
|
|
|
1920
1987
|
/**
|
|
1921
1988
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
1989
|
+
* @memberof Engine
|
|
1922
1990
|
* @example
|
|
1923
1991
|
* let a = new Color; // white
|
|
1924
1992
|
* let b = new Color(1, 0, 0); // red
|
|
@@ -2096,7 +2164,8 @@ class Color
|
|
|
2096
2164
|
* @return {Color} */
|
|
2097
2165
|
setHex(hex)
|
|
2098
2166
|
{
|
|
2099
|
-
ASSERT(
|
|
2167
|
+
ASSERT(isString(hex), 'Color hex code must be a string');
|
|
2168
|
+
ASSERT(hex[0] === '#', 'Color hex code must start with #');
|
|
2100
2169
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
2101
2170
|
|
|
2102
2171
|
if (hex.length < 6)
|
|
@@ -2138,77 +2207,78 @@ class Color
|
|
|
2138
2207
|
}
|
|
2139
2208
|
|
|
2140
2209
|
///////////////////////////////////////////////////////////////////////////////
|
|
2141
|
-
//
|
|
2210
|
+
// Default Colors
|
|
2142
2211
|
|
|
2143
2212
|
/** Color - White #ffffff
|
|
2144
2213
|
* @type {Color}
|
|
2145
2214
|
* @memberof Utilities */
|
|
2146
|
-
const WHITE = rgb();
|
|
2215
|
+
const WHITE = protectEngineConstant(rgb());
|
|
2147
2216
|
|
|
2148
|
-
/** Color - Clear White #
|
|
2217
|
+
/** Color - Clear White #757474ff with 0 alpha
|
|
2149
2218
|
* @type {Color}
|
|
2150
2219
|
* @memberof Utilities */
|
|
2151
|
-
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
2220
|
+
const CLEAR_WHITE = protectEngineConstant(rgb(1,1,1,0));
|
|
2152
2221
|
|
|
2153
2222
|
/** Color - Black #000000
|
|
2154
2223
|
* @type {Color}
|
|
2155
2224
|
* @memberof Utilities */
|
|
2156
|
-
const BLACK = rgb(0,0,0);
|
|
2225
|
+
const BLACK = protectEngineConstant(rgb(0,0,0));
|
|
2157
2226
|
|
|
2158
2227
|
/** Color - Clear Black #000000 with 0 alpha
|
|
2159
2228
|
* @type {Color}
|
|
2160
2229
|
* @memberof Utilities */
|
|
2161
|
-
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
2230
|
+
const CLEAR_BLACK = protectEngineConstant(rgb(0,0,0,0));
|
|
2162
2231
|
|
|
2163
2232
|
/** Color - Gray #808080
|
|
2164
2233
|
* @type {Color}
|
|
2165
2234
|
* @memberof Utilities */
|
|
2166
|
-
const GRAY = rgb(.5,.5,.5);
|
|
2235
|
+
const GRAY = protectEngineConstant(rgb(.5,.5,.5));
|
|
2167
2236
|
|
|
2168
2237
|
/** Color - Red #ff0000
|
|
2169
2238
|
* @type {Color}
|
|
2170
2239
|
* @memberof Utilities */
|
|
2171
|
-
const RED = rgb(1,0,0);
|
|
2240
|
+
const RED = protectEngineConstant(rgb(1,0,0));
|
|
2172
2241
|
|
|
2173
2242
|
/** Color - Orange #ff8000
|
|
2174
2243
|
* @type {Color}
|
|
2175
2244
|
* @memberof Utilities */
|
|
2176
|
-
const ORANGE = rgb(1,.5,0);
|
|
2245
|
+
const ORANGE = protectEngineConstant(rgb(1,.5,0));
|
|
2177
2246
|
|
|
2178
2247
|
/** Color - Yellow #ffff00
|
|
2179
2248
|
* @type {Color}
|
|
2180
2249
|
* @memberof Utilities */
|
|
2181
|
-
const YELLOW = rgb(1,1,0);
|
|
2250
|
+
const YELLOW = protectEngineConstant(rgb(1,1,0));
|
|
2182
2251
|
|
|
2183
2252
|
/** Color - Green #00ff00
|
|
2184
2253
|
* @type {Color}
|
|
2185
2254
|
* @memberof Utilities */
|
|
2186
|
-
const GREEN = rgb(0,1,0);
|
|
2255
|
+
const GREEN = protectEngineConstant(rgb(0,1,0));
|
|
2187
2256
|
|
|
2188
2257
|
/** Color - Cyan #00ffff
|
|
2189
2258
|
* @type {Color}
|
|
2190
2259
|
* @memberof Utilities */
|
|
2191
|
-
const CYAN = rgb(0,1,1);
|
|
2260
|
+
const CYAN = protectEngineConstant(rgb(0,1,1));
|
|
2192
2261
|
|
|
2193
2262
|
/** Color - Blue #0000ff
|
|
2194
2263
|
* @type {Color}
|
|
2195
2264
|
* @memberof Utilities */
|
|
2196
|
-
const BLUE = rgb(0,0,1);
|
|
2265
|
+
const BLUE = protectEngineConstant(rgb(0,0,1));
|
|
2197
2266
|
|
|
2198
2267
|
/** Color - Purple #8000ff
|
|
2199
2268
|
* @type {Color}
|
|
2200
2269
|
* @memberof Utilities */
|
|
2201
|
-
const PURPLE = rgb(.5,0,1);
|
|
2270
|
+
const PURPLE = protectEngineConstant(rgb(.5,0,1));
|
|
2202
2271
|
|
|
2203
2272
|
/** Color - Magenta #ff00ff
|
|
2204
2273
|
* @type {Color}
|
|
2205
2274
|
* @memberof Utilities */
|
|
2206
|
-
const MAGENTA = rgb(1,0,1);
|
|
2275
|
+
const MAGENTA = protectEngineConstant(rgb(1,0,1));
|
|
2207
2276
|
|
|
2208
2277
|
///////////////////////////////////////////////////////////////////////////////
|
|
2209
2278
|
|
|
2210
2279
|
/**
|
|
2211
2280
|
* Timer object tracks how long has passed since it was set
|
|
2281
|
+
* @memberof Engine
|
|
2212
2282
|
* @example
|
|
2213
2283
|
* let a = new Timer; // creates a timer that is not set
|
|
2214
2284
|
* a.set(3); // sets the timer to 3 seconds
|
|
@@ -2270,6 +2340,36 @@ class Timer
|
|
|
2270
2340
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
2271
2341
|
* @return {number} */
|
|
2272
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);
|
|
2273
2373
|
}
|
|
2274
2374
|
/**
|
|
2275
2375
|
* LittleJS Engine Settings
|
|
@@ -2577,7 +2677,7 @@ let medalsPreventUnlock = false;
|
|
|
2577
2677
|
/** Set position of camera in world space
|
|
2578
2678
|
* @param {Vector2} pos
|
|
2579
2679
|
* @memberof Settings */
|
|
2580
|
-
function setCameraPos(pos) { cameraPos = pos; }
|
|
2680
|
+
function setCameraPos(pos) { cameraPos = pos.copy(); }
|
|
2581
2681
|
|
|
2582
2682
|
/** Set angle of camera in world space
|
|
2583
2683
|
* @param {number} angle
|
|
@@ -2600,17 +2700,17 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
|
2600
2700
|
/** Set color to clear the canvas to before render
|
|
2601
2701
|
* @param {Color} color
|
|
2602
2702
|
* @memberof Settings */
|
|
2603
|
-
function setCanvasClearColor(color) { canvasClearColor = color; }
|
|
2703
|
+
function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
2604
2704
|
|
|
2605
2705
|
/** Set max size of the canvas
|
|
2606
2706
|
* @param {Vector2} size
|
|
2607
2707
|
* @memberof Settings */
|
|
2608
|
-
function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
2708
|
+
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2609
2709
|
|
|
2610
2710
|
/** Set fixed size of the canvas
|
|
2611
2711
|
* @param {Vector2} size
|
|
2612
2712
|
* @memberof Settings */
|
|
2613
|
-
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
2713
|
+
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
2614
2714
|
|
|
2615
2715
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2616
2716
|
* - If enabled sets css image-rendering:pixelated
|
|
@@ -2675,7 +2775,7 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
2675
2775
|
/** Set default size of tiles in pixels
|
|
2676
2776
|
* @param {Vector2} size
|
|
2677
2777
|
* @memberof Settings */
|
|
2678
|
-
function setTileSizeDefault(size) { tileSizeDefault = size; }
|
|
2778
|
+
function setTileSizeDefault(size) { tileSizeDefault = size.copy(); }
|
|
2679
2779
|
|
|
2680
2780
|
/** Set to prevent tile bleeding from neighbors in pixels
|
|
2681
2781
|
* @param {number} scale
|
|
@@ -2720,7 +2820,7 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
|
2720
2820
|
/** Set how much gravity to apply to objects
|
|
2721
2821
|
* @param {Vector2} newGravity
|
|
2722
2822
|
* @memberof Settings */
|
|
2723
|
-
function setGravity(newGravity) { gravity = newGravity; }
|
|
2823
|
+
function setGravity(newGravity) { gravity = newGravity.copy(); }
|
|
2724
2824
|
|
|
2725
2825
|
/** Set to scales emit rate of particles
|
|
2726
2826
|
* @param {number} scale
|
|
@@ -2810,7 +2910,7 @@ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
|
|
|
2810
2910
|
/** Set size of medal display
|
|
2811
2911
|
* @param {Vector2} size
|
|
2812
2912
|
* @memberof Settings */
|
|
2813
|
-
function setMedalDisplaySize(size) { medalDisplaySize = size; }
|
|
2913
|
+
function setMedalDisplaySize(size) { medalDisplaySize = size.copy(); }
|
|
2814
2914
|
|
|
2815
2915
|
/** Set to stop medals from being unlockable
|
|
2816
2916
|
* @param {boolean} preventUnlock
|
|
@@ -2850,6 +2950,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2850
2950
|
* - Collision for objects can be set to be solid to block other objects
|
|
2851
2951
|
* - Objects may get pushed into overlapping other solid objects, if so they will push away
|
|
2852
2952
|
* - Solid objects are more performance intensive and should be used sparingly
|
|
2953
|
+
* @memberof Engine
|
|
2853
2954
|
* @example
|
|
2854
2955
|
* // create an engine object, normally you would first extend the class with your own
|
|
2855
2956
|
* const pos = vec2(2,3);
|
|
@@ -2858,18 +2959,18 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2858
2959
|
class EngineObject
|
|
2859
2960
|
{
|
|
2860
2961
|
/** Create an engine object and adds it to the list of objects
|
|
2861
|
-
* @param {Vector2} [pos=(0,0)]
|
|
2862
|
-
* @param {Vector2} [size=(1,1)]
|
|
2863
|
-
* @param {TileInfo} [tileInfo]
|
|
2864
|
-
* @param {number} [angle]
|
|
2865
|
-
* @param {Color} [color=
|
|
2866
|
-
* @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
|
|
2867
2968
|
*/
|
|
2868
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=
|
|
2969
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=WHITE, renderOrder=0)
|
|
2869
2970
|
{
|
|
2870
2971
|
// check passed in params
|
|
2871
|
-
ASSERT(isVector2(pos), 'object pos
|
|
2872
|
-
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');
|
|
2873
2974
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
2874
2975
|
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
2875
2976
|
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
@@ -3154,7 +3255,7 @@ class EngineObject
|
|
|
3154
3255
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
3155
3256
|
}
|
|
3156
3257
|
|
|
3157
|
-
/** Destroy this object, destroy its children, detach
|
|
3258
|
+
/** Destroy this object, destroy its children, detach its parent, and mark it for removal */
|
|
3158
3259
|
destroy()
|
|
3159
3260
|
{
|
|
3160
3261
|
if (this.destroyed)
|
|
@@ -3228,6 +3329,8 @@ class EngineObject
|
|
|
3228
3329
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
3229
3330
|
{
|
|
3230
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');
|
|
3231
3334
|
this.children.push(child);
|
|
3232
3335
|
child.parent = this;
|
|
3233
3336
|
child.localPos = localPos.copy();
|
|
@@ -3239,6 +3342,7 @@ class EngineObject
|
|
|
3239
3342
|
removeChild(child)
|
|
3240
3343
|
{
|
|
3241
3344
|
ASSERT(child.parent === this && this.children.includes(child));
|
|
3345
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
3242
3346
|
this.children.splice(this.children.indexOf(child), 1);
|
|
3243
3347
|
child.parent = 0;
|
|
3244
3348
|
}
|
|
@@ -3377,7 +3481,7 @@ let drawCount;
|
|
|
3377
3481
|
* Create a tile info object using a grid based system
|
|
3378
3482
|
* - This can take vecs or floats for easier use and conversion
|
|
3379
3483
|
* - If an index is passed in, the tile size and index will determine the position
|
|
3380
|
-
* @param {Vector2|number} [pos=0] -
|
|
3484
|
+
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
3381
3485
|
* @param {Vector2|number} [size=tileSizeDefault] - Size of tile in pixels
|
|
3382
3486
|
* @param {number} [textureIndex] - Texture index to use
|
|
3383
3487
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
@@ -3422,6 +3526,7 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
3422
3526
|
|
|
3423
3527
|
/**
|
|
3424
3528
|
* Tile Info - Stores info about how to draw a tile
|
|
3529
|
+
* @memberof Draw
|
|
3425
3530
|
*/
|
|
3426
3531
|
class TileInfo
|
|
3427
3532
|
{
|
|
@@ -3482,7 +3587,10 @@ class TileInfo
|
|
|
3482
3587
|
}
|
|
3483
3588
|
}
|
|
3484
3589
|
|
|
3485
|
-
/**
|
|
3590
|
+
/**
|
|
3591
|
+
* Tile Info - Stores info about each texture
|
|
3592
|
+
* @memberof Draw
|
|
3593
|
+
*/
|
|
3486
3594
|
class TextureInfo
|
|
3487
3595
|
{
|
|
3488
3596
|
/**
|
|
@@ -3528,10 +3636,11 @@ class TextureInfo
|
|
|
3528
3636
|
function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
3529
3637
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
3530
3638
|
{
|
|
3531
|
-
ASSERT(isVector2(pos), '
|
|
3532
|
-
ASSERT(isVector2(size), '
|
|
3533
|
-
ASSERT(isColor(color)
|
|
3534
|
-
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');
|
|
3535
3644
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3536
3645
|
|
|
3537
3646
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
@@ -3626,10 +3735,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3626
3735
|
* @memberof Draw */
|
|
3627
3736
|
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3628
3737
|
{
|
|
3629
|
-
ASSERT(isVector2(pos), '
|
|
3630
|
-
ASSERT(isVector2(size), '
|
|
3631
|
-
ASSERT(isColor(colorTop) && isColor(colorBottom), '
|
|
3632
|
-
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');
|
|
3633
3742
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3634
3743
|
if (useWebGL)
|
|
3635
3744
|
{
|
|
@@ -3687,11 +3796,11 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3687
3796
|
* @memberof Draw */
|
|
3688
3797
|
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
3689
3798
|
{
|
|
3690
|
-
ASSERT(Array.isArray(points), '
|
|
3691
|
-
ASSERT(isNumber(width), '
|
|
3692
|
-
ASSERT(isColor(color), '
|
|
3693
|
-
ASSERT(isVector2(pos), '
|
|
3694
|
-
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');
|
|
3695
3804
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3696
3805
|
if (useWebGL)
|
|
3697
3806
|
{
|
|
@@ -3762,8 +3871,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
3762
3871
|
* @memberof Draw */
|
|
3763
3872
|
function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3764
3873
|
{
|
|
3765
|
-
ASSERT(isVector2(size), '
|
|
3766
|
-
ASSERT(isNumber(sides), '
|
|
3874
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3875
|
+
ASSERT(isNumber(sides), 'sides must be a number');
|
|
3767
3876
|
|
|
3768
3877
|
// build regular polygon points
|
|
3769
3878
|
const points = [];
|
|
@@ -3789,12 +3898,13 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3789
3898
|
* @memberof Draw */
|
|
3790
3899
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
3791
3900
|
{
|
|
3792
|
-
ASSERT(isVector2(pos), '
|
|
3793
|
-
ASSERT(Array.isArray(points), '
|
|
3794
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3795
|
-
ASSERT(isNumber(lineWidth), '
|
|
3796
|
-
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');
|
|
3797
3906
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3907
|
+
|
|
3798
3908
|
if (useWebGL)
|
|
3799
3909
|
{
|
|
3800
3910
|
let scale = 1;
|
|
@@ -3841,13 +3951,14 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3841
3951
|
* @memberof Draw */
|
|
3842
3952
|
function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3843
3953
|
{
|
|
3844
|
-
ASSERT(isVector2(pos), '
|
|
3845
|
-
ASSERT(isVector2(size), '
|
|
3846
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3847
|
-
ASSERT(isNumber(angle), '
|
|
3848
|
-
ASSERT(isNumber(lineWidth), '
|
|
3849
|
-
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');
|
|
3850
3960
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3961
|
+
|
|
3851
3962
|
if (useWebGL)
|
|
3852
3963
|
{
|
|
3853
3964
|
// draw as a regular polygon
|
|
@@ -3884,21 +3995,32 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3884
3995
|
* @memberof Draw */
|
|
3885
3996
|
function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3886
3997
|
{
|
|
3887
|
-
ASSERT(isNumber(size), '
|
|
3998
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3888
3999
|
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
3889
4000
|
}
|
|
3890
4001
|
|
|
4002
|
+
/**
|
|
4003
|
+
* @callback Canvas2DDrawFunction - A function that draws to a 2D canvas context
|
|
4004
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
4005
|
+
* @memberof Draw
|
|
4006
|
+
*/
|
|
4007
|
+
|
|
3891
4008
|
/** Draw directly to a 2d canvas context in world space
|
|
3892
4009
|
* @param {Vector2} pos
|
|
3893
4010
|
* @param {Vector2} size
|
|
3894
4011
|
* @param {number} angle
|
|
3895
4012
|
* @param {boolean} [mirror]
|
|
3896
|
-
* @param {
|
|
4013
|
+
* @param {Canvas2DDrawFunction} [drawFunction]
|
|
3897
4014
|
* @param {boolean} [screenSpace=false]
|
|
3898
4015
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3899
4016
|
* @memberof Draw */
|
|
3900
4017
|
function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpace=false, context=drawContext)
|
|
3901
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
|
+
|
|
3902
4024
|
if (!screenSpace)
|
|
3903
4025
|
{
|
|
3904
4026
|
// transform from world space to screen space
|
|
@@ -3966,6 +4088,16 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3966
4088
|
* @memberof Draw */
|
|
3967
4089
|
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
3968
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
|
+
|
|
3969
4101
|
context.fillStyle = color.toString();
|
|
3970
4102
|
context.strokeStyle = lineColor.toString();
|
|
3971
4103
|
context.lineWidth = lineWidth;
|
|
@@ -4167,6 +4299,7 @@ let engineFontImage;
|
|
|
4167
4299
|
* - 96 characters (from space to tilde) are stored in an image
|
|
4168
4300
|
* - Uses a default 8x8 font if none is supplied
|
|
4169
4301
|
* - You can also use fonts from the main tile sheet
|
|
4302
|
+
* @memberof Draw
|
|
4170
4303
|
* @example
|
|
4171
4304
|
* // use built in font
|
|
4172
4305
|
* const font = new FontImage;
|
|
@@ -4223,7 +4356,7 @@ class FontImage
|
|
|
4223
4356
|
* @param {boolean} [center]
|
|
4224
4357
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4225
4358
|
*/
|
|
4226
|
-
drawTextScreen(text, pos, scale=4, center, context=overlayContext)
|
|
4359
|
+
drawTextScreen(text, pos, scale=4, center=true, context=overlayContext)
|
|
4227
4360
|
{
|
|
4228
4361
|
context.save();
|
|
4229
4362
|
const size = this.tileSize;
|
|
@@ -4917,6 +5050,7 @@ function audioInit()
|
|
|
4917
5050
|
* Sound Object - Stores a sound for later use and can be played positionally
|
|
4918
5051
|
*
|
|
4919
5052
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
5053
|
+
* @memberof Audio
|
|
4920
5054
|
* @example
|
|
4921
5055
|
* // create a sound
|
|
4922
5056
|
* const sound_example = new Sound([.5,.5]);
|
|
@@ -5011,12 +5145,12 @@ class Sound
|
|
|
5011
5145
|
|
|
5012
5146
|
/** Play the sound as a musical note with a semitone offset
|
|
5013
5147
|
* This can be used to play music with chromatic scales
|
|
5014
|
-
* @param {number} semitoneOffset - How many semitones to offset pitch
|
|
5148
|
+
* @param {number} [semitoneOffset=0] - How many semitones to offset pitch
|
|
5015
5149
|
* @param {Vector2} [pos] - World space position to play the sound if any
|
|
5016
5150
|
* @param {number} [volume=1] - How much to scale volume by
|
|
5017
5151
|
* @return {SoundInstance} - The audio source node
|
|
5018
5152
|
*/
|
|
5019
|
-
playNote(semitoneOffset, pos, volume)
|
|
5153
|
+
playNote(semitoneOffset=0, pos, volume)
|
|
5020
5154
|
{
|
|
5021
5155
|
const pitch = getNoteFrequency(semitoneOffset, 1);
|
|
5022
5156
|
return this.play(pos, volume, pitch, 0);
|
|
@@ -5039,6 +5173,8 @@ class Sound
|
|
|
5039
5173
|
/**
|
|
5040
5174
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
5041
5175
|
* - this can be used to play wave, mp3, and ogg files
|
|
5176
|
+
* @extends Sound
|
|
5177
|
+
* @memberof Audio
|
|
5042
5178
|
* @example
|
|
5043
5179
|
* // create a sound
|
|
5044
5180
|
* const sound_example = new SoundWave('sound.mp3');
|
|
@@ -5048,22 +5184,29 @@ class Sound
|
|
|
5048
5184
|
*/
|
|
5049
5185
|
class SoundWave extends Sound
|
|
5050
5186
|
{
|
|
5187
|
+
/**
|
|
5188
|
+
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
5189
|
+
* @param {SoundWave} sound
|
|
5190
|
+
* @memberof Audio
|
|
5191
|
+
*/
|
|
5192
|
+
|
|
5051
5193
|
/** Create a sound object and cache the wave file for later use
|
|
5052
5194
|
* @param {string} filename - Filename of audio file to load
|
|
5053
5195
|
* @param {number} [randomness] - How much to randomize frequency each time sound plays
|
|
5054
5196
|
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
5055
5197
|
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
5056
|
-
* @param {
|
|
5198
|
+
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
5057
5199
|
*/
|
|
5058
5200
|
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
5059
5201
|
{
|
|
5060
5202
|
super(undefined, range, taper);
|
|
5061
5203
|
if (!soundEnable || headlessMode) return;
|
|
5204
|
+
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
5062
5205
|
|
|
5063
|
-
/** @property {
|
|
5206
|
+
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
5064
5207
|
this.onloadCallback = onloadCallback;
|
|
5065
5208
|
this.randomness = randomness;
|
|
5066
|
-
this.loadSound(filename);
|
|
5209
|
+
filename && this.loadSound(filename);
|
|
5067
5210
|
}
|
|
5068
5211
|
|
|
5069
5212
|
/** Loads a sound from a URL and decodes it into sample data. Must be used with await!
|
|
@@ -5072,6 +5215,8 @@ class SoundWave extends Sound
|
|
|
5072
5215
|
async loadSound(filename)
|
|
5073
5216
|
{
|
|
5074
5217
|
const response = await fetch(filename);
|
|
5218
|
+
if (!response.ok)
|
|
5219
|
+
throw new Error(`Failed to load sound from ${filename}: ${response.status} ${response.statusText}`);
|
|
5075
5220
|
const arrayBuffer = await response.arrayBuffer();
|
|
5076
5221
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
5077
5222
|
|
|
@@ -5107,7 +5252,7 @@ class SoundWave extends Sound
|
|
|
5107
5252
|
this.sampleChannels = sampleChannels;
|
|
5108
5253
|
this.loadedPercent = 1;
|
|
5109
5254
|
if (this.onloadCallback)
|
|
5110
|
-
this.onloadCallback();
|
|
5255
|
+
this.onloadCallback(this);
|
|
5111
5256
|
}
|
|
5112
5257
|
}
|
|
5113
5258
|
|
|
@@ -5116,6 +5261,7 @@ class SoundWave extends Sound
|
|
|
5116
5261
|
/**
|
|
5117
5262
|
* Sound Instance - Wraps an AudioBufferSourceNode for individual sound control
|
|
5118
5263
|
* Represents a single playing instance of a sound with pause/resume capabilities
|
|
5264
|
+
* @memberof Audio
|
|
5119
5265
|
* @example
|
|
5120
5266
|
* // Play a sound and get an instance for control
|
|
5121
5267
|
* const jumpSound = new Sound([.5,.5,220]);
|
|
@@ -5315,6 +5461,12 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
5315
5461
|
|
|
5316
5462
|
///////////////////////////////////////////////////////////////////////////////
|
|
5317
5463
|
|
|
5464
|
+
/**
|
|
5465
|
+
* @callback AudioEndedCallback - Function called when a sound ends
|
|
5466
|
+
* @param {AudioBufferSourceNode} source
|
|
5467
|
+
* @memberof Audio
|
|
5468
|
+
*/
|
|
5469
|
+
|
|
5318
5470
|
/** Play cached audio samples with given settings
|
|
5319
5471
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
5320
5472
|
* @param {number} [volume] - How much to scale volume by
|
|
@@ -5324,7 +5476,7 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
5324
5476
|
* @param {number} [sampleRate=44100] - Sample rate for the sound
|
|
5325
5477
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
5326
5478
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
5327
|
-
* @param {
|
|
5479
|
+
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
5328
5480
|
* @return {AudioBufferSourceNode} - The audio node of the sound played
|
|
5329
5481
|
* @memberof Audio */
|
|
5330
5482
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
@@ -5356,16 +5508,14 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
5356
5508
|
if (onended)
|
|
5357
5509
|
source.addEventListener('ended', ()=> onended(source));
|
|
5358
5510
|
|
|
5511
|
+
const startOffset = offset * rate;
|
|
5359
5512
|
if (!audioIsRunning())
|
|
5360
5513
|
{
|
|
5361
|
-
// fix stalled audio
|
|
5362
|
-
audioContext.resume();
|
|
5363
|
-
return;
|
|
5514
|
+
// fix stalled audio and start
|
|
5515
|
+
audioContext.resume().then(()=>source.start(0, startOffset));
|
|
5364
5516
|
}
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
const startOffset = offset * rate;
|
|
5368
|
-
source.start(0, startOffset);
|
|
5517
|
+
else
|
|
5518
|
+
source.start(0, startOffset);
|
|
5369
5519
|
return source;
|
|
5370
5520
|
}
|
|
5371
5521
|
|
|
@@ -5530,7 +5680,7 @@ function zzfxG
|
|
|
5530
5680
|
* - Unlimited numbers of layers, allocates canvases as needed
|
|
5531
5681
|
* - Tile layers can be drawn to using their context with canvas2d
|
|
5532
5682
|
* - Tile layers can also have collision with EngineObjects
|
|
5533
|
-
* @namespace
|
|
5683
|
+
* @namespace TileLayers
|
|
5534
5684
|
*/
|
|
5535
5685
|
|
|
5536
5686
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -5538,13 +5688,13 @@ function zzfxG
|
|
|
5538
5688
|
|
|
5539
5689
|
/** Keep track of all tile layers with collision
|
|
5540
5690
|
* @type {Array<TileCollisionLayer>}
|
|
5541
|
-
* @memberof
|
|
5691
|
+
* @memberof TileLayers */
|
|
5542
5692
|
const tileCollisionLayers = [];
|
|
5543
5693
|
|
|
5544
5694
|
/** Get tile collision data for a given cell in the grid
|
|
5545
5695
|
* @param {Vector2} pos
|
|
5546
5696
|
* @return {number}
|
|
5547
|
-
* @memberof
|
|
5697
|
+
* @memberof TileLayers */
|
|
5548
5698
|
function tileCollisionGetData(pos)
|
|
5549
5699
|
{
|
|
5550
5700
|
// check all tile collision layers
|
|
@@ -5560,7 +5710,7 @@ function tileCollisionGetData(pos)
|
|
|
5560
5710
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5561
5711
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
5562
5712
|
* @return {TileCollisionLayer}
|
|
5563
|
-
* @memberof
|
|
5713
|
+
* @memberof TileLayers */
|
|
5564
5714
|
function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
5565
5715
|
{
|
|
5566
5716
|
for (const layer of tileCollisionLayers)
|
|
@@ -5578,7 +5728,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
5578
5728
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5579
5729
|
* @param {boolean} [solidOnly=true] - Only check solid layers if true
|
|
5580
5730
|
* @return {Vector2}
|
|
5581
|
-
* @memberof
|
|
5731
|
+
* @memberof TileLayers */
|
|
5582
5732
|
function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
5583
5733
|
{
|
|
5584
5734
|
for (const layer of tileCollisionLayers)
|
|
@@ -5601,8 +5751,8 @@ function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
|
5601
5751
|
* @param {number} [collisionLayer] - Layer to use for collision if any
|
|
5602
5752
|
* @param {boolean} [draw] - Should the layer be drawn automatically
|
|
5603
5753
|
* @return {Array<TileCollisionLayer>}
|
|
5604
|
-
* @memberof
|
|
5605
|
-
function
|
|
5754
|
+
* @memberof TileLayers */
|
|
5755
|
+
function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
|
|
5606
5756
|
{
|
|
5607
5757
|
if (!tileMapData)
|
|
5608
5758
|
{
|
|
@@ -5633,9 +5783,9 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5633
5783
|
tileLayers[layerIndex] = tileLayer;
|
|
5634
5784
|
|
|
5635
5785
|
// apply layer color
|
|
5636
|
-
const layerColor = dataLayer.
|
|
5637
|
-
|
|
5638
|
-
|
|
5786
|
+
const layerColor = dataLayer.tintcolor ?
|
|
5787
|
+
new Color().setHex(dataLayer.tintcolor) :
|
|
5788
|
+
dataLayer.color || WHITE;
|
|
5639
5789
|
ASSERT(isColor(layerColor), 'layer color is not a color');
|
|
5640
5790
|
|
|
5641
5791
|
for (let x=levelSize.x; x--;)
|
|
@@ -5662,6 +5812,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5662
5812
|
///////////////////////////////////////////////////////////////////////////////
|
|
5663
5813
|
/**
|
|
5664
5814
|
* Tile layer data object stores info about how to draw a tile
|
|
5815
|
+
* @memberof TileLayers
|
|
5665
5816
|
* @example
|
|
5666
5817
|
* // create tile layer data with tile index 0 and random orientation and color
|
|
5667
5818
|
* const tileIndex = 0;
|
|
@@ -5699,6 +5850,7 @@ class TileLayerData
|
|
|
5699
5850
|
* - Contains an offscreen canvas that can be rendered to
|
|
5700
5851
|
* - WebGL rendering is optional, call useWebGL to enable
|
|
5701
5852
|
* @extends EngineObject
|
|
5853
|
+
* @memberof TileLayers
|
|
5702
5854
|
* @example
|
|
5703
5855
|
* const canvasLayer = new CanvasLayer(vec2(), vec2(200,100));
|
|
5704
5856
|
*/
|
|
@@ -5713,6 +5865,7 @@ class CanvasLayer extends EngineObject
|
|
|
5713
5865
|
*/
|
|
5714
5866
|
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
5715
5867
|
{
|
|
5868
|
+
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
5716
5869
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
5717
5870
|
|
|
5718
5871
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
@@ -5760,12 +5913,18 @@ class CanvasLayer extends EngineObject
|
|
|
5760
5913
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5761
5914
|
}
|
|
5762
5915
|
|
|
5916
|
+
/**
|
|
5917
|
+
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
5918
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
5919
|
+
* @memberof TileLayers
|
|
5920
|
+
*/
|
|
5921
|
+
|
|
5763
5922
|
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
5764
5923
|
* @param {Vector2} pos
|
|
5765
5924
|
* @param {Vector2} size
|
|
5766
5925
|
* @param {number} angle
|
|
5767
5926
|
* @param {boolean} mirror
|
|
5768
|
-
* @param {
|
|
5927
|
+
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5769
5928
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5770
5929
|
{
|
|
5771
5930
|
const context = this.context;
|
|
@@ -5802,7 +5961,7 @@ class CanvasLayer extends EngineObject
|
|
|
5802
5961
|
else
|
|
5803
5962
|
{
|
|
5804
5963
|
// untextured
|
|
5805
|
-
context.fillStyle = color;
|
|
5964
|
+
context.fillStyle = color.toString();
|
|
5806
5965
|
context.fillRect(-.5, -.5, 1, 1);
|
|
5807
5966
|
}
|
|
5808
5967
|
});
|
|
@@ -5839,7 +5998,9 @@ class CanvasLayer extends EngineObject
|
|
|
5839
5998
|
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
5840
5999
|
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
5841
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)
|
|
5842
6002
|
* @extends CanvasLayer
|
|
6003
|
+
* @memberof TileLayers
|
|
5843
6004
|
* @example
|
|
5844
6005
|
* const tileLayer = new TileLayer(vec2(), vec2(200,100));
|
|
5845
6006
|
*/
|
|
@@ -5849,15 +6010,14 @@ class TileLayer extends CanvasLayer
|
|
|
5849
6010
|
* @param {Vector2} position - World space position
|
|
5850
6011
|
* @param {Vector2} size - World space size
|
|
5851
6012
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
5852
|
-
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
5853
6013
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5854
6014
|
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5855
6015
|
*/
|
|
5856
|
-
constructor(position, size, tileInfo=tile(),
|
|
6016
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
5857
6017
|
{
|
|
5858
6018
|
super(position, size, 0, renderOrder, size);
|
|
6019
|
+
|
|
5859
6020
|
this.tileInfo = tileInfo;
|
|
5860
|
-
|
|
5861
6021
|
const canvasSize = size.multiply(tileInfo.size);
|
|
5862
6022
|
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
5863
6023
|
this.canvas = new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
@@ -5894,6 +6054,8 @@ class TileLayer extends CanvasLayer
|
|
|
5894
6054
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
5895
6055
|
setData(layerPos, data, redraw=false)
|
|
5896
6056
|
{
|
|
6057
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
6058
|
+
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
5897
6059
|
if (layerPos.arrayCheck(this.size))
|
|
5898
6060
|
{
|
|
5899
6061
|
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
@@ -5905,7 +6067,10 @@ class TileLayer extends CanvasLayer
|
|
|
5905
6067
|
* @param {Vector2} layerPos - Local position in array
|
|
5906
6068
|
* @return {TileLayerData} */
|
|
5907
6069
|
getData(layerPos)
|
|
5908
|
-
{
|
|
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
|
+
}
|
|
5909
6074
|
|
|
5910
6075
|
// Render the tile layer, called automatically by the engine
|
|
5911
6076
|
render()
|
|
@@ -5914,9 +6079,10 @@ class TileLayer extends CanvasLayer
|
|
|
5914
6079
|
|
|
5915
6080
|
// draw the tile layer as a single tile
|
|
5916
6081
|
const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
|
|
5917
|
-
const
|
|
6082
|
+
const size = this.drawSize || this.size;
|
|
6083
|
+
const pos = this.pos.add(size.scale(.5));
|
|
5918
6084
|
const useWebGL = glEnable && this.glTexture !== undefined;
|
|
5919
|
-
drawTile(pos,
|
|
6085
|
+
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
5920
6086
|
}
|
|
5921
6087
|
|
|
5922
6088
|
/** Draw all the tile data to an offscreen canvas
|
|
@@ -6008,6 +6174,7 @@ class TileLayer extends CanvasLayer
|
|
|
6008
6174
|
* - there can be multiple tile collision layers
|
|
6009
6175
|
* - tile collision layers should not overlap each other
|
|
6010
6176
|
* @extends TileLayer
|
|
6177
|
+
* @memberof TileLayers
|
|
6011
6178
|
*/
|
|
6012
6179
|
class TileCollisionLayer extends TileLayer
|
|
6013
6180
|
{
|
|
@@ -6020,8 +6187,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6020
6187
|
*/
|
|
6021
6188
|
constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
|
|
6022
6189
|
{
|
|
6023
|
-
|
|
6024
|
-
super(position, size.floor(), tileInfo, scale, renderOrder, useWebGL);
|
|
6190
|
+
super(position, size.floor(), tileInfo, renderOrder, useWebGL);
|
|
6025
6191
|
|
|
6026
6192
|
/** @property {Array<number>} - The tile collision grid */
|
|
6027
6193
|
this.collisionData = [];
|
|
@@ -6051,6 +6217,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6051
6217
|
* @param {Vector2} size - width and height of tile collision 2d grid */
|
|
6052
6218
|
initCollision(size)
|
|
6053
6219
|
{
|
|
6220
|
+
ASSERT(isVector2(size), 'size must be a Vector2');
|
|
6054
6221
|
this.size = size.floor();
|
|
6055
6222
|
this.collisionData = [];
|
|
6056
6223
|
this.collisionData.length = size.area();
|
|
@@ -6062,6 +6229,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6062
6229
|
* @param {number} [data] */
|
|
6063
6230
|
setCollisionData(gridPos, data=1)
|
|
6064
6231
|
{
|
|
6232
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
6065
6233
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
6066
6234
|
gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
6067
6235
|
}
|
|
@@ -6071,6 +6239,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6071
6239
|
* @return {number} */
|
|
6072
6240
|
getCollisionData(gridPos)
|
|
6073
6241
|
{
|
|
6242
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
6074
6243
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
6075
6244
|
return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
6076
6245
|
}
|
|
@@ -6082,6 +6251,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
6082
6251
|
* @return {boolean} */
|
|
6083
6252
|
collisionTest(pos, size=new Vector2, object)
|
|
6084
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
|
+
|
|
6085
6257
|
// transform to local layer space
|
|
6086
6258
|
const posX = pos.x - this.pos.x;
|
|
6087
6259
|
const posY = pos.y - this.pos.y;
|
|
@@ -6112,6 +6284,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
6112
6284
|
* @return {Vector2} */
|
|
6113
6285
|
collisionRaycast(posStart, posEnd, object)
|
|
6114
6286
|
{
|
|
6287
|
+
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
6288
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
6289
|
+
|
|
6115
6290
|
// transform to local layer space
|
|
6116
6291
|
const posStartX = posStart.x - this.pos.x;
|
|
6117
6292
|
const posStartY = posStart.y - this.pos.y;
|
|
@@ -6161,9 +6336,16 @@ class TileCollisionLayer extends TileLayer
|
|
|
6161
6336
|
* LittleJS Particle System
|
|
6162
6337
|
*/
|
|
6163
6338
|
|
|
6339
|
+
/**
|
|
6340
|
+
* @callback ParticleCallbackFunction - Function that processes a particle
|
|
6341
|
+
* @param {Particle} particle
|
|
6342
|
+
* @memberof Engine
|
|
6343
|
+
*/
|
|
6344
|
+
|
|
6164
6345
|
/**
|
|
6165
6346
|
* Particle Emitter - Spawns particles with the given settings
|
|
6166
6347
|
* @extends EngineObject
|
|
6348
|
+
* @memberof Engine
|
|
6167
6349
|
* @example
|
|
6168
6350
|
* // create a particle emitter
|
|
6169
6351
|
* let pos = vec2(2,3);
|
|
@@ -6188,10 +6370,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6188
6370
|
* @param {number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
6189
6371
|
* @param {number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
6190
6372
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
6191
|
-
* @param {Color} [colorStartA=
|
|
6192
|
-
* @param {Color} [colorStartB=
|
|
6193
|
-
* @param {Color} [colorEndA=
|
|
6194
|
-
* @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
|
|
6195
6377
|
* @param {number} [particleTime] - How long particles live
|
|
6196
6378
|
* @param {number} [sizeStart] - How big are particles at start
|
|
6197
6379
|
* @param {number} [sizeEnd] - How big are particles at end
|
|
@@ -6218,10 +6400,10 @@ class ParticleEmitter extends EngineObject
|
|
|
6218
6400
|
emitRate = 100,
|
|
6219
6401
|
emitConeAngle = PI,
|
|
6220
6402
|
tileInfo,
|
|
6221
|
-
colorStartA =
|
|
6222
|
-
colorStartB =
|
|
6223
|
-
colorEndA =
|
|
6224
|
-
colorEndB =
|
|
6403
|
+
colorStartA = WHITE,
|
|
6404
|
+
colorStartB = WHITE,
|
|
6405
|
+
colorEndA = CLEAR_WHITE,
|
|
6406
|
+
colorEndB = CLEAR_WHITE,
|
|
6225
6407
|
particleTime = .5,
|
|
6226
6408
|
sizeStart = .1,
|
|
6227
6409
|
sizeEnd = 1,
|
|
@@ -6244,7 +6426,8 @@ class ParticleEmitter extends EngineObject
|
|
|
6244
6426
|
|
|
6245
6427
|
// emitter settings
|
|
6246
6428
|
/** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
6247
|
-
this.emitSize = emitSize
|
|
6429
|
+
this.emitSize = emitSize instanceof Vector2 ?
|
|
6430
|
+
emitSize.copy() : emitSize;
|
|
6248
6431
|
/** @property {number} - How long to stay alive (0 is forever) */
|
|
6249
6432
|
this.emitTime = emitTime;
|
|
6250
6433
|
/** @property {number} - How many particles per second to spawn, does not emit if 0 */
|
|
@@ -6254,13 +6437,13 @@ class ParticleEmitter extends EngineObject
|
|
|
6254
6437
|
|
|
6255
6438
|
// color settings
|
|
6256
6439
|
/** @property {Color} - Color at start of life 1, randomized between start colors */
|
|
6257
|
-
this.colorStartA = colorStartA;
|
|
6440
|
+
this.colorStartA = colorStartA.copy();
|
|
6258
6441
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
6259
|
-
this.colorStartB = colorStartB;
|
|
6442
|
+
this.colorStartB = colorStartB.copy();
|
|
6260
6443
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
6261
|
-
this.colorEndA = colorEndA;
|
|
6444
|
+
this.colorEndA = colorEndA.copy();
|
|
6262
6445
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
6263
|
-
this.colorEndB = colorEndB;
|
|
6446
|
+
this.colorEndB = colorEndB.copy();
|
|
6264
6447
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
6265
6448
|
this.randomColorLinear = randomColorLinear;
|
|
6266
6449
|
|
|
@@ -6295,9 +6478,9 @@ class ParticleEmitter extends EngineObject
|
|
|
6295
6478
|
this.localSpace = localSpace;
|
|
6296
6479
|
/** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
6297
6480
|
this.trailScale = 0;
|
|
6298
|
-
/** @property {
|
|
6481
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is destroyed */
|
|
6299
6482
|
this.particleDestroyCallback = undefined;
|
|
6300
|
-
/** @property {
|
|
6483
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is created */
|
|
6301
6484
|
this.particleCreateCallback = undefined;
|
|
6302
6485
|
/** @property {number} - Track particle emit time */
|
|
6303
6486
|
this.emitTimeBuffer = 0;
|
|
@@ -6398,6 +6581,7 @@ class ParticleEmitter extends EngineObject
|
|
|
6398
6581
|
/**
|
|
6399
6582
|
* Particle Object - Created automatically by Particle Emitters
|
|
6400
6583
|
* @extends EngineObject
|
|
6584
|
+
* @memberof Engine
|
|
6401
6585
|
*/
|
|
6402
6586
|
class Particle extends EngineObject
|
|
6403
6587
|
{
|
|
@@ -6416,7 +6600,7 @@ class Particle extends EngineObject
|
|
|
6416
6600
|
* @param {boolean} additive - Does it use additive blend mode
|
|
6417
6601
|
* @param {number} trailScale - If a trail, how long to make it
|
|
6418
6602
|
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
6419
|
-
* @param {
|
|
6603
|
+
* @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
|
|
6420
6604
|
*/
|
|
6421
6605
|
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
6422
6606
|
)
|
|
@@ -6425,14 +6609,14 @@ class Particle extends EngineObject
|
|
|
6425
6609
|
|
|
6426
6610
|
/** @property {Color} - Color at start of life */
|
|
6427
6611
|
this.colorStart = colorStart;
|
|
6428
|
-
/** @property {Color} -
|
|
6429
|
-
this.
|
|
6612
|
+
/** @property {Color} - Color at end of life */
|
|
6613
|
+
this.colorEnd = colorEnd;
|
|
6430
6614
|
/** @property {number} - How long to live for */
|
|
6431
6615
|
this.lifeTime = lifeTime;
|
|
6432
6616
|
/** @property {number} - Size at start of life */
|
|
6433
6617
|
this.sizeStart = sizeStart;
|
|
6434
|
-
/** @property {number} -
|
|
6435
|
-
this.
|
|
6618
|
+
/** @property {number} - Size at end of life */
|
|
6619
|
+
this.sizeEnd = sizeEnd;
|
|
6436
6620
|
/** @property {number} - How quick to fade in/out */
|
|
6437
6621
|
this.fadeRate = fadeRate;
|
|
6438
6622
|
/** @property {boolean} - Is it additive */
|
|
@@ -6441,7 +6625,7 @@ class Particle extends EngineObject
|
|
|
6441
6625
|
this.trailScale = trailScale;
|
|
6442
6626
|
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
6443
6627
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
6444
|
-
/** @property {
|
|
6628
|
+
/** @property {ParticleCallbackFunction} - Called when particle dies */
|
|
6445
6629
|
this.destroyCallback = destroyCallback;
|
|
6446
6630
|
// particles do not clamp speed by default
|
|
6447
6631
|
this.clampSpeed = false;
|
|
@@ -6468,54 +6652,58 @@ class Particle extends EngineObject
|
|
|
6468
6652
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
6469
6653
|
render()
|
|
6470
6654
|
{
|
|
6471
|
-
//
|
|
6472
|
-
const
|
|
6473
|
-
const radius = this.sizeStart +
|
|
6474
|
-
|
|
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
|
|
6475
6665
|
const fadeRate = this.fadeRate/2;
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
this.colorStart.g + p * this.colorEndDelta.g,
|
|
6479
|
-
this.colorStart.b + p * this.colorEndDelta.b,
|
|
6480
|
-
(this.colorStart.a + p * this.colorEndDelta.a) *
|
|
6481
|
-
(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;
|
|
6482
6668
|
|
|
6483
6669
|
// draw the particle
|
|
6484
6670
|
this.additive && setBlendMode(true);
|
|
6485
6671
|
|
|
6672
|
+
// update the position and angle for drawing
|
|
6486
6673
|
let pos = this.pos, angle = this.angle;
|
|
6487
6674
|
if (this.localSpaceEmitter)
|
|
6488
6675
|
{
|
|
6489
6676
|
// in local space of emitter
|
|
6490
|
-
|
|
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));
|
|
6491
6681
|
angle += this.localSpaceEmitter.angle;
|
|
6492
6682
|
}
|
|
6493
6683
|
if (this.trailScale)
|
|
6494
6684
|
{
|
|
6495
6685
|
// trail style particles
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
const speed =
|
|
6686
|
+
const direction = this.localSpaceEmitter ?
|
|
6687
|
+
this.velocity.rotate(-this.localSpaceEmitter.angle) :
|
|
6688
|
+
this.velocity;
|
|
6689
|
+
const speed = direction.length();
|
|
6500
6690
|
if (speed)
|
|
6501
6691
|
{
|
|
6502
|
-
|
|
6692
|
+
// stretch in direction of motion
|
|
6503
6693
|
const trailLength = speed * this.trailScale;
|
|
6504
|
-
size.y = max(size.x, trailLength);
|
|
6505
|
-
angle = direction.
|
|
6506
|
-
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);
|
|
6507
6697
|
}
|
|
6508
6698
|
}
|
|
6509
6699
|
else
|
|
6510
|
-
drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
|
|
6700
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
6511
6701
|
this.additive && setBlendMode();
|
|
6512
|
-
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
6702
|
+
debugParticles && debugRect(pos, this.size, '#f005', 0, angle);
|
|
6513
6703
|
|
|
6514
|
-
if (
|
|
6704
|
+
if (p1 === 1)
|
|
6515
6705
|
{
|
|
6516
|
-
// destroy particle when
|
|
6517
|
-
this.color = color;
|
|
6518
|
-
this.size = size;
|
|
6706
|
+
// destroy particle when its time runs out
|
|
6519
6707
|
this.destroyCallback && this.destroyCallback(this);
|
|
6520
6708
|
this.destroyed = 1;
|
|
6521
6709
|
}
|
|
@@ -6580,8 +6768,14 @@ function medalsInit(saveName)
|
|
|
6580
6768
|
}
|
|
6581
6769
|
}
|
|
6582
6770
|
|
|
6771
|
+
/**
|
|
6772
|
+
* @callback MedalCallbackFunction - Function that processes a medal
|
|
6773
|
+
* @param {Medal} medal
|
|
6774
|
+
* @memberof Medals
|
|
6775
|
+
*/
|
|
6776
|
+
|
|
6583
6777
|
/** Calls a function for each medal
|
|
6584
|
-
* @param {
|
|
6778
|
+
* @param {MedalCallbackFunction} callback
|
|
6585
6779
|
* @memberof Medals */
|
|
6586
6780
|
function medalsForEach(callback)
|
|
6587
6781
|
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
@@ -6590,6 +6784,7 @@ function medalsForEach(callback)
|
|
|
6590
6784
|
|
|
6591
6785
|
/**
|
|
6592
6786
|
* Medal - Tracks an unlockable medal
|
|
6787
|
+
* @memberof Medals
|
|
6593
6788
|
* @example
|
|
6594
6789
|
* // create a medal
|
|
6595
6790
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
@@ -6658,11 +6853,12 @@ class Medal
|
|
|
6658
6853
|
const height = medalDisplaySize.y;
|
|
6659
6854
|
const x = overlayCanvas.width - width;
|
|
6660
6855
|
const y = -height*hidePercent;
|
|
6856
|
+
const backgroundColor = hsl(0,0,.9);
|
|
6661
6857
|
|
|
6662
6858
|
// draw containing rect and clip to that region
|
|
6663
6859
|
context.save();
|
|
6664
6860
|
context.beginPath();
|
|
6665
|
-
context.fillStyle =
|
|
6861
|
+
context.fillStyle = backgroundColor.toString();
|
|
6666
6862
|
context.strokeStyle = BLACK.toString();
|
|
6667
6863
|
context.lineWidth = 3;
|
|
6668
6864
|
context.rect(x, y, width, height);
|
|
@@ -7464,24 +7660,25 @@ function glPolyStrip(points)
|
|
|
7464
7660
|
return strip;
|
|
7465
7661
|
}
|
|
7466
7662
|
/**
|
|
7467
|
-
* LittleJS Newgrounds
|
|
7663
|
+
* LittleJS Newgrounds Plugin
|
|
7468
7664
|
* - NewgroundsMedal extends Medal with Newgrounds API functionality
|
|
7469
|
-
* - Call new NewgroundsPlugin() to setup Newgrounds
|
|
7665
|
+
* - Call new NewgroundsPlugin(app_id) to setup Newgrounds
|
|
7470
7666
|
* - Uses CryptoJS for encryption if optional cipher is provided
|
|
7667
|
+
* - provides functions to interact with medals scoreboards
|
|
7471
7668
|
* - Keeps connection alive and logs views
|
|
7472
|
-
*
|
|
7473
|
-
* - Functions to unlock medals
|
|
7669
|
+
* @namespace Newgrounds
|
|
7474
7670
|
*/
|
|
7475
7671
|
|
|
7476
7672
|
/** Global Newgrounds object
|
|
7477
7673
|
* @type {NewgroundsPlugin}
|
|
7478
|
-
* @memberof
|
|
7674
|
+
* @memberof Newgrounds */
|
|
7479
7675
|
let newgrounds;
|
|
7480
7676
|
|
|
7481
7677
|
///////////////////////////////////////////////////////////////////////////////
|
|
7482
7678
|
/**
|
|
7483
7679
|
* Newgrounds medal auto unlocks in newgrounds API
|
|
7484
7680
|
* @extends Medal
|
|
7681
|
+
* @memberof Newgrounds
|
|
7485
7682
|
*/
|
|
7486
7683
|
class NewgroundsMedal extends Medal
|
|
7487
7684
|
{
|
|
@@ -7506,6 +7703,7 @@ class NewgroundsMedal extends Medal
|
|
|
7506
7703
|
///////////////////////////////////////////////////////////////////////////////
|
|
7507
7704
|
/**
|
|
7508
7705
|
* Newgrounds API object
|
|
7706
|
+
* @memberof Newgrounds
|
|
7509
7707
|
*/
|
|
7510
7708
|
class NewgroundsPlugin
|
|
7511
7709
|
{
|
|
@@ -7641,19 +7839,22 @@ class NewgroundsPlugin
|
|
|
7641
7839
|
/**
|
|
7642
7840
|
* LittleJS Post Processing Plugin
|
|
7643
7841
|
* - Supports shadertoy style post processing shaders
|
|
7644
|
-
* - call new
|
|
7842
|
+
* - call new PostProcessPlugin() to setup post processing
|
|
7645
7843
|
* - can be enabled to pass other canvases through a final shader
|
|
7844
|
+
* @namespace PostProcess
|
|
7646
7845
|
*/
|
|
7647
7846
|
|
|
7648
7847
|
///////////////////////////////////////////////////////////////////////////////
|
|
7649
7848
|
|
|
7650
7849
|
/** Global Post Process plugin object
|
|
7651
|
-
* @type {PostProcessPlugin}
|
|
7850
|
+
* @type {PostProcessPlugin}
|
|
7851
|
+
* @memberof PostProcess */
|
|
7652
7852
|
let postProcess;
|
|
7653
7853
|
|
|
7654
7854
|
/////////////////////////////////////////////////////////////////////////
|
|
7655
7855
|
/**
|
|
7656
7856
|
* UI System Global Object
|
|
7857
|
+
* @memberof PostProcess
|
|
7657
7858
|
*/
|
|
7658
7859
|
class PostProcessPlugin
|
|
7659
7860
|
{
|
|
@@ -7761,12 +7962,15 @@ class PostProcessPlugin
|
|
|
7761
7962
|
}
|
|
7762
7963
|
/**
|
|
7763
7964
|
* LittleJS ZzFXM Plugin
|
|
7965
|
+
* @namespace ZzFXM
|
|
7764
7966
|
*/
|
|
7765
7967
|
|
|
7766
7968
|
/**
|
|
7767
7969
|
* Music Object - Stores a zzfx music track for later use
|
|
7768
7970
|
*
|
|
7769
7971
|
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
7972
|
+
* @extends Sound
|
|
7973
|
+
* @memberof ZzFXM
|
|
7770
7974
|
* @example
|
|
7771
7975
|
* // create some music
|
|
7772
7976
|
* const music_example = new Music(
|
|
@@ -7825,7 +8029,8 @@ class ZzFXMusic extends Sound
|
|
|
7825
8029
|
* @param {Array} patterns - Array of pattern data
|
|
7826
8030
|
* @param {Array} sequence - Array of pattern indexes
|
|
7827
8031
|
* @param {number} [BPM] - Playback speed of the song in BPM
|
|
7828
|
-
* @return {Array} - Left and right channel sample data
|
|
8032
|
+
* @return {Array} - Left and right channel sample data
|
|
8033
|
+
* @memberof ZzFXM */
|
|
7829
8034
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
7830
8035
|
{
|
|
7831
8036
|
let i, j, k;
|
|
@@ -7928,17 +8133,20 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
7928
8133
|
* - Buttons
|
|
7929
8134
|
* - Checkboxes
|
|
7930
8135
|
* - Images
|
|
8136
|
+
* @namespace UISystem
|
|
7931
8137
|
*/
|
|
7932
8138
|
|
|
7933
8139
|
///////////////////////////////////////////////////////////////////////////////
|
|
7934
8140
|
|
|
7935
8141
|
/** Global UI system plugin object
|
|
7936
|
-
* @type {UISystemPlugin}
|
|
8142
|
+
* @type {UISystemPlugin}
|
|
8143
|
+
* @memberof UISystem */
|
|
7937
8144
|
let uiSystem;
|
|
7938
8145
|
|
|
7939
8146
|
///////////////////////////////////////////////////////////////////////////////
|
|
7940
8147
|
/**
|
|
7941
8148
|
* UI System Global Object
|
|
8149
|
+
* @memberof UISystem
|
|
7942
8150
|
*/
|
|
7943
8151
|
class UISystemPlugin
|
|
7944
8152
|
{
|
|
@@ -7983,10 +8191,12 @@ class UISystemPlugin
|
|
|
7983
8191
|
this.uiObjects = [];
|
|
7984
8192
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
7985
8193
|
this.uiContext = context;
|
|
7986
|
-
/** @property {UIObject} - Top most object user is over */
|
|
7987
|
-
this.hoverObject = undefined;
|
|
7988
8194
|
/** @property {UIObject} - Object user is currently interacting with */
|
|
7989
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;
|
|
7990
8200
|
|
|
7991
8201
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7992
8202
|
|
|
@@ -8017,6 +8227,7 @@ class UISystemPlugin
|
|
|
8017
8227
|
updateInvisibleObject(o);
|
|
8018
8228
|
}
|
|
8019
8229
|
// reset hover object at start of update
|
|
8230
|
+
uiSystem.lastHoverObject = uiSystem.hoverObject;
|
|
8020
8231
|
uiSystem.hoverObject = undefined;
|
|
8021
8232
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
8022
8233
|
{
|
|
@@ -8049,6 +8260,13 @@ class UISystemPlugin
|
|
|
8049
8260
|
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius] */
|
|
8050
8261
|
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius)
|
|
8051
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
|
+
|
|
8052
8270
|
const context = uiSystem.uiContext;
|
|
8053
8271
|
context.fillStyle = color.toString();
|
|
8054
8272
|
context.beginPath();
|
|
@@ -8072,6 +8290,11 @@ class UISystemPlugin
|
|
|
8072
8290
|
* @param {Color} [lineColor=uiSystem.defaultLineColor] */
|
|
8073
8291
|
drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
|
|
8074
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
|
+
|
|
8075
8298
|
const context = uiSystem.uiContext;
|
|
8076
8299
|
context.strokeStyle = lineColor.toString();
|
|
8077
8300
|
context.lineWidth = lineWidth;
|
|
@@ -8107,12 +8330,37 @@ class UISystemPlugin
|
|
|
8107
8330
|
{
|
|
8108
8331
|
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
8109
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
|
+
}
|
|
8110
8358
|
}
|
|
8111
8359
|
|
|
8112
8360
|
///////////////////////////////////////////////////////////////////////////////
|
|
8113
8361
|
/**
|
|
8114
8362
|
* UI Object - Base level object for all UI elements
|
|
8115
|
-
*/
|
|
8363
|
+
* @memberof UISystem */
|
|
8116
8364
|
class UIObject
|
|
8117
8365
|
{
|
|
8118
8366
|
/** Create a UIObject
|
|
@@ -8121,6 +8369,9 @@ class UIObject
|
|
|
8121
8369
|
*/
|
|
8122
8370
|
constructor(pos=vec2(), size=vec2())
|
|
8123
8371
|
{
|
|
8372
|
+
ASSERT(isVector2(pos), 'ui object pos must be a vec2');
|
|
8373
|
+
ASSERT(isVector2(size), 'ui object size must be a vec2');
|
|
8374
|
+
|
|
8124
8375
|
/** @property {Vector2} - Local position of the object */
|
|
8125
8376
|
this.localPos = pos.copy();
|
|
8126
8377
|
/** @property {Vector2} - Screen space position of the object */
|
|
@@ -8128,21 +8379,21 @@ class UIObject
|
|
|
8128
8379
|
/** @property {Vector2} - Screen space size of the object */
|
|
8129
8380
|
this.size = size.copy();
|
|
8130
8381
|
/** @property {Color} - Color of the object */
|
|
8131
|
-
this.color = uiSystem.defaultColor;
|
|
8382
|
+
this.color = uiSystem.defaultColor.copy();
|
|
8132
8383
|
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
8133
8384
|
this.activeColor = undefined;
|
|
8134
8385
|
/** @property {string} - Text for this ui object */
|
|
8135
8386
|
this.text = undefined;
|
|
8136
8387
|
/** @property {Color} - Color when disabled */
|
|
8137
|
-
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
8388
|
+
this.disabledColor = uiSystem.defaultDisabledColor.copy();
|
|
8138
8389
|
/** @property {boolean} - Is this object disabled? */
|
|
8139
8390
|
this.disabled = false;
|
|
8140
8391
|
/** @property {Color} - Color for text */
|
|
8141
|
-
this.textColor = uiSystem.defaultTextColor
|
|
8392
|
+
this.textColor = uiSystem.defaultTextColor.copy()
|
|
8142
8393
|
/** @property {Color} - Color used when hovering over the object */
|
|
8143
|
-
this.hoverColor = uiSystem.defaultHoverColor
|
|
8394
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy()
|
|
8144
8395
|
/** @property {Color} - Color for line drawing */
|
|
8145
|
-
this.lineColor = uiSystem.defaultLineColor
|
|
8396
|
+
this.lineColor = uiSystem.defaultLineColor.copy()
|
|
8146
8397
|
/** @property {number} - Width for line drawing */
|
|
8147
8398
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
8148
8399
|
/** @property {number} - Corner radius for rounded rects */
|
|
@@ -8199,7 +8450,7 @@ class UIObject
|
|
|
8199
8450
|
/** Update the object, called automatically by plugin once each frame */
|
|
8200
8451
|
update()
|
|
8201
8452
|
{
|
|
8202
|
-
const wasHover = this
|
|
8453
|
+
const wasHover = uiSystem.lastHoverObject === this;
|
|
8203
8454
|
const isActive = this.isActiveObject();
|
|
8204
8455
|
const mouseDown = mouseIsDown(0);
|
|
8205
8456
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
@@ -8212,15 +8463,14 @@ class UIObject
|
|
|
8212
8463
|
}
|
|
8213
8464
|
if (this.isHoverObject())
|
|
8214
8465
|
{
|
|
8215
|
-
if (mousePress)
|
|
8216
|
-
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
8217
8466
|
if (!this.disabled)
|
|
8218
8467
|
{
|
|
8219
8468
|
if (mousePress)
|
|
8220
8469
|
{
|
|
8221
8470
|
if (this.interactive)
|
|
8222
8471
|
{
|
|
8223
|
-
this.
|
|
8472
|
+
if (!this.dragActivate || (!wasHover || mouseWasPressed(0)))
|
|
8473
|
+
this.onPress();
|
|
8224
8474
|
if (this.soundPress)
|
|
8225
8475
|
this.soundPress.play();
|
|
8226
8476
|
if (uiSystem.activeObject && !isActive)
|
|
@@ -8228,13 +8478,15 @@ class UIObject
|
|
|
8228
8478
|
uiSystem.activeObject = this;
|
|
8229
8479
|
}
|
|
8230
8480
|
}
|
|
8231
|
-
if (!mouseDown &&
|
|
8481
|
+
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
8232
8482
|
{
|
|
8233
8483
|
this.onClick();
|
|
8234
8484
|
if (this.soundClick)
|
|
8235
8485
|
this.soundClick.play();
|
|
8236
8486
|
}
|
|
8237
8487
|
}
|
|
8488
|
+
// clear mouse was pressed state even when disabled
|
|
8489
|
+
mousePress && inputClearKey(0,0,0,1,0);
|
|
8238
8490
|
}
|
|
8239
8491
|
if (isActive)
|
|
8240
8492
|
if (!mouseDown || (this.dragActivate && !this.isHoverObject()))
|
|
@@ -8245,6 +8497,7 @@ class UIObject
|
|
|
8245
8497
|
uiSystem.activeObject = undefined;
|
|
8246
8498
|
}
|
|
8247
8499
|
|
|
8500
|
+
// call enter/leave events
|
|
8248
8501
|
if (this.isHoverObject() !== wasHover)
|
|
8249
8502
|
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
8250
8503
|
}
|
|
@@ -8255,7 +8508,7 @@ class UIObject
|
|
|
8255
8508
|
if (!this.size.x || !this.size.y) return;
|
|
8256
8509
|
|
|
8257
8510
|
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
8258
|
-
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;
|
|
8259
8512
|
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
8260
8513
|
}
|
|
8261
8514
|
|
|
@@ -8306,6 +8559,7 @@ class UIObject
|
|
|
8306
8559
|
/**
|
|
8307
8560
|
* UIText - A UI object that displays text
|
|
8308
8561
|
* @extends UIObject
|
|
8562
|
+
* @memberof UISystem
|
|
8309
8563
|
*/
|
|
8310
8564
|
class UIText extends UIObject
|
|
8311
8565
|
{
|
|
@@ -8320,6 +8574,10 @@ class UIText extends UIObject
|
|
|
8320
8574
|
{
|
|
8321
8575
|
super(pos, size);
|
|
8322
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
|
+
|
|
8323
8581
|
// set properties
|
|
8324
8582
|
this.text = text;
|
|
8325
8583
|
this.align = align;
|
|
@@ -8339,6 +8597,7 @@ class UIText extends UIObject
|
|
|
8339
8597
|
/**
|
|
8340
8598
|
* UITile - A UI object that displays a tile image
|
|
8341
8599
|
* @extends UIObject
|
|
8600
|
+
* @memberof UISystem
|
|
8342
8601
|
*/
|
|
8343
8602
|
class UITile extends UIObject
|
|
8344
8603
|
{
|
|
@@ -8353,15 +8612,19 @@ class UITile extends UIObject
|
|
|
8353
8612
|
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false)
|
|
8354
8613
|
{
|
|
8355
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
|
+
|
|
8356
8620
|
/** @property {TileInfo} - Tile image to use */
|
|
8357
8621
|
this.tileInfo = tileInfo;
|
|
8358
8622
|
/** @property {number} - Angle to rotate in radians */
|
|
8359
8623
|
this.angle = angle;
|
|
8360
8624
|
/** @property {boolean} - Should it be mirrored? */
|
|
8361
8625
|
this.mirror = mirror;
|
|
8362
|
-
|
|
8363
8626
|
// set properties
|
|
8364
|
-
this.color = color;
|
|
8627
|
+
this.color = color.copy();
|
|
8365
8628
|
}
|
|
8366
8629
|
render()
|
|
8367
8630
|
{
|
|
@@ -8373,6 +8636,7 @@ class UITile extends UIObject
|
|
|
8373
8636
|
/**
|
|
8374
8637
|
* UIButton - A UI object that acts as a button
|
|
8375
8638
|
* @extends UIObject
|
|
8639
|
+
* @memberof UISystem
|
|
8376
8640
|
*/
|
|
8377
8641
|
class UIButton extends UIObject
|
|
8378
8642
|
{
|
|
@@ -8386,9 +8650,12 @@ class UIButton extends UIObject
|
|
|
8386
8650
|
{
|
|
8387
8651
|
super(pos, size);
|
|
8388
8652
|
|
|
8653
|
+
ASSERT(isString(text), 'ui button must be a string');
|
|
8654
|
+
ASSERT(isColor(color), 'ui button color must be a color');
|
|
8655
|
+
|
|
8389
8656
|
// set properties
|
|
8390
8657
|
this.text = text;
|
|
8391
|
-
this.color = color
|
|
8658
|
+
this.color = color.copy()
|
|
8392
8659
|
this.interactive = true;
|
|
8393
8660
|
}
|
|
8394
8661
|
render()
|
|
@@ -8406,6 +8673,7 @@ class UIButton extends UIObject
|
|
|
8406
8673
|
/**
|
|
8407
8674
|
* UICheckbox - A UI object that acts as a checkbox
|
|
8408
8675
|
* @extends UIObject
|
|
8676
|
+
* @memberof UISystem
|
|
8409
8677
|
*/
|
|
8410
8678
|
class UICheckbox extends UIObject
|
|
8411
8679
|
{
|
|
@@ -8419,12 +8687,15 @@ class UICheckbox extends UIObject
|
|
|
8419
8687
|
constructor(pos, size, checked=false, text='', color=uiSystem.defaultButtonColor)
|
|
8420
8688
|
{
|
|
8421
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
|
+
|
|
8422
8694
|
/** @property {boolean} - Current percentage value of this scrollbar 0-1 */
|
|
8423
8695
|
this.checked = checked;
|
|
8424
|
-
|
|
8425
8696
|
// set properties
|
|
8426
8697
|
this.text = text;
|
|
8427
|
-
this.color = color;
|
|
8698
|
+
this.color = color.copy();
|
|
8428
8699
|
this.interactive = true;
|
|
8429
8700
|
}
|
|
8430
8701
|
onClick()
|
|
@@ -8456,6 +8727,7 @@ class UICheckbox extends UIObject
|
|
|
8456
8727
|
/**
|
|
8457
8728
|
* UIScrollbar - A UI object that acts as a scrollbar
|
|
8458
8729
|
* @extends UIObject
|
|
8730
|
+
* @memberof UISystem
|
|
8459
8731
|
*/
|
|
8460
8732
|
class UIScrollbar extends UIObject
|
|
8461
8733
|
{
|
|
@@ -8471,14 +8743,19 @@ class UIScrollbar extends UIObject
|
|
|
8471
8743
|
{
|
|
8472
8744
|
super(pos, size);
|
|
8473
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
|
+
|
|
8474
8751
|
/** @property {number} - Current percentage value of this scrollbar 0-1 */
|
|
8475
8752
|
this.value = value;
|
|
8476
8753
|
/** @property {Color} - Color for the handle part of the scrollbar */
|
|
8477
|
-
this.handleColor = handleColor;
|
|
8754
|
+
this.handleColor = handleColor.copy();
|
|
8478
8755
|
|
|
8479
8756
|
// set properties
|
|
8480
8757
|
this.text = text;
|
|
8481
|
-
this.color = color;
|
|
8758
|
+
this.color = color.copy();
|
|
8482
8759
|
this.interactive = true;
|
|
8483
8760
|
}
|
|
8484
8761
|
update()
|
|
@@ -8486,29 +8763,43 @@ class UIScrollbar extends UIObject
|
|
|
8486
8763
|
super.update();
|
|
8487
8764
|
if (this.isActiveObject() && this.interactive)
|
|
8488
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
|
+
|
|
8489
8772
|
// check if value changed
|
|
8490
|
-
const
|
|
8491
|
-
const
|
|
8492
|
-
const
|
|
8493
|
-
const p2 = this.pos.x + handleWidth/2;
|
|
8773
|
+
const handleWidth = barSize - handleSize;
|
|
8774
|
+
const p1 = centerPos - handleWidth/2;
|
|
8775
|
+
const p2 = centerPos + handleWidth/2;
|
|
8494
8776
|
const oldValue = this.value;
|
|
8495
|
-
this.value =
|
|
8777
|
+
this.value = isHorizontal ?
|
|
8778
|
+
percent(mousePosScreen.x, p1, p2) :
|
|
8779
|
+
percent(mousePosScreen.y, p2, p1);
|
|
8496
8780
|
this.value === oldValue || this.onChange();
|
|
8497
8781
|
}
|
|
8498
8782
|
}
|
|
8499
8783
|
render()
|
|
8500
8784
|
{
|
|
8501
8785
|
super.render();
|
|
8502
|
-
|
|
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
|
+
|
|
8503
8793
|
// draw the scrollbar handle
|
|
8504
|
-
const
|
|
8505
|
-
const
|
|
8506
|
-
const
|
|
8507
|
-
const
|
|
8508
|
-
|
|
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))
|
|
8509
8800
|
const handleColor = this.disabled ? this.disabledColor :
|
|
8510
8801
|
this.interactive && this.isActiveObject() ? this.color : this.handleColor;
|
|
8511
|
-
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8802
|
+
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8512
8803
|
|
|
8513
8804
|
// draw the text scaled to fit on the scrollbar
|
|
8514
8805
|
const textSize = this.getTextSize();
|
|
@@ -8555,6 +8846,7 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
|
|
|
8555
8846
|
* - Each object has a Box2D body which can have multiple fixtures and joints
|
|
8556
8847
|
* - Provides interface for Box2D body and fixture functions
|
|
8557
8848
|
* @extends EngineObject
|
|
8849
|
+
* @memberof Box2D
|
|
8558
8850
|
*/
|
|
8559
8851
|
class Box2dObject extends EngineObject
|
|
8560
8852
|
{
|
|
@@ -8566,7 +8858,7 @@ class Box2dObject extends EngineObject
|
|
|
8566
8858
|
* @param {Color} [color]
|
|
8567
8859
|
* @param {number} [bodyType]
|
|
8568
8860
|
* @param {number} [renderOrder] */
|
|
8569
|
-
constructor(pos
|
|
8861
|
+
constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
8570
8862
|
{
|
|
8571
8863
|
super(pos, size, tileInfo, angle, color, renderOrder);
|
|
8572
8864
|
|
|
@@ -8580,7 +8872,7 @@ class Box2dObject extends EngineObject
|
|
|
8580
8872
|
this.lineColor = BLACK;
|
|
8581
8873
|
}
|
|
8582
8874
|
|
|
8583
|
-
/** Destroy this object and
|
|
8875
|
+
/** Destroy this object and its physics body */
|
|
8584
8876
|
destroy()
|
|
8585
8877
|
{
|
|
8586
8878
|
// destroy physics body, fixtures, and joints
|
|
@@ -9064,6 +9356,7 @@ class Box2dRaycastResult
|
|
|
9064
9356
|
* Box2D Joint
|
|
9065
9357
|
* - Base class for Box2D joints
|
|
9066
9358
|
* - A joint is used to connect objects together
|
|
9359
|
+
* @memberof Box2D
|
|
9067
9360
|
*/
|
|
9068
9361
|
class Box2dJoint
|
|
9069
9362
|
{
|
|
@@ -9119,6 +9412,7 @@ class Box2dJoint
|
|
|
9119
9412
|
* - This a soft constraint with a max force
|
|
9120
9413
|
* - This allows the constraint to stretch and without applying huge forces
|
|
9121
9414
|
* @extends Box2dJoint
|
|
9415
|
+
* @memberof Box2D
|
|
9122
9416
|
*/
|
|
9123
9417
|
class Box2dTargetJoint extends Box2dJoint
|
|
9124
9418
|
{
|
|
@@ -9168,6 +9462,7 @@ class Box2dTargetJoint extends Box2dJoint
|
|
|
9168
9462
|
* - Constrains two points on two objects to remain at a fixed distance
|
|
9169
9463
|
* - You can view this as a massless, rigid rod
|
|
9170
9464
|
* @extends Box2dJoint
|
|
9465
|
+
* @memberof Box2D
|
|
9171
9466
|
*/
|
|
9172
9467
|
class Box2dDistanceJoint extends Box2dJoint
|
|
9173
9468
|
{
|
|
@@ -9231,6 +9526,7 @@ class Box2dDistanceJoint extends Box2dJoint
|
|
|
9231
9526
|
* Box2D Pin Joint
|
|
9232
9527
|
* - Pins two objects together at a point
|
|
9233
9528
|
* @extends Box2dDistanceJoint
|
|
9529
|
+
* @memberof Box2D
|
|
9234
9530
|
*/
|
|
9235
9531
|
class Box2dPinJoint extends Box2dDistanceJoint
|
|
9236
9532
|
{
|
|
@@ -9250,6 +9546,7 @@ class Box2dPinJoint extends Box2dDistanceJoint
|
|
|
9250
9546
|
* Box2D Rope Joint
|
|
9251
9547
|
* - Enforces a maximum distance between two points on two objects
|
|
9252
9548
|
* @extends Box2dJoint
|
|
9549
|
+
* @memberof Box2D
|
|
9253
9550
|
*/
|
|
9254
9551
|
class Box2dRopeJoint extends Box2dJoint
|
|
9255
9552
|
{
|
|
@@ -9302,6 +9599,7 @@ class Box2dRopeJoint extends Box2dJoint
|
|
|
9302
9599
|
* - You can use a motor to drive the relative rotation about the shared point
|
|
9303
9600
|
* - A maximum motor torque is provided so that infinite forces are not generated
|
|
9304
9601
|
* @extends Box2dJoint
|
|
9602
|
+
* @memberof Box2D
|
|
9305
9603
|
*/
|
|
9306
9604
|
class Box2dRevoluteJoint extends Box2dJoint
|
|
9307
9605
|
{
|
|
@@ -9403,6 +9701,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
9403
9701
|
* - Either joint can be a revolute or prismatic joint
|
|
9404
9702
|
* - You specify a gear ratio to bind the motions together
|
|
9405
9703
|
* @extends Box2dJoint
|
|
9704
|
+
* @memberof Box2D
|
|
9406
9705
|
*/
|
|
9407
9706
|
class Box2dGearJoint extends Box2dJoint
|
|
9408
9707
|
{
|
|
@@ -9451,6 +9750,7 @@ class Box2dGearJoint extends Box2dJoint
|
|
|
9451
9750
|
* - You can use a joint limit to restrict the range of motion
|
|
9452
9751
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
9453
9752
|
* @extends Box2dJoint
|
|
9753
|
+
* @memberof Box2D
|
|
9454
9754
|
*/
|
|
9455
9755
|
class Box2dPrismaticJoint extends Box2dJoint
|
|
9456
9756
|
{
|
|
@@ -9560,6 +9860,7 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
9560
9860
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
9561
9861
|
* - This joint is designed for vehicle suspensions
|
|
9562
9862
|
* @extends Box2dJoint
|
|
9863
|
+
* @memberof Box2D
|
|
9563
9864
|
*/
|
|
9564
9865
|
class Box2dWheelJoint extends Box2dJoint
|
|
9565
9866
|
{
|
|
@@ -9655,6 +9956,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
9655
9956
|
* Box2D Weld Joint
|
|
9656
9957
|
* - Glues two objects together
|
|
9657
9958
|
* @extends Box2dJoint
|
|
9959
|
+
* @memberof Box2D
|
|
9658
9960
|
*/
|
|
9659
9961
|
class Box2dWeldJoint extends Box2dJoint
|
|
9660
9962
|
{
|
|
@@ -9713,6 +10015,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
9713
10015
|
* - Used to apply top-down friction
|
|
9714
10016
|
* - Provides 2D translational friction and angular friction
|
|
9715
10017
|
* @extends Box2dJoint
|
|
10018
|
+
* @memberof Box2D
|
|
9716
10019
|
*/
|
|
9717
10020
|
class Box2dFrictionJoint extends Box2dJoint
|
|
9718
10021
|
{
|
|
@@ -9767,6 +10070,7 @@ class Box2dFrictionJoint extends Box2dJoint
|
|
|
9767
10070
|
* - The pulley supports a ratio such that: length1 + ratio * length2 <= constant
|
|
9768
10071
|
* - The force transmitted is scaled by the ratio
|
|
9769
10072
|
* @extends Box2dJoint
|
|
10073
|
+
* @memberof Box2D
|
|
9770
10074
|
*/
|
|
9771
10075
|
class Box2dPulleyJoint extends Box2dJoint
|
|
9772
10076
|
{
|
|
@@ -9834,6 +10138,7 @@ class Box2dPulleyJoint extends Box2dJoint
|
|
|
9834
10138
|
* - Controls the relative motion between two objects
|
|
9835
10139
|
* - Typical usage is to control the movement of a object with respect to the ground
|
|
9836
10140
|
* @extends Box2dJoint
|
|
10141
|
+
* @memberof Box2D
|
|
9837
10142
|
*/
|
|
9838
10143
|
class Box2dMotorJoint extends Box2dJoint
|
|
9839
10144
|
{
|
|
@@ -9897,6 +10202,7 @@ class Box2dMotorJoint extends Box2dJoint
|
|
|
9897
10202
|
/**
|
|
9898
10203
|
* Box2D Global Object
|
|
9899
10204
|
* - Wraps Box2d world and provides global functions
|
|
10205
|
+
* @memberof Box2D
|
|
9900
10206
|
*/
|
|
9901
10207
|
class Box2dPlugin
|
|
9902
10208
|
{
|
|
@@ -10615,6 +10921,7 @@ export
|
|
|
10615
10921
|
isColor,
|
|
10616
10922
|
isVector2,
|
|
10617
10923
|
isNumber,
|
|
10924
|
+
isString,
|
|
10618
10925
|
|
|
10619
10926
|
// Default Colors
|
|
10620
10927
|
WHITE,
|
|
@@ -10750,7 +11057,7 @@ export
|
|
|
10750
11057
|
tileCollisionGetData,
|
|
10751
11058
|
tileCollisionTest,
|
|
10752
11059
|
tileCollisionRaycast,
|
|
10753
|
-
|
|
11060
|
+
tileLayersLoad,
|
|
10754
11061
|
TileLayerData,
|
|
10755
11062
|
CanvasLayer,
|
|
10756
11063
|
TileLayer,
|