littlejsengine 1.17.11 → 1.17.15
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/AI.md +162 -0
- package/README.md +50 -63
- package/dist/littlejs.d.ts +150 -68
- package/dist/littlejs.esm.js +323 -105
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +320 -104
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +318 -101
- package/examples/electron/{build.js → build.mjs} +26 -23
- package/examples/electron/game.js +5 -50
- package/examples/electron/index.html +2 -2
- package/examples/index.html +1 -0
- package/examples/module/{build.js → build.mjs} +22 -19
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/hillGlideGame.js +1 -1
- package/examples/shorts/nineSlice.js +3 -3
- package/examples/shorts/shapes.js +1 -1
- package/examples/shorts/uiSystem.js +17 -16
- package/examples/starter/build.bat +1 -1
- package/examples/starter/{build.js → build.mjs} +25 -22
- package/examples/starter/index.html +2 -2
- package/examples/typescript/build.mjs +60 -0
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/tsconfig.json +11 -2
- package/package.json +2 -2
- package/plugins/pluginExport.js +1 -0
- package/plugins/uiSystem.js +143 -20
- package/reference.md +0 -1
- package/src/engine.js +16 -14
- package/src/engineAudio.js +9 -6
- package/src/{engineBuild.js → engineBuild.mjs} +95 -78
- package/src/engineDebug.js +8 -5
- package/src/engineDraw.js +16 -14
- package/src/engineExport.js +2 -1
- package/src/engineInput.js +29 -8
- package/src/engineMath.js +46 -14
- package/src/engineMedals.js +6 -3
- package/src/engineObject.js +14 -3
- package/src/engineParticles.js +8 -3
- package/src/engineRelease.js +6 -2
- package/src/engineTileLayer.js +8 -4
- package/src/engineUtilities.js +6 -2
- package/src/engineWebGL.js +11 -8
- package/src/jsconfig.json +2 -1
- package/examples/electron/build.bat +0 -7
- package/examples/module/build.bat +0 -7
- package/examples/typescript/build.bat +0 -5
- package/examples/typescript/build.js +0 -31
package/dist/littlejs.js
CHANGED
|
@@ -8,18 +8,20 @@
|
|
|
8
8
|
* MIT License - Copyright 2021 Frank Force
|
|
9
9
|
*
|
|
10
10
|
* Engine Features
|
|
11
|
-
* - Object oriented system with base class
|
|
12
|
-
* -
|
|
13
|
-
* - Engine helper classes
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* - Input
|
|
17
|
-
* - Tile layer rendering and collision
|
|
18
|
-
* - Particle effect system
|
|
19
|
-
* - Medal system
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
22
|
-
* -
|
|
11
|
+
* - Object oriented system with EngineObject base class
|
|
12
|
+
* - Automatic object lifecycle (update, physics, collision, rendering)
|
|
13
|
+
* - Engine helper classes: Vector2, Color, Timer, RandomGenerator
|
|
14
|
+
* - Hybrid rendering with WebGL batching and Canvas2D fallback
|
|
15
|
+
* - Audio system with wave, mp3, or ZzFX sound effects
|
|
16
|
+
* - Input system with keyboard, mouse, gamepad, and touch support
|
|
17
|
+
* - Tile layer rendering and collision detection
|
|
18
|
+
* - Particle effect system with emitters
|
|
19
|
+
* - Medal/achievement system with local storage
|
|
20
|
+
* - Comprehensive debug tools and visualizations
|
|
21
|
+
* - Fixed 60 FPS timestep with configurable time scale
|
|
22
|
+
* - Raycast and spatial query utilities
|
|
23
|
+
* - Plugin system for extending engine functionality
|
|
24
|
+
* - Start with engineInit() and provide your game callbacks
|
|
23
25
|
* @namespace Engine
|
|
24
26
|
*/
|
|
25
27
|
|
|
@@ -33,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
33
35
|
* @type {string}
|
|
34
36
|
* @default
|
|
35
37
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.17.
|
|
38
|
+
const engineVersion = '1.17.15';
|
|
37
39
|
|
|
38
40
|
/** Frames per second to update
|
|
39
41
|
* @type {number}
|
|
@@ -607,7 +609,7 @@ function drawEngineLogo(t)
|
|
|
607
609
|
const color = (c,l)=> l?`hsl(${[.95,.56,.13][c%3]*360} 99%${[0,50,75][l]}%`:'#000';
|
|
608
610
|
|
|
609
611
|
// center and fit tos screen
|
|
610
|
-
const alpha =
|
|
612
|
+
const alpha = oscillate(1,1,t);
|
|
611
613
|
const p = percent(alpha, .1, .5);
|
|
612
614
|
const size = min(6, min(w,h)/99);
|
|
613
615
|
x.translate(w/2,h/2);
|
|
@@ -685,11 +687,14 @@ function drawEngineLogo(t)
|
|
|
685
687
|
}
|
|
686
688
|
/**
|
|
687
689
|
* LittleJS Debug System
|
|
688
|
-
* - Press Esc to
|
|
689
|
-
* - Number keys toggle debug
|
|
690
|
-
* - +/-
|
|
691
|
-
* -
|
|
692
|
-
* -
|
|
690
|
+
* - Press Esc to toggle debug overlay with object picking
|
|
691
|
+
* - Number keys toggle debug visualizations (physics, particles, etc.)
|
|
692
|
+
* - +/- keys control time scale for slow motion/fast forward
|
|
693
|
+
* - ASSERT and LOG macros for development (removed in release builds)
|
|
694
|
+
* - Debug primitive rendering (rectangles, circles, lines, points, text)
|
|
695
|
+
* - Screenshot and video capture support
|
|
696
|
+
* - FPS counter and performance watermark
|
|
697
|
+
* - Debug overlay shows mouse position and picked objects
|
|
693
698
|
* @namespace Debug
|
|
694
699
|
*/
|
|
695
700
|
|
|
@@ -1404,10 +1409,17 @@ function debugProtectConstant(obj)
|
|
|
1404
1409
|
}
|
|
1405
1410
|
/**
|
|
1406
1411
|
* LittleJS Math Classes and Functions
|
|
1407
|
-
* -
|
|
1408
|
-
* -
|
|
1409
|
-
* -
|
|
1410
|
-
* -
|
|
1412
|
+
* - Comprehensive math utilities for game development
|
|
1413
|
+
* - Vector2 class for 2D positions, directions, and math operations
|
|
1414
|
+
* - Color class for RGBA colors with interpolation and manipulation
|
|
1415
|
+
* - RandomGenerator for seeded pseudo-random number generation
|
|
1416
|
+
* - Math shortcuts (PI, abs, floor, ceil, min, max, sin, cos, etc.)
|
|
1417
|
+
* - Interpolation functions (lerp, smoothStep, percent)
|
|
1418
|
+
* - Clamping, wrapping, and modulo operations
|
|
1419
|
+
* - Angle utilities with wrap-around support
|
|
1420
|
+
* - Collision detection (overlapping, intersection, line tests)
|
|
1421
|
+
* - Random number generation and seeding
|
|
1422
|
+
* - Type checking utilities
|
|
1411
1423
|
* @namespace Math
|
|
1412
1424
|
*/
|
|
1413
1425
|
|
|
@@ -1656,10 +1668,24 @@ function isIntersecting(start, end, pos, size)
|
|
|
1656
1668
|
* @param {number} [amplitude] - Amplitude (max height) of the wave
|
|
1657
1669
|
* @param {number} [t=time] - Value to use for time of the wave
|
|
1658
1670
|
* @param {number} [offset] - Value to use for time offset of the wave
|
|
1671
|
+
* @param {number} [type] - Wave type: 0=sine, 1=triangle, 2=square, 3=sawtooth
|
|
1659
1672
|
* @return {number} - Value waving between 0 and amplitude
|
|
1660
1673
|
* @memberof Math */
|
|
1661
|
-
function
|
|
1662
|
-
{
|
|
1674
|
+
function oscillate(frequency=1, amplitude=1, t=time, offset=0, type=0)
|
|
1675
|
+
{
|
|
1676
|
+
const phase = (offset + t*frequency) % 1;
|
|
1677
|
+
let value;
|
|
1678
|
+
|
|
1679
|
+
if (type === 1) // triangle
|
|
1680
|
+
value = 2 * abs(2 * phase - 1) - 1;
|
|
1681
|
+
else if (type === 2) // square
|
|
1682
|
+
value = phase < .5 ? -1 : 1;
|
|
1683
|
+
else if (type === 3) // sawtooth
|
|
1684
|
+
value = 2 * phase - 1;
|
|
1685
|
+
else // sine
|
|
1686
|
+
value = -cos(phase * 2*PI);
|
|
1687
|
+
return amplitude/2 * (value + 1);
|
|
1688
|
+
}
|
|
1663
1689
|
|
|
1664
1690
|
/**
|
|
1665
1691
|
* Check if object is a valid number, not NaN or undefined, but it may be infinite
|
|
@@ -1741,15 +1767,17 @@ function lineTest(posStart, posEnd, testFunction, normal)
|
|
|
1741
1767
|
// set hit point
|
|
1742
1768
|
const hitPos = vec2(posStart.x + dirX*t, posStart.y + dirY*t);
|
|
1743
1769
|
|
|
1744
|
-
//
|
|
1770
|
+
// ensure result is inside the tile
|
|
1745
1771
|
const e = 1e-9;
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
hitPos.y
|
|
1772
|
+
const hitPosFloor = hitPos.floor();
|
|
1773
|
+
if (hitPosFloor.x < pos.x)
|
|
1774
|
+
hitPos.x = pos.x;
|
|
1775
|
+
else if (hitPosFloor.x > pos.x)
|
|
1776
|
+
hitPos.x = pos.x + 1 - e;
|
|
1777
|
+
if (hitPosFloor.y < pos.y)
|
|
1778
|
+
hitPos.y = pos.y;
|
|
1779
|
+
else if (hitPosFloor.y > pos.y)
|
|
1780
|
+
hitPos.y = pos.y + 1 - e;
|
|
1753
1781
|
|
|
1754
1782
|
// set normal
|
|
1755
1783
|
if (normal)
|
|
@@ -2142,6 +2170,15 @@ class Vector2
|
|
|
2142
2170
|
* @return {Vector2} */
|
|
2143
2171
|
floor() { return new Vector2(floor(this.x), floor(this.y)); }
|
|
2144
2172
|
|
|
2173
|
+
/** Returns a copy of this vector snapped to a grid
|
|
2174
|
+
* @param {number} grid - grid size to snap to
|
|
2175
|
+
* @return {Vector2} */
|
|
2176
|
+
snap(grid)
|
|
2177
|
+
{
|
|
2178
|
+
ASSERT_NUMBER_VALID(grid);
|
|
2179
|
+
return new Vector2(floor(this.x*grid)/grid, floor(this.y*grid)/grid);
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2145
2182
|
/** Returns new vec2 with modded values
|
|
2146
2183
|
* @param {number} [divisor]
|
|
2147
2184
|
* @return {Vector2} */
|
|
@@ -2516,8 +2553,12 @@ const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
|
2516
2553
|
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
2517
2554
|
/**
|
|
2518
2555
|
* LittleJS Utility Classes and Functions
|
|
2519
|
-
* -
|
|
2520
|
-
* -
|
|
2556
|
+
* - Timer - tracks time automatically with support for pause and real-time modes
|
|
2557
|
+
* - Time formatting helper
|
|
2558
|
+
* - JSON file fetching
|
|
2559
|
+
* - File saving (text, canvas, data URLs)
|
|
2560
|
+
* - Native share dialog support
|
|
2561
|
+
* - Local storage save data management
|
|
2521
2562
|
* @namespace Utilities
|
|
2522
2563
|
*/
|
|
2523
2564
|
|
|
@@ -3310,6 +3351,13 @@ function setDebugWatermark(show) { debugWatermark = show; }
|
|
|
3310
3351
|
function setDebugKey(key) { debugKey = key; }
|
|
3311
3352
|
/**
|
|
3312
3353
|
* LittleJS Object System
|
|
3354
|
+
* - EngineObject is the base class for all game objects
|
|
3355
|
+
* - Handles automatic updating, rendering, physics, and collision
|
|
3356
|
+
* - Supports parent-child hierarchies with transform inheritance
|
|
3357
|
+
* - 2D physics with velocity, acceleration, damping, and gravity
|
|
3358
|
+
* - Collision system with tiles and other objects
|
|
3359
|
+
* - Renders sprites from tile sheets with color and rotation
|
|
3360
|
+
* - Objects sorted by renderOrder for layered rendering
|
|
3313
3361
|
*/
|
|
3314
3362
|
|
|
3315
3363
|
/**
|
|
@@ -3822,33 +3870,38 @@ class EngineObject
|
|
|
3822
3870
|
{
|
|
3823
3871
|
if (!debug) return;
|
|
3824
3872
|
|
|
3873
|
+
// check if there is anything to show
|
|
3874
|
+
const hasPhysics = this.collideTiles || this.collideSolidObjects || this.isSolid;
|
|
3875
|
+
if (!hasPhysics && !this.parent) return;
|
|
3876
|
+
|
|
3825
3877
|
// show object info for debugging
|
|
3826
3878
|
const size = vec2(max(this.size.x, .2), max(this.size.y, .2));
|
|
3827
3879
|
const color = rgb(this.collideTiles?1:0, this.collideSolidObjects?1:0, this.isSolid?1:0, .5);
|
|
3828
|
-
|
|
3880
|
+
debugRect(this.pos, size, color, 0, this.angle, hasPhysics);
|
|
3829
3881
|
if (this.parent)
|
|
3830
|
-
|
|
3831
|
-
this.parent &&
|
|
3882
|
+
debugRect(this.pos, size.scale(.8), rgb(1,1,1,.5), 0, this.angle);
|
|
3883
|
+
this.parent && debugLine(this.pos, this.parent.pos, rgb(1,1,1,.5), .5);
|
|
3832
3884
|
}
|
|
3833
3885
|
}
|
|
3834
3886
|
/**
|
|
3835
3887
|
* LittleJS Drawing System
|
|
3836
|
-
* - Hybrid
|
|
3837
|
-
* -
|
|
3838
|
-
* -
|
|
3839
|
-
* -
|
|
3840
|
-
* -
|
|
3888
|
+
* - Hybrid rendering with both Canvas2D and WebGL support
|
|
3889
|
+
* - Optimized tile sheet sprite rendering using WebGL batching
|
|
3890
|
+
* - Primitive drawing for polygons, ellipses, and lines
|
|
3891
|
+
* - Tile-based rendering with TileInfo and TextureInfo classes
|
|
3892
|
+
* - Text rendering with custom fonts and FontImage support
|
|
3893
|
+
* - Color and additive color blending for effects
|
|
3894
|
+
* - Rotation, mirroring, and scaling transformations
|
|
3895
|
+
* - Camera system with position, scale, and rotation
|
|
3896
|
+
* - Multiple canvas support (main, WebGL, work canvases)
|
|
3897
|
+
* - Gradient fills and outlined shapes
|
|
3898
|
+
* - Image manipulation and color tinting
|
|
3841
3899
|
*
|
|
3842
|
-
*
|
|
3843
|
-
*
|
|
3844
|
-
* mainCanvas
|
|
3845
|
-
*
|
|
3900
|
+
* Rendering Architecture:
|
|
3901
|
+
* - glCanvas: WebGL canvas for accelerated sprite batch rendering
|
|
3902
|
+
* - mainCanvas: Canvas2D overlay for text, UI, and custom drawing
|
|
3903
|
+
* - All draw functions default to WebGL when enabled, can force Canvas2D with useWebGL parameter
|
|
3846
3904
|
*
|
|
3847
|
-
* The WebGL rendering system is very fast with some caveats...
|
|
3848
|
-
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
3849
|
-
* - Group additive rendering together using renderOrder to mitigate this issue
|
|
3850
|
-
*
|
|
3851
|
-
* The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
|
|
3852
3905
|
* @namespace Draw
|
|
3853
3906
|
*/
|
|
3854
3907
|
|
|
@@ -3939,6 +3992,7 @@ function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDe
|
|
|
3939
3992
|
// create tile info object
|
|
3940
3993
|
const textureInfo = typeof texture === 'number' ?
|
|
3941
3994
|
textureInfos[texture] : texture;
|
|
3995
|
+
ASSERT(textureInfo instanceof TextureInfo, 'tile texture is not loaded');
|
|
3942
3996
|
|
|
3943
3997
|
// get the position of the tile
|
|
3944
3998
|
const sizePaddedX = size.x + padding*2;
|
|
@@ -4982,11 +5036,14 @@ async function fontImageInit()
|
|
|
4982
5036
|
}
|
|
4983
5037
|
/**
|
|
4984
5038
|
* LittleJS Input System
|
|
4985
|
-
* -
|
|
4986
|
-
* -
|
|
4987
|
-
* -
|
|
4988
|
-
* - Touch input
|
|
4989
|
-
* - Virtual gamepad for
|
|
5039
|
+
* - Keyboard input with key down, pressed, and released states
|
|
5040
|
+
* - Mouse input with position (world and screen space), buttons, and wheel
|
|
5041
|
+
* - Gamepad support for multiple controllers with analog sticks and buttons
|
|
5042
|
+
* - Touch input mapped to mouse position and buttons
|
|
5043
|
+
* - Virtual on-screen gamepad for mobile devices
|
|
5044
|
+
* - Automatic gamepad vs keyboard/mouse detection
|
|
5045
|
+
* - Input event prevention for canvas focus
|
|
5046
|
+
* - Clipboard copy/paste support
|
|
4990
5047
|
* @namespace Input
|
|
4991
5048
|
*/
|
|
4992
5049
|
|
|
@@ -5316,7 +5373,19 @@ function inputInit()
|
|
|
5316
5373
|
inputData[0][remapKey(e.code)] = 3;
|
|
5317
5374
|
}
|
|
5318
5375
|
|
|
5319
|
-
// prevent
|
|
5376
|
+
// try to prevent default browser handling of input
|
|
5377
|
+
if (!inputPreventDefault || !document.hasFocus() || !e.cancelable) return;
|
|
5378
|
+
|
|
5379
|
+
// don't break browser shortcuts
|
|
5380
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
5381
|
+
|
|
5382
|
+
// don't interfere with user typing into UI fields
|
|
5383
|
+
if (isTextInput(e.target) || isTextInput(document.activeElement)) return;
|
|
5384
|
+
|
|
5385
|
+
// fix browser setting "Search for text when you start typing"
|
|
5386
|
+
const printable = typeof e.key === 'string' && e.key.length === 1;
|
|
5387
|
+
|
|
5388
|
+
// prevent arrow key and other default keys from messing with stuff
|
|
5320
5389
|
const preventDefaultKeys =
|
|
5321
5390
|
[
|
|
5322
5391
|
'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', // scrolling
|
|
@@ -5324,9 +5393,15 @@ function inputInit()
|
|
|
5324
5393
|
'Tab', // focus navigation
|
|
5325
5394
|
'Backspace', // browser back
|
|
5326
5395
|
];
|
|
5327
|
-
if (preventDefaultKeys.includes(e.code))
|
|
5328
|
-
if (inputPreventDefault && document.hasFocus() && e.cancelable)
|
|
5396
|
+
if (preventDefaultKeys.includes(e.code) || printable)
|
|
5329
5397
|
e.preventDefault();
|
|
5398
|
+
|
|
5399
|
+
function isTextInput(element)
|
|
5400
|
+
{
|
|
5401
|
+
const tag = element?.tagName;
|
|
5402
|
+
const editable = element?.isContentEditable;
|
|
5403
|
+
return editable || ['INPUT','TEXTAREA','SELECT'].includes(tag);
|
|
5404
|
+
}
|
|
5330
5405
|
}
|
|
5331
5406
|
function onKeyUp(e)
|
|
5332
5407
|
{
|
|
@@ -5759,12 +5834,15 @@ function touchGamepadButtonCenter()
|
|
|
5759
5834
|
}
|
|
5760
5835
|
/**
|
|
5761
5836
|
* LittleJS Audio System
|
|
5762
|
-
* -
|
|
5763
|
-
* - <a href=https://
|
|
5764
|
-
* -
|
|
5765
|
-
* -
|
|
5766
|
-
* -
|
|
5767
|
-
* -
|
|
5837
|
+
* - Play audio files (mp3, ogg, wave) and generate sounds with ZzFX
|
|
5838
|
+
* - ZzFX sound generator integration: <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX</a>
|
|
5839
|
+
* - Sound caching for fast playback and memory efficiency
|
|
5840
|
+
* - Volume control with attenuation and stereo panning
|
|
5841
|
+
* - 2D spatial audio based on camera position with distance-based falloff
|
|
5842
|
+
* - Sound instance management (pause, resume, stop)
|
|
5843
|
+
* - Speech synthesis for text-to-speech
|
|
5844
|
+
* - Music playback with ZzFXM support
|
|
5845
|
+
* - Web Audio API integration with master gain control
|
|
5768
5846
|
* @namespace Audio
|
|
5769
5847
|
*/
|
|
5770
5848
|
|
|
@@ -6433,10 +6511,14 @@ function zzfxG
|
|
|
6433
6511
|
}
|
|
6434
6512
|
/**
|
|
6435
6513
|
* LittleJS Tile Layer System
|
|
6436
|
-
* -
|
|
6437
|
-
* - Unlimited
|
|
6438
|
-
* -
|
|
6439
|
-
* -
|
|
6514
|
+
* - Renders large tile-based levels efficiently using cached canvases
|
|
6515
|
+
* - Unlimited tile layers with automatic canvas allocation
|
|
6516
|
+
* - Layers support both rendering and collision detection
|
|
6517
|
+
* - Direct canvas2d drawing access for custom tile rendering
|
|
6518
|
+
* - TileLayer for rendering, TileCollisionLayer for physics
|
|
6519
|
+
* - Collision callbacks for tile interactions with objects
|
|
6520
|
+
* - Optimized raycast support for tile-based physics
|
|
6521
|
+
* - Integration with Box2D physics via Box2DTileLayer plugin
|
|
6440
6522
|
* @namespace TileLayers
|
|
6441
6523
|
*/
|
|
6442
6524
|
|
|
@@ -7141,9 +7223,14 @@ class TileCollisionLayer extends TileLayer
|
|
|
7141
7223
|
}
|
|
7142
7224
|
/**
|
|
7143
7225
|
* LittleJS Particle System
|
|
7144
|
-
* -
|
|
7145
|
-
* -
|
|
7146
|
-
* -
|
|
7226
|
+
* - Fast and flexible particle effects system
|
|
7227
|
+
* - ParticleEmitter spawns and manages lightweight Particle objects
|
|
7228
|
+
* - Particles support color gradients, fading, rotation, and scaling
|
|
7229
|
+
* - Physics simulation with velocity, gravity, and damping
|
|
7230
|
+
* - Collision detection with tile layers
|
|
7231
|
+
* - Additive blending for glowing effects
|
|
7232
|
+
* - Cone-based emission with randomization
|
|
7233
|
+
* - Particle design tool available for easy emitter creation
|
|
7147
7234
|
* @namespace Particles
|
|
7148
7235
|
*/
|
|
7149
7236
|
|
|
@@ -7662,9 +7749,12 @@ class Particle
|
|
|
7662
7749
|
}
|
|
7663
7750
|
/**
|
|
7664
7751
|
* LittleJS Medal System
|
|
7665
|
-
* -
|
|
7666
|
-
* -
|
|
7667
|
-
* -
|
|
7752
|
+
* - Achievement/trophy system for games
|
|
7753
|
+
* - Medal class with name, description, icon, and unlock tracking
|
|
7754
|
+
* - Automatic saving to local storage
|
|
7755
|
+
* - Visual display queue with slide-in notifications
|
|
7756
|
+
* - Newgrounds API integration for online achievements
|
|
7757
|
+
* - Debug mode to unlock/reset medals during development
|
|
7668
7758
|
* @namespace Medals
|
|
7669
7759
|
*/
|
|
7670
7760
|
|
|
@@ -7853,14 +7943,17 @@ class Medal
|
|
|
7853
7943
|
}
|
|
7854
7944
|
/**
|
|
7855
7945
|
* LittleJS WebGL Interface
|
|
7856
|
-
* -
|
|
7857
|
-
* -
|
|
7858
|
-
* -
|
|
7859
|
-
* -
|
|
7860
|
-
* -
|
|
7861
|
-
* -
|
|
7862
|
-
* -
|
|
7863
|
-
* -
|
|
7946
|
+
* - WebGL2 rendering engine for high-performance graphics
|
|
7947
|
+
* - Batched sprite rendering for drawing thousands of sprites efficiently
|
|
7948
|
+
* - Instanced rendering using vertex array objects (VAOs)
|
|
7949
|
+
* - Polygon rendering with triangle strip support
|
|
7950
|
+
* - Shader system with custom vertex and fragment shaders
|
|
7951
|
+
* - Texture management with automatic atlas support
|
|
7952
|
+
* - Post-processing effects via framebuffer and shader plugins
|
|
7953
|
+
* - Automatic fallback to Canvas2D if WebGL is unavailable
|
|
7954
|
+
* - Context loss and restoration handling
|
|
7955
|
+
* - Can be disabled with glEnable setting
|
|
7956
|
+
* - Advanced users can create custom shaders and render targets
|
|
7864
7957
|
* @namespace WebGL
|
|
7865
7958
|
*/
|
|
7866
7959
|
|
|
@@ -9351,9 +9444,16 @@ class UISystemPlugin
|
|
|
9351
9444
|
this.lastHoverObject = undefined;
|
|
9352
9445
|
/** @property {UIObject} - Current confirm menu being shown */
|
|
9353
9446
|
this.confirmDialog = undefined;
|
|
9447
|
+
/** @property {UIObject} - Object to send keyboard input to */
|
|
9448
|
+
this.keyInputObject = undefined;
|
|
9354
9449
|
|
|
9355
9450
|
engineAddPlugin(uiUpdate, uiRender);
|
|
9356
9451
|
|
|
9452
|
+
// key down handler
|
|
9453
|
+
function onKeyDown(e)
|
|
9454
|
+
{ uiSystem.keyInputObject?.onKeyDown(e); }
|
|
9455
|
+
document.addEventListener('keydown', onKeyDown);
|
|
9456
|
+
|
|
9357
9457
|
// set object position in parent space
|
|
9358
9458
|
function updateTransforms(o)
|
|
9359
9459
|
{
|
|
@@ -9372,9 +9472,18 @@ class UISystemPlugin
|
|
|
9372
9472
|
// reset hover object at start of update
|
|
9373
9473
|
uiSystem.lastHoverObject = uiSystem.hoverObject;
|
|
9374
9474
|
uiSystem.hoverObject = undefined;
|
|
9375
|
-
|
|
9475
|
+
|
|
9376
9476
|
if (mouseWasPressed(0))
|
|
9377
9477
|
{
|
|
9478
|
+
// exit navigation mode on mouse press
|
|
9479
|
+
uiSystem.navigationMode = false;
|
|
9480
|
+
uiSystem.navigationObject = undefined;
|
|
9481
|
+
}
|
|
9482
|
+
if (uiSystem.keyInputObject)
|
|
9483
|
+
{
|
|
9484
|
+
// handle text input
|
|
9485
|
+
uiSystem.activeObject = uiSystem.keyInputObject;
|
|
9486
|
+
uiSystem.hoverObject = uiSystem.keyInputObject;
|
|
9378
9487
|
uiSystem.navigationMode = false;
|
|
9379
9488
|
uiSystem.navigationObject = undefined;
|
|
9380
9489
|
}
|
|
@@ -9383,7 +9492,7 @@ class UISystemPlugin
|
|
|
9383
9492
|
const navigableObjects = uiSystem.getNavigableObjects();
|
|
9384
9493
|
if (!navigableObjects.length)
|
|
9385
9494
|
uiSystem.navigationObject = undefined;
|
|
9386
|
-
else
|
|
9495
|
+
else if (!uiSystem.keyInputObject)
|
|
9387
9496
|
{
|
|
9388
9497
|
// unselect object if it is no longer navigable
|
|
9389
9498
|
if (!navigableObjects.includes(uiSystem.navigationObject))
|
|
@@ -9696,6 +9805,7 @@ class UISystemPlugin
|
|
|
9696
9805
|
this.activeObject = undefined;
|
|
9697
9806
|
this.hoverObject = undefined;
|
|
9698
9807
|
this.lastHoverObject = undefined;
|
|
9808
|
+
this.keyInputObject = undefined;
|
|
9699
9809
|
}
|
|
9700
9810
|
|
|
9701
9811
|
/** Get all navigable UI objects sorted by navigationIndex
|
|
@@ -9976,7 +10086,7 @@ class UIObject
|
|
|
9976
10086
|
}
|
|
9977
10087
|
}
|
|
9978
10088
|
|
|
9979
|
-
/** Check if the mouse is overlapping
|
|
10089
|
+
/** Check if the mouse is overlapping this ui object
|
|
9980
10090
|
* @return {boolean} - True if overlapping */
|
|
9981
10091
|
isMouseOverlapping()
|
|
9982
10092
|
{
|
|
@@ -9995,8 +10105,16 @@ class UIObject
|
|
|
9995
10105
|
this.onUpdate();
|
|
9996
10106
|
|
|
9997
10107
|
// unset active if disabled
|
|
9998
|
-
if (this.disabled
|
|
9999
|
-
|
|
10108
|
+
if (this.disabled)
|
|
10109
|
+
{
|
|
10110
|
+
if (this === uiSystem.activeObject)
|
|
10111
|
+
uiSystem.activeObject = undefined;
|
|
10112
|
+
if (this === uiSystem.keyInputObject)
|
|
10113
|
+
uiSystem.keyInputObject = undefined;
|
|
10114
|
+
}
|
|
10115
|
+
|
|
10116
|
+
if (uiSystem.keyInputObject)
|
|
10117
|
+
return;
|
|
10000
10118
|
|
|
10001
10119
|
const wasHover = uiSystem.lastHoverObject === this;
|
|
10002
10120
|
const isActive = this.isActiveObject();
|
|
@@ -10023,19 +10141,12 @@ class UIObject
|
|
|
10023
10141
|
uiSystem.activeObject = this;
|
|
10024
10142
|
|
|
10025
10143
|
if (uiSystem.activateOnPress)
|
|
10026
|
-
|
|
10027
|
-
this.onClick();
|
|
10028
|
-
if (!this.soundPress && this.soundClick)
|
|
10029
|
-
this.soundClick.play();
|
|
10030
|
-
}
|
|
10144
|
+
this.click(!this.soundPress);
|
|
10031
10145
|
}
|
|
10032
10146
|
}
|
|
10033
10147
|
if (!uiSystem.activateOnPress)
|
|
10034
10148
|
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
10035
|
-
|
|
10036
|
-
this.onClick();
|
|
10037
|
-
this.soundClick && this.soundClick.play();
|
|
10038
|
-
}
|
|
10149
|
+
this.click();
|
|
10039
10150
|
}
|
|
10040
10151
|
|
|
10041
10152
|
// clear mouse was pressed state even when disabled
|
|
@@ -10087,11 +10198,7 @@ class UIObject
|
|
|
10087
10198
|
}
|
|
10088
10199
|
|
|
10089
10200
|
/** Called when the navigation button is pressed on this object */
|
|
10090
|
-
navigatePressed()
|
|
10091
|
-
{
|
|
10092
|
-
this.onClick();
|
|
10093
|
-
this.soundClick && this.soundClick.play();
|
|
10094
|
-
}
|
|
10201
|
+
navigatePressed() { this.click(); }
|
|
10095
10202
|
|
|
10096
10203
|
/** @return {boolean} - Is the mouse hovering over this element */
|
|
10097
10204
|
isHoverObject() { return uiSystem.hoverObject === this; }
|
|
@@ -10102,6 +10209,9 @@ class UIObject
|
|
|
10102
10209
|
/** @return {boolean} - Is the gamepad or keyboard navigation object */
|
|
10103
10210
|
isNavigationObject() { return uiSystem.navigationObject === this; }
|
|
10104
10211
|
|
|
10212
|
+
/** @return {boolean} - Is this object in keyboard input mode */
|
|
10213
|
+
isKeyInputObject() { return uiSystem.keyInputObject === this; }
|
|
10214
|
+
|
|
10105
10215
|
/** @return {boolean} - Can it be interacted with */
|
|
10106
10216
|
isInteractive() { return this.interactive && this.visible && !this.disabled;}
|
|
10107
10217
|
|
|
@@ -10138,6 +10248,15 @@ class UIObject
|
|
|
10138
10248
|
uiSystem.drawRect(this.pos, this.size, CLEAR_BLACK, 4, color);
|
|
10139
10249
|
}
|
|
10140
10250
|
|
|
10251
|
+
/** Internal function called when object is clicked
|
|
10252
|
+
* @param {boolean} [playSound] */
|
|
10253
|
+
click(playSound)
|
|
10254
|
+
{
|
|
10255
|
+
this.onClick();
|
|
10256
|
+
if (playSound && this.soundClick)
|
|
10257
|
+
this.soundClick.play();
|
|
10258
|
+
}
|
|
10259
|
+
|
|
10141
10260
|
/** Called each frame before object updates */
|
|
10142
10261
|
onUpdate() {}
|
|
10143
10262
|
|
|
@@ -10213,6 +10332,102 @@ class UIText extends UIObject
|
|
|
10213
10332
|
}
|
|
10214
10333
|
}
|
|
10215
10334
|
|
|
10335
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10336
|
+
/**
|
|
10337
|
+
* UITextInput - An editable text input field
|
|
10338
|
+
* - A simple text entry field that supports basic editing
|
|
10339
|
+
* - Suitable for short text input like names or numbers
|
|
10340
|
+
* @extends UIObject
|
|
10341
|
+
* @memberof UISystem
|
|
10342
|
+
*/
|
|
10343
|
+
class UITextInput extends UIObject
|
|
10344
|
+
{
|
|
10345
|
+
/** Create a UITextInput object
|
|
10346
|
+
* @param {Vector2} [pos]
|
|
10347
|
+
* @param {Vector2} [size]
|
|
10348
|
+
* @param {string} [text]
|
|
10349
|
+
*/
|
|
10350
|
+
constructor(pos, size, text='')
|
|
10351
|
+
{
|
|
10352
|
+
super(pos, size);
|
|
10353
|
+
|
|
10354
|
+
ASSERT(isString(text), 'ui text must be a string');
|
|
10355
|
+
|
|
10356
|
+
/** @property {number} - Max length of input (0 = no limit) */
|
|
10357
|
+
this.maxLength = 0;
|
|
10358
|
+
|
|
10359
|
+
// set properties
|
|
10360
|
+
this.text = text;
|
|
10361
|
+
this.interactive = true;
|
|
10362
|
+
this.canBeHover = true;
|
|
10363
|
+
}
|
|
10364
|
+
|
|
10365
|
+
click()
|
|
10366
|
+
{
|
|
10367
|
+
// start editing the text
|
|
10368
|
+
uiSystem.keyInputObject = this;
|
|
10369
|
+
this.onClick();
|
|
10370
|
+
}
|
|
10371
|
+
|
|
10372
|
+
/** Stop editing the text edited */
|
|
10373
|
+
stopEditing()
|
|
10374
|
+
{
|
|
10375
|
+
if (!this.isKeyInputObject())
|
|
10376
|
+
return;
|
|
10377
|
+
|
|
10378
|
+
if (this.soundRelease)
|
|
10379
|
+
this.soundRelease.play();
|
|
10380
|
+
uiSystem.activeObject = undefined;
|
|
10381
|
+
uiSystem.keyInputObject = undefined;
|
|
10382
|
+
this.onChange();
|
|
10383
|
+
}
|
|
10384
|
+
|
|
10385
|
+
/** Key down event handler if this object is being edited
|
|
10386
|
+
* @param {KeyboardEvent} [e] */
|
|
10387
|
+
onKeyDown(e)
|
|
10388
|
+
{
|
|
10389
|
+
const code = e.code, key = e.key
|
|
10390
|
+
if (code === 'Backspace')
|
|
10391
|
+
this.text = this.text.slice(0, -1);
|
|
10392
|
+
else if (code === 'Enter' || code === 'Escape')
|
|
10393
|
+
this.stopEditing();
|
|
10394
|
+
else if (key.length === 1) // printable characters
|
|
10395
|
+
{
|
|
10396
|
+
if (!this.maxLength || this.text.length < this.maxLength)
|
|
10397
|
+
this.text += key;
|
|
10398
|
+
}
|
|
10399
|
+
}
|
|
10400
|
+
|
|
10401
|
+
update()
|
|
10402
|
+
{
|
|
10403
|
+
super.update();
|
|
10404
|
+
|
|
10405
|
+
if (!this.isKeyInputObject())
|
|
10406
|
+
return;
|
|
10407
|
+
|
|
10408
|
+
// click off object to stop editing
|
|
10409
|
+
if (mouseWasPressed(0) && !this.isMouseOverlapping() ||
|
|
10410
|
+
gamepadWasPressed(0, gamepadPrimary))
|
|
10411
|
+
{
|
|
10412
|
+
this.stopEditing();
|
|
10413
|
+
inputClearKey(0,0);
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
|
|
10417
|
+
render()
|
|
10418
|
+
{
|
|
10419
|
+
super.render();
|
|
10420
|
+
|
|
10421
|
+
// draw the text scaled to fit
|
|
10422
|
+
const textSize = this.getTextSize();
|
|
10423
|
+
let text = this.text;
|
|
10424
|
+
if (this.isKeyInputObject()) // add a cursor to end of text
|
|
10425
|
+
text += timeReal%1 < .5 ? '█' : '░';
|
|
10426
|
+
uiSystem.drawText(text, this.pos, textSize,
|
|
10427
|
+
this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
10428
|
+
}
|
|
10429
|
+
}
|
|
10430
|
+
|
|
10216
10431
|
///////////////////////////////////////////////////////////////////////////////
|
|
10217
10432
|
/**
|
|
10218
10433
|
* UITile - A UI object that displays a tile image
|
|
@@ -10324,9 +10539,10 @@ class UICheckbox extends UIObject
|
|
|
10324
10539
|
this.color = color.copy();
|
|
10325
10540
|
this.interactive = true;
|
|
10326
10541
|
}
|
|
10327
|
-
|
|
10542
|
+
click()
|
|
10328
10543
|
{
|
|
10329
10544
|
this.checked = !this.checked;
|
|
10545
|
+
this.onClick();
|
|
10330
10546
|
this.onChange();
|
|
10331
10547
|
}
|
|
10332
10548
|
render()
|