littlejsengine 1.7.21 → 1.8.1

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.
Files changed (51) hide show
  1. package/README.md +27 -55
  2. package/build/littlejs.d.ts +101 -58
  3. package/build/littlejs.esm.js +497 -419
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +493 -228
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +493 -228
  8. package/examples/breakout/game.js +17 -14
  9. package/examples/breakout/gameObjects.js +29 -8
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/README.md +2 -2
  12. package/examples/breakoutTutorial/game.js +4 -4
  13. package/examples/breakoutTutorial/index.html +2 -2
  14. package/examples/electron/game.js +3 -2
  15. package/examples/electron/index.html +2 -2
  16. package/examples/empty/game.js +1 -1
  17. package/examples/favicon.png +0 -0
  18. package/examples/js13k/game.js +20 -15
  19. package/examples/js13k/index.html +14 -14
  20. package/examples/module/game.js +4 -3
  21. package/examples/module/index.html +1 -1
  22. package/examples/particles/index.html +19 -22
  23. package/examples/platformer/game.js +3 -3
  24. package/examples/platformer/gameEffects.js +21 -19
  25. package/examples/platformer/gameLevel.js +6 -6
  26. package/examples/platformer/gameObjects.js +18 -18
  27. package/examples/platformer/gamePlayer.js +4 -3
  28. package/examples/platformer/index.html +6 -6
  29. package/examples/platformer/tiles.png +0 -0
  30. package/examples/platformer/tilesLevel.png +0 -0
  31. package/examples/puzzle/game.js +10 -8
  32. package/examples/puzzle/index.html +2 -2
  33. package/examples/screenshot.jpg +0 -0
  34. package/examples/starter/game.js +32 -21
  35. package/examples/starter/index.html +13 -13
  36. package/examples/starter/tiles.png +0 -0
  37. package/examples/stress/index.html +2 -2
  38. package/examples/typescript/game.js +4 -3
  39. package/examples/typescript/game.ts +4 -3
  40. package/examples/typescript/index.html +1 -1
  41. package/package.json +1 -1
  42. package/src/engine.js +59 -52
  43. package/src/engineDraw.js +137 -38
  44. package/src/engineExport.js +4 -191
  45. package/src/engineMedals.js +13 -45
  46. package/src/engineObject.js +12 -13
  47. package/src/engineParticles.js +17 -17
  48. package/src/engineSettings.js +195 -2
  49. package/src/engineTileLayer.js +23 -22
  50. package/src/engineUtilities.js +6 -6
  51. package/src/engineWebGL.js +31 -32
@@ -617,10 +617,10 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
617
617
  * - Can be used to create a deterministic random number sequence
618
618
  * @example
619
619
  * let r = new RandomGenerator(123); // random number generator with seed 123
620
- * let a = r.rand(); // random value between 0 and 1
621
- * let b = r.randInt(10); // random integer between 0 and 9
620
+ * let a = r.float(); // random value between 0 and 1
621
+ * let b = r.int(10); // random integer between 0 and 9
622
622
  * r.seed = 123; // reset the seed
623
- * let c = r.rand(); // the same value as a
623
+ * let c = r.float(); // the same value as a
624
624
  */
625
625
  class RandomGenerator
626
626
  {
@@ -649,18 +649,18 @@ class RandomGenerator
649
649
  * @param {Number} valueA
650
650
  * @param {Number} [valueB=0]
651
651
  * @return {Number} */
652
- int(valueA, valueB=0) { return Math.floor(this.rand(valueA, valueB)); }
652
+ int(valueA, valueB=0) { return Math.floor(this.float(valueA, valueB)); }
653
653
 
654
654
  /** Randomly returns either -1 or 1 deterministically
655
655
  * @return {Number} */
656
- sign() { return this.randInt(2) * 2 - 1; }
656
+ sign() { return this.int(2) * 2 - 1; }
657
657
  }
658
658
 
659
659
  ///////////////////////////////////////////////////////////////////////////////
660
660
 
661
661
  /**
662
662
  * Create a 2d vector, can take another Vector2 to copy, 2 scalars, or 1 scalar
663
- * @param {Number} [x=0]
663
+ * @param {(Number|Vector2)} [x=0]
664
664
  * @param {Number} [y=0]
665
665
  * @return {Vector2}
666
666
  * @example
@@ -1144,7 +1144,7 @@ let glOverlay = 1;
1144
1144
  * @memberof Settings */
1145
1145
  let tileSizeDefault = vec2(16);
1146
1146
 
1147
- /** Prevent tile bleeding from neighbors in pixels
1147
+ /** How many pixels smaller to draw tiles to prevent bleeding from neighbors
1148
1148
  * @type {Number}
1149
1149
  * @default
1150
1150
  * @memberof Settings */
@@ -1318,7 +1318,200 @@ let medalDisplayIconSize = 50;
1318
1318
  * @type {Boolean}
1319
1319
  * @default 0
1320
1320
  * @memberof Settings */
1321
- let medalsPreventUnlock;
1321
+ let medalsPreventUnlock;
1322
+
1323
+ ///////////////////////////////////////////////////////////////////////////////
1324
+ // Setters for global variables
1325
+
1326
+ /** Set position of camera in world space
1327
+ * @param {Vector2} pos
1328
+ * @memberof Settings */
1329
+ function setCameraPos(pos) { cameraPos = pos; }
1330
+
1331
+ /** Set scale of camera in world space
1332
+ * @param {Number} scale
1333
+ * @memberof Settings */
1334
+ function setCameraScale(scale) { cameraScale = scale; }
1335
+
1336
+ /** Set max size of the canvas
1337
+ * @param {Vector2} size
1338
+ * @memberof Settings */
1339
+ function setCanvasMaxSize(size) { canvasMaxSize = size; }
1340
+
1341
+ /** Set fixed size of the canvas
1342
+ * @param {Vector2} size
1343
+ * @memberof Settings */
1344
+ function setCanvasFixedSize(size) { canvasFixedSize = size; }
1345
+
1346
+ /** Disables anti aliasing for pixel art if true
1347
+ * @param {Boolean} pixelated
1348
+ * @memberof Settings */
1349
+ function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
1350
+
1351
+ /** Set default font used for text rendering
1352
+ * @param {String} font
1353
+ * @memberof Settings */
1354
+ function setFontDefault(font) { fontDefault = font; }
1355
+
1356
+ /** Set if webgl rendering is enabled
1357
+ * @param {Boolean} enable
1358
+ * @memberof Settings */
1359
+ function setGlEnable(enable) { glEnable = enable; }
1360
+
1361
+ /** Set to not composite the WebGL canvas
1362
+ * @param {Boolean} overlay
1363
+ * @memberof Settings */
1364
+ function setGlOverlay(overlay) { glOverlay = overlay; }
1365
+
1366
+ /** Set default size of tiles in pixels
1367
+ * @param {Vector2} size
1368
+ * @memberof Settings */
1369
+ function setTileSizeDefault(size) { tileSizeDefault = size; }
1370
+
1371
+ /** Set to prevent tile bleeding from neighbors in pixels
1372
+ * @param {Number} scale
1373
+ * @memberof Settings */
1374
+ function setTileFixBleedScale(scale) { tileFixBleedScale = scale; }
1375
+
1376
+ /** Set if collisions between objects are enabled
1377
+ * @param {Boolean} enable
1378
+ * @memberof Settings */
1379
+ function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
1380
+
1381
+ /** Set default object mass for collison calcuations
1382
+ * @param {Number} mass
1383
+ * @memberof Settings */
1384
+ function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
1385
+
1386
+ /** Set how much to slow velocity by each frame
1387
+ * @param {Number} damping
1388
+ * @memberof Settings */
1389
+ function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
1390
+
1391
+ /** Set how much to slow angular velocity each frame
1392
+ * @param {Number} damping
1393
+ * @memberof Settings */
1394
+ function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
1395
+
1396
+ /** Set how much to bounce when a collision occur
1397
+ * @param {Number} elasticity
1398
+ * @memberof Settings */
1399
+ function setObjectDefaultElasticity(elasticity) { objectDefaultElasticity = elasticity; }
1400
+
1401
+ /** Set how much to slow when touching
1402
+ * @param {Number} friction
1403
+ * @memberof Settings */
1404
+ function setObjectDefaultFriction(friction) { objectDefaultFriction = friction; }
1405
+
1406
+ /** Set max speed to avoid fast objects missing collisions
1407
+ * @param {Number} speed
1408
+ * @memberof Settings */
1409
+ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
1410
+
1411
+ /** Set how much gravity to apply to objects along the Y axis
1412
+ * @param {Number} gravity
1413
+ * @memberof Settings */
1414
+ function setGravity(g) { gravity = g; }
1415
+
1416
+ /** Set to scales emit rate of particles
1417
+ * @param {Number} scale
1418
+ * @memberof Settings */
1419
+ function setParticleEmitRateScale(scale) { particleEmitRateScale = scale; }
1420
+
1421
+ /** Set if gamepads are enabled
1422
+ * @param {Boolean} enable
1423
+ * @memberof Settings */
1424
+ function setGamepadsEnable(enable) { gamepadsEnable = enable; }
1425
+
1426
+ /** Set if the dpad input is also routed to the left analog stick
1427
+ * @param {Boolean} enable
1428
+ * @memberof Settings */
1429
+ function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick = enable; }
1430
+
1431
+ /** Set if true the WASD keys are also routed to the direction keys
1432
+ * @param {Boolean} enable
1433
+ * @memberof Settings */
1434
+ function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
1435
+
1436
+ /** Set if touch gamepad should appear on mobile devices
1437
+ * @param {Boolean} enable
1438
+ * @memberof Settings */
1439
+ function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
1440
+
1441
+ /** Set if touch gamepad should be analog stick or 8 way dpad
1442
+ * @param {Boolean} analog
1443
+ * @memberof Settings */
1444
+ function setTouchGamepadAnalog(analog) { touchGamepadAnalog = analog; }
1445
+
1446
+ /** Set size of virutal gamepad for touch devices in pixels
1447
+ * @param {Number} size
1448
+ * @memberof Settings */
1449
+ function setTouchGamepadSize(size) { touchGamepadSize = size; }
1450
+
1451
+ /** Set transparency of touch gamepad overlay
1452
+ * @param {Number} alpha
1453
+ * @memberof Settings */
1454
+ function setTouchGamepadAlpha(alpha) { touchGamepadAlpha = alpha; }
1455
+
1456
+ /** Set to allow vibration hardware if it exists
1457
+ * @param {Boolean} enable
1458
+ * @memberof Settings */
1459
+ function setVibrateEnable(enable) { vibrateEnable = enable; }
1460
+
1461
+ /** Set to disable all audio code
1462
+ * @param {Boolean} enable
1463
+ * @memberof Settings */
1464
+ function setSoundEnable(enable) { soundEnable = enable; }
1465
+
1466
+ /** Set volume scale to apply to all sound, music and speech
1467
+ * @param {Number} volume
1468
+ * @memberof Settings */
1469
+ function setSoundVolume(volume) { soundVolume = volume; }
1470
+
1471
+ /** Set default range where sound no longer plays
1472
+ * @param {Number} range
1473
+ * @memberof Settings */
1474
+ function setSoundDefaultRange(range) { soundDefaultRange = range; }
1475
+
1476
+ /** Set default range percent to start tapering off sound
1477
+ * @param {Number} taper
1478
+ * @memberof Settings */
1479
+ function setSoundDefaultTaper(taper) { soundDefaultTaper = taper; }
1480
+
1481
+ /** Set how long to show medals for in seconds
1482
+ * @param {Number} time
1483
+ * @memberof Settings */
1484
+ function setMedalDisplayTime(time) { medalDisplayTime = time; }
1485
+
1486
+ /** Set how quickly to slide on/off medals in seconds
1487
+ * @param {Number} time
1488
+ * @memberof Settings */
1489
+ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
1490
+
1491
+ /** Set size of medal display
1492
+ * @param {Vector2} size
1493
+ * @memberof Settings */
1494
+ function setMedalDisplaySize(size) { medalDisplaySize = size; }
1495
+
1496
+ /** Set size of icon in medal display
1497
+ * @param {Number} size
1498
+ * @memberof Settings */
1499
+ function setMedalDisplayIconSize(size) { medalDisplayIconSize = size; }
1500
+
1501
+ /** Set to stop medals from being unlockable
1502
+ * @param {Boolean} preventUnlock
1503
+ * @memberof Settings */
1504
+ function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUnlock; }
1505
+
1506
+ /** Set if watermark with FPS should be shown
1507
+ * @param {Boolean} show
1508
+ * @memberof Debug */
1509
+ function setShowWatermark(show) { showWatermark = show; }
1510
+
1511
+ /** Set key code used to toggle debug mode, Esc by default
1512
+ * @param {Number} key
1513
+ * @memberof Debug */
1514
+ function setDebugKey(key) { debugKey = key; }
1322
1515
  /**
1323
1516
  * LittleJS Object System
1324
1517
  */
@@ -1353,18 +1546,19 @@ let medalsPreventUnlock;
1353
1546
  class EngineObject
1354
1547
  {
1355
1548
  /** Create an engine object and adds it to the list of objects
1356
- * @param {Vector2} [position=Vector2()] - World space position of the object
1357
- * @param {Vector2} [size=Vector2(1,1)] - World space size of the object
1358
- * @param {Number} [tileIndex=-1] - Tile to use to render object (-1 is untextured)
1359
- * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
1360
- * @param {Number} [angle=0] - Angle the object is rotated by
1361
- * @param {Color} [color=Color()] - Color to apply to tile when rendered
1362
- * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
1549
+ * @param {Vector2} [pos=Vector2()] - World space position of the object
1550
+ * @param {Vector2} [size=Vector2(1,1)] - World space size of the object
1551
+ * @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
1552
+ * @param {Number} [angle=0] - Angle the object is rotated by
1553
+ * @param {Color} [color=Color()] - Color to apply to tile when rendered
1554
+ * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
1363
1555
  */
1364
- constructor(pos=vec2(), size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, angle=0, color, renderOrder=0)
1556
+ constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color, renderOrder=0)
1365
1557
  {
1366
1558
  // set passed in params
1367
1559
  ASSERT(isVector2(pos) && isVector2(size)); // ensure pos and size are vec2s
1560
+ ASSERT(typeof tileInfo !== 'number' || !tileInfo); // prevent old style calls
1561
+ // to fix old calls, replace with tile(tileIndex, tileSize)
1368
1562
 
1369
1563
  /** @property {Vector2} - World space position of the object */
1370
1564
  this.pos = pos.copy();
@@ -1372,10 +1566,8 @@ class EngineObject
1372
1566
  this.size = size;
1373
1567
  /** @property {Vector2} - Size of object used for drawing, uses size if not set */
1374
1568
  this.drawSize;
1375
- /** @property {Number} - Tile to use to render object (-1 is untextured) */
1376
- this.tileIndex = tileIndex;
1377
- /** @property {Vector2} - Size of tile in source pixels */
1378
- this.tileSize = tileSize;
1569
+ /** @property {TileInfo} - Tile info to render object (undefined is untextured) */
1570
+ this.tileInfo = tileInfo;
1379
1571
  /** @property {Number} - Angle to rotate the object */
1380
1572
  this.angle = angle;
1381
1573
  /** @property {Color} - Color to apply when rendered */
@@ -1591,7 +1783,7 @@ class EngineObject
1591
1783
  render()
1592
1784
  {
1593
1785
  // default object render
1594
- drawTile(this.pos, this.drawSize || this.size, this.tileIndex, this.tileSize, this.color, this.angle, this.mirror, this.additiveColor);
1786
+ drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
1595
1787
  }
1596
1788
 
1597
1789
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
@@ -1746,13 +1938,109 @@ let overlayContext;
1746
1938
  * @memberof Draw */
1747
1939
  let mainCanvasSize = vec2();
1748
1940
 
1749
- /** Tile sheet for batch rendering system
1750
- * @type {CanvasImageSource}
1941
+ /** Array containing texture info for batch rendering system
1942
+ * @type {Array}
1751
1943
  * @memberof Draw */
1752
- const tileImage = new Image;
1944
+ let textureInfos = [];
1753
1945
 
1754
1946
  // Engine internal variables not exposed to documentation
1755
- let tileImageSize, tileImageFixBleed, drawCount;
1947
+ let drawCount;
1948
+
1949
+ ///////////////////////////////////////////////////////////////////////////////
1950
+
1951
+ /**
1952
+ * Create a tile info object
1953
+ * - This can take vecs or floats for easier use and conversion
1954
+ * - If an index is passed in, the tile size and index will determine the position
1955
+ * @param {(Number|Vector2)} [pos=Vector2()] - Top left corner of tile in pixels or index
1956
+ * @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
1957
+ * @param {Number} [textureIndex=0] - Texture index to use
1958
+ * @return {TileInfo}
1959
+ * @example
1960
+ * tile(2) // a tile at index 2 using the default tile size of 16
1961
+ * tile(5, 8) // a tile at index 5 using a tile size of 8
1962
+ * tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
1963
+ * tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
1964
+ * @memberof Draw
1965
+ */
1966
+ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
1967
+ {
1968
+ // if size is a number, make it a vector
1969
+ if (size.x == undefined)
1970
+ {
1971
+ ASSERT(size > 0);
1972
+ size = vec2(size);
1973
+ }
1974
+
1975
+ // if pos is a number, use it as a tile index
1976
+ if (pos.x == undefined)
1977
+ {
1978
+ const textureInfo = textureInfos[textureIndex];
1979
+ if (textureInfo)
1980
+ {
1981
+ const cols = textureInfo.size.x / size.x |0;
1982
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
1983
+ }
1984
+ else
1985
+ pos = vec2();
1986
+ }
1987
+
1988
+ // return a tile info object
1989
+ return new TileInfo(pos, size, textureIndex);
1990
+ }
1991
+
1992
+ /**
1993
+ * Tile Info - Stores info about how to draw a tile
1994
+ */
1995
+ class TileInfo
1996
+ {
1997
+ /** Create a tile info object
1998
+ * @param {Vector2} [pos=Vector2()] - Top left corner of tile in pixels
1999
+ * @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
2000
+ * @param {Number} [textureIndex=0] - Texture index to use
2001
+ */
2002
+ constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
2003
+ {
2004
+ /** @property {Vector2} - Top left corner of tile in pixels */
2005
+ this.pos = pos;
2006
+ /** @property {Vector2} - Size of tile in pixels */
2007
+ this.size = size;
2008
+ /** @property {Number} - Texture index to use */
2009
+ this.textureIndex = textureIndex;
2010
+ }
2011
+
2012
+ /** Returns an offset copy of this tile, useful for animation
2013
+ * @param {Vector2} offset - Offset to apply in pixels
2014
+ * @return {TileInfo}
2015
+ */
2016
+ offset(offset)
2017
+ { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
2018
+
2019
+ /** Returns the texture info for this tile
2020
+ * @return {TextureInfo}
2021
+ */
2022
+ getTextureInfo()
2023
+ { return textureInfos[this.textureIndex]; }
2024
+ }
2025
+
2026
+ /** Texture Info - Stores info about each texture */
2027
+ class TextureInfo
2028
+ {
2029
+ // create a TextureInfo, called automatically by the engine
2030
+ constructor(image)
2031
+ {
2032
+ /** @property {CanvasImageSource} - image source */
2033
+ this.image = image;
2034
+ /** @property {Vector2} - size of the image */
2035
+ this.size = vec2(image.width, image.height);
2036
+ /** @property {WebGLTexture} - webgl texture */
2037
+ this.glTexture = glEnable && glCreateTexture(image);
2038
+ /** @property {Vector2} - size to adjust tile to fix bleeding */
2039
+ this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
2040
+ }
2041
+ }
2042
+
2043
+ ///////////////////////////////////////////////////////////////////////////////
1756
2044
 
1757
2045
  /** Convert from screen to world space coordinates
1758
2046
  * @param {Vector2} screenPos
@@ -1783,7 +2071,7 @@ function worldToScreen(worldPos)
1783
2071
  /** Draw textured tile centered in world space, with color applied if using WebGL
1784
2072
  * @param {Vector2} pos - Center of the tile in world space
1785
2073
  * @param {Vector2} [size=Vector2(1,1)] - Size of the tile in world space
1786
- * @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
2074
+ * @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
1787
2075
  * @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
1788
2076
  * @param {Color} [color=Color()] - Color to modulate with
1789
2077
  * @param {Number} [angle=0] - Angle to rotate by
@@ -1792,11 +2080,14 @@ function worldToScreen(worldPos)
1792
2080
  * @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
1793
2081
  * @param {Boolean} [screenSpace=0] - If true the pos and size are in screen space
1794
2082
  * @memberof Draw */
1795
- function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
2083
+ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
1796
2084
  angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace)
1797
2085
  {
2086
+ ASSERT(typeof tileInfo !== 'number' || !tileInfo); // prevent old style calls
2087
+ // to fix old calls, replace with tile(tileIndex, tileSize)
2088
+
1798
2089
  showWatermark && ++drawCount;
1799
-
2090
+ const textureInfo = tileInfo && tileInfo.getTextureInfo();
1800
2091
  if (glEnable && useWebGL)
1801
2092
  {
1802
2093
  if (screenSpace)
@@ -1805,46 +2096,48 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
1805
2096
  pos = screenToWorld(pos);
1806
2097
  size = size.scale(1/cameraScale);
1807
2098
  }
1808
- if (tileIndex < 0 || !tileImage.width)
1809
- {
1810
- // if negative tile index or image not found, force untextured
1811
- glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
1812
- }
1813
- else
2099
+
2100
+ if (textureInfo)
1814
2101
  {
1815
2102
  // calculate uvs and render
1816
- const cols = tileImageSize.x / tileSize.x |0;
1817
- const uvSizeX = tileSize.x / tileImageSize.x;
1818
- const uvSizeY = tileSize.y / tileImageSize.y;
1819
- const uvX = (tileIndex%cols)*uvSizeX, uvY = (tileIndex/cols|0)*uvSizeY;
1820
-
2103
+ const x = tileInfo.pos.x / textureInfo.size.x;
2104
+ const y = tileInfo.pos.y / textureInfo.size.y;
2105
+ const w = tileInfo.size.x / textureInfo.size.x;
2106
+ const h = tileInfo.size.y / textureInfo.size.y;
2107
+ const tileImageFixBleed = textureInfo.fixBleedSize;
2108
+ glSetTexture(textureInfo.glTexture);
1821
2109
  glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
1822
- uvX + tileImageFixBleed.x, uvY + tileImageFixBleed.y,
1823
- uvX - tileImageFixBleed.x + uvSizeX, uvY - tileImageFixBleed.y + uvSizeY,
2110
+ x + tileImageFixBleed.x, y + tileImageFixBleed.y,
2111
+ x - tileImageFixBleed.x + w, y - tileImageFixBleed.y + h,
1824
2112
  color.rgbaInt(), additiveColor.rgbaInt());
1825
2113
  }
2114
+ else
2115
+ {
2116
+ // if no tile info, force untextured
2117
+ glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
2118
+ }
1826
2119
  }
1827
2120
  else
1828
2121
  {
1829
2122
  // normal canvas 2D rendering method (slower)
1830
2123
  drawCanvas2D(pos, size, angle, mirror, (context)=>
1831
2124
  {
1832
- if (tileIndex < 0)
2125
+ if (textureInfo)
1833
2126
  {
1834
- // if negative tile index, force untextured
1835
- context.fillStyle = color;
1836
- context.fillRect(-.5, -.5, 1, 1);
2127
+ // calculate uvs and render
2128
+ const x = tileInfo.pos.x + tileFixBleedScale;
2129
+ const y = tileInfo.pos.y + tileFixBleedScale;
2130
+ const w = tileInfo.size.x - 2*tileFixBleedScale;
2131
+ const h = tileInfo.size.y - 2*tileFixBleedScale;
2132
+ context.globalAlpha = color.a; // only alpha is supported
2133
+ context.drawImage(textureInfo.image, x, y, w, h, -.5, -.5, 1, 1);
2134
+ context.globalAlpha = 1; // set back to full alpha
1837
2135
  }
1838
2136
  else
1839
2137
  {
1840
- // calculate uvs and render
1841
- const cols = tileImageSize.x / tileSize.x |0;
1842
- const sX = (tileIndex%cols)*tileSize.x + tileFixBleedScale;
1843
- const sY = (tileIndex/cols|0)*tileSize.y + tileFixBleedScale;
1844
- const sWidth = tileSize.x - 2*tileFixBleedScale;
1845
- const sHeight = tileSize.y - 2*tileFixBleedScale;
1846
- context.globalAlpha = color.a; // only alpha is supported
1847
- context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
2138
+ // if no tile info, force untextured
2139
+ context.fillStyle = color;
2140
+ context.fillRect(-.5, -.5, 1, 1);
1848
2141
  }
1849
2142
  }, undefined, screenSpace);
1850
2143
  }
@@ -1859,7 +2152,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
1859
2152
  * @param {Boolean} [screenSpace=0]
1860
2153
  * @memberof Draw */
1861
2154
  function drawRect(pos, size, color, angle, useWebGL, screenSpace)
1862
- { drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, undefined, useWebGL, screenSpace); }
2155
+ { drawTile(pos, size, undefined, color, angle, 0, undefined, useWebGL, screenSpace); }
1863
2156
 
1864
2157
  /** Draw colored polygon using passed in points
1865
2158
  * @param {Array} points - Array of Vector2 points
@@ -2004,10 +2297,9 @@ class FontImage
2004
2297
  * @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
2005
2298
  * @param {Vector2} [tileSize=vec2(8)] - Size of the font source tiles
2006
2299
  * @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
2007
- * @param {Number} [startTileIndex=0] - Tile index in image where font starts
2008
2300
  * @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
2009
2301
  */
2010
- constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
2302
+ constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
2011
2303
  {
2012
2304
  // load default font image
2013
2305
  if (!engineFontImage)
@@ -2016,7 +2308,6 @@ class FontImage
2016
2308
  this.image = image || engineFontImage;
2017
2309
  this.tileSize = tileSize;
2018
2310
  this.paddingSize = paddingSize;
2019
- this.startTileIndex = startTileIndex;
2020
2311
  this.context = context;
2021
2312
  }
2022
2313
 
@@ -2057,7 +2348,7 @@ class FontImage
2057
2348
  charCode = 127; // unknown character
2058
2349
 
2059
2350
  // get the character source location and draw it
2060
- const tile = this.startTileIndex + charCode - 32;
2351
+ const tile = charCode - 32;
2061
2352
  const x = tile % cols;
2062
2353
  const y = tile / cols |0;
2063
2354
  const drawPos = pos.add(vec2(j,i).multiply(drawSize));
@@ -2089,8 +2380,7 @@ function toggleFullscreen()
2089
2380
  }
2090
2381
  else if (document.body.requestFullscreen)
2091
2382
  document.body.requestFullscreen();
2092
- }
2093
-
2383
+ }
2094
2384
  /**
2095
2385
  * LittleJS Input System
2096
2386
  * - Tracks keyboard down, pressed, and released
@@ -3248,7 +3538,7 @@ class TileLayerData
3248
3538
  }
3249
3539
 
3250
3540
  /**
3251
- * Tile layer object - cached rendering system for tile layers
3541
+ * Tile Layer - cached rendering system for tile layers
3252
3542
  * - Each Tile layer is rendered to an off screen canvas
3253
3543
  * - To allow dynamic modifications, layers are rendered using canvas 2d
3254
3544
  * - Some devices like mobile phones are limited to 4k texture resolution
@@ -3264,13 +3554,13 @@ class TileLayer extends EngineObject
3264
3554
  /** Create a tile layer object
3265
3555
  * @param {Vector2} [position=Vector2()] - World space position
3266
3556
  * @param {Vector2} [size=tileCollisionSize] - World space size
3267
- * @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
3557
+ * @param {TileInfo} [tileInfo] - Tile info for layer
3268
3558
  * @param {Vector2} [scale=Vector2(1,1)] - How much to scale this layer when rendered
3269
3559
  * @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
3270
3560
  */
3271
- constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
3561
+ constructor(pos, size=tileCollisionSize, tileInfo=tile(), scale=vec2(1), renderOrder=0)
3272
3562
  {
3273
- super(pos, size, -1, tileSize, 0, undefined, renderOrder);
3563
+ super(pos, size, tileInfo, 0, undefined, renderOrder);
3274
3564
 
3275
3565
  /** @property {HTMLCanvasElement} - The canvas used by this tile layer */
3276
3566
  this.canvas = document.createElement('canvas');
@@ -3347,13 +3637,13 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3347
3637
  mainCanvas = this.canvas;
3348
3638
  mainContext = this.context;
3349
3639
  cameraPos = this.size.scale(.5);
3350
- cameraScale = this.tileSize.x;
3640
+ cameraScale = this.tileInfo.size.x;
3351
3641
 
3352
3642
  if (clear)
3353
3643
  {
3354
3644
  // clear and set size
3355
- mainCanvas.width = this.size.x * this.tileSize.x;
3356
- mainCanvas.height = this.size.y * this.tileSize.y;
3645
+ mainCanvas.width = this.size.x * this.tileInfo.size.x;
3646
+ mainCanvas.height = this.size.y * this.tileInfo.size.y;
3357
3647
  }
3358
3648
 
3359
3649
  // begin a new render for the tile canvas
@@ -3384,7 +3674,8 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3384
3674
  if (d.tile != undefined)
3385
3675
  {
3386
3676
  ASSERT(mainContext == this.context); // must call redrawStart() before drawing tiles
3387
- drawTile(pos, vec2(1), d.tile, this.tileSize, d.color, d.direction*PI/2, d.mirror);
3677
+ const tileInfo = tile(d.tile, this.tileInfo.size, this.tileInfo.textureIndex);
3678
+ drawTile(pos, vec2(1), tileInfo, d.color, d.direction*PI/2, d.mirror);
3388
3679
  }
3389
3680
  }
3390
3681
 
@@ -3406,8 +3697,8 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3406
3697
  {
3407
3698
  const context = this.context;
3408
3699
  context.save();
3409
- pos = pos.subtract(this.pos).multiply(this.tileSize);
3410
- size = size.multiply(this.tileSize);
3700
+ pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
3701
+ size = size.multiply(this.tileInfo.size);
3411
3702
  context.translate(pos.x, this.canvas.height - pos.y);
3412
3703
  context.rotate(angle);
3413
3704
  context.scale(mirror ? -size.x : size.x, size.y);
@@ -3418,28 +3709,28 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3418
3709
  /** Draw a tile directly onto the layer canvas
3419
3710
  * @param {Vector2} pos
3420
3711
  * @param {Vector2} [size=Vector2(1,1)]
3421
- * @param {Number} [tileIndex=-1]
3422
- * @param {Vector2} [tileSize=tileSizeDefault]
3712
+ * @param {TileInfo} [tileInfo]
3423
3713
  * @param {Color} [color=Color()]
3424
3714
  * @param {Number} [angle=0]
3425
3715
  * @param {Boolean} [mirror=0] */
3426
- drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color, angle, mirror)
3716
+ drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
3427
3717
  {
3428
3718
  this.drawCanvas2D(pos, size, angle, mirror, (context)=>
3429
3719
  {
3430
- if (tileIndex < 0)
3720
+ const textureInfo = tileInfo && tileInfo.getTextureInfo();
3721
+ if (textureInfo)
3431
3722
  {
3432
- // untextured
3433
- context.fillStyle = color;
3434
- context.fillRect(-.5, -.5, 1, 1);
3723
+ context.globalAlpha = color.a; // only alpha is supported
3724
+ context.drawImage(textureInfo.image,
3725
+ tileInfo.pos.x, tileInfo.pos.y,
3726
+ tileInfo.size.x, tileInfo.size.y, -.5, -.5, 1, 1);
3727
+ context.globalAlpha = 1;
3435
3728
  }
3436
3729
  else
3437
3730
  {
3438
- const cols = tileImage.width/tileSize.x;
3439
- context.globalAlpha = color.a; // only alpha, no color, is supported in this mode
3440
- context.drawImage(tileImage,
3441
- (tileIndex%cols)*tileSize.x, (tileIndex/cols|0)*tileSize.y,
3442
- tileSize.x, tileSize.y, -.5, -.5, 1, 1);
3731
+ // untextured
3732
+ context.fillStyle = color;
3733
+ context.fillRect(-.5, -.5, 1, 1);
3443
3734
  }
3444
3735
  });
3445
3736
  }
@@ -3467,7 +3758,7 @@ constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1)
3467
3758
  * let particleEmiter = new ParticleEmitter
3468
3759
  * (
3469
3760
  * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
3470
- * 0, vec2(16), // tileIndex, tileSize
3761
+ * tile(0, 16), // tileInfo
3471
3762
  * new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
3472
3763
  * new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
3473
3764
  * 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
@@ -3484,8 +3775,7 @@ class ParticleEmitter extends EngineObject
3484
3775
  * @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
3485
3776
  * @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
3486
3777
  * @param {Number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
3487
- * @param {Number} [tileIndex=-1] - Index into tile sheet, if <0 no texture is applied
3488
- * @param {Vector2} [tileSize=tileSizeDefault] - Tile size for particles
3778
+ * @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
3489
3779
  * @param {Color} [colorStartA=Color()] - Color at start of life 1, randomized between start colors
3490
3780
  * @param {Color} [colorStartB=Color()] - Color at start of life 2, randomized between start colors
3491
3781
  * @param {Color} [colorEndA=Color(1,1,1,0)] - Color at end of life 1, randomized between end colors
@@ -3515,8 +3805,7 @@ class ParticleEmitter extends EngineObject
3515
3805
  emitTime = 0,
3516
3806
  emitRate = 100,
3517
3807
  emitConeAngle = PI,
3518
- tileIndex = -1,
3519
- tileSize = tileSizeDefault,
3808
+ tileInfo,
3520
3809
  colorStartA = new Color,
3521
3810
  colorStartB = new Color,
3522
3811
  colorEndA = new Color(1,1,1,0),
@@ -3539,7 +3828,7 @@ class ParticleEmitter extends EngineObject
3539
3828
  localSpace
3540
3829
  )
3541
3830
  {
3542
- super(pos, vec2(), tileIndex, tileSize, angle, undefined, renderOrder);
3831
+ super(pos, vec2(), tileInfo, angle, undefined, renderOrder);
3543
3832
 
3544
3833
  // emitter settings
3545
3834
  /** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
@@ -3638,7 +3927,7 @@ class ParticleEmitter extends EngineObject
3638
3927
  angle += this.angle;
3639
3928
  }
3640
3929
 
3641
- const particle = new Particle(pos, this.tileIndex, this.tileSize, angle);
3930
+ const particle = new Particle(pos, this.tileInfo, this.tileSize, angle);
3642
3931
 
3643
3932
  // randomness scales each paremeter by a percentage
3644
3933
  const randomness = this.randomness;
@@ -3698,12 +3987,11 @@ class Particle extends EngineObject
3698
3987
  /**
3699
3988
  * Create a particle with the given settings
3700
3989
  * @param {Vector2} position - World space position of the particle
3701
- * @param {Number} [tileIndex=-1] - Tile to use to render, untextured if -1
3702
- * @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
3990
+ * @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
3703
3991
  * @param {Number} [angle=0] - Angle to rotate the particle
3704
3992
  */
3705
- constructor(pos, tileIndex, tileSize, angle)
3706
- { super(pos, vec2(), tileIndex, tileSize, angle); }
3993
+ constructor(pos, tileInfo, angle)
3994
+ { super(pos, vec2(), tileInfo, angle); }
3707
3995
 
3708
3996
  /** Render the particle, automatically called each frame, sorted by renderOrder */
3709
3997
  render()
@@ -3737,14 +4025,17 @@ class Particle extends EngineObject
3737
4025
  if (this.localSpaceEmitter)
3738
4026
  velocity = velocity.rotate(-this.localSpaceEmitter.angle);
3739
4027
  const speed = velocity.length();
3740
- const direction = velocity.scale(1/speed);
3741
- const trailLength = speed * this.trailScale;
3742
- size.y = max(size.x, trailLength);
3743
- angle = direction.angle();
3744
- drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileIndex, this.tileSize, color, angle, this.mirror);
4028
+ if (speed)
4029
+ {
4030
+ const direction = velocity.scale(1/speed);
4031
+ const trailLength = speed * this.trailScale;
4032
+ size.y = max(size.x, trailLength);
4033
+ angle = direction.angle();
4034
+ drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileInfo, color, angle, this.mirror);
4035
+ }
3745
4036
  }
3746
4037
  else
3747
- drawTile(pos, size, this.tileIndex, this.tileSize, color, angle, this.mirror);
4038
+ drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
3748
4039
  this.additive && setBlendMode();
3749
4040
  debugParticles && debugRect(pos, size, '#f005', 0, angle);
3750
4041
 
@@ -3791,7 +4082,7 @@ function medalsInit(saveName)
3791
4082
  }
3792
4083
 
3793
4084
  /**
3794
- * Medal Object - Tracks an unlockable medal
4085
+ * Medal - Tracks an unlockable medal
3795
4086
  * @example
3796
4087
  * // create a medal
3797
4088
  * const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
@@ -3804,7 +4095,7 @@ function medalsInit(saveName)
3804
4095
  */
3805
4096
  class Medal
3806
4097
  {
3807
- /** Create an medal object and adds it to the list of medals
4098
+ /** Create a medal object and adds it to the list of medals
3808
4099
  * @param {Number} id - The unique identifier of the medal
3809
4100
  * @param {String} name - Name of the medal
3810
4101
  * @param {String} [description] - Description of the medal
@@ -3916,33 +4207,33 @@ let newgrounds;
3916
4207
  /** This can used to enable Newgrounds functionality
3917
4208
  * @param {Number} app_id - The newgrounds App ID
3918
4209
  * @param {String} [cipher] - The encryption Key (AES-128/Base64)
4210
+ * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher
3919
4211
  * @memberof Medals */
3920
- function newgroundsInit(app_id, cipher) { newgrounds = new Newgrounds(app_id, cipher); }
4212
+ function newgroundsInit(app_id, cipher, cryptoJS)
4213
+ { newgrounds = new Newgrounds(app_id, cipher, cryptoJS); }
3921
4214
 
3922
4215
  /**
3923
4216
  * Newgrounds API wrapper object
3924
4217
  * @example
3925
- * // create a newgrounds object, replace the app id and cipher with your own
4218
+ * // create a newgrounds object, replace the app id with your own
3926
4219
  * const app_id = '53123:1ZuSTQ9l';
3927
- * const cipher = 'enF0vGH@Mj/FRASKL23Q==';
3928
- * newgrounds = new Newgrounds(app_id, cipher);
4220
+ * newgrounds = new Newgrounds(app_id);
3929
4221
  */
3930
4222
  class Newgrounds
3931
4223
  {
3932
4224
  /** Create a newgrounds object
3933
4225
  * @param {Number} app_id - The newgrounds App ID
3934
- * @param {String} [cipher] - The encryption Key (AES-128/Base64) */
3935
- constructor(app_id, cipher)
4226
+ * @param {String} [cipher] - The encryption Key (AES-128/Base64)
4227
+ * @param {Object} [cryptoJS] - An instance of CryptoJS, if there is a cipher */
4228
+ constructor(app_id, cipher, cryptoJS)
3936
4229
  {
3937
- ASSERT(!newgrounds && app_id);
4230
+ ASSERT(!newgrounds && app_id); // can only be one newgrounds object
4231
+ ASSERT(!cipher || cryptoJS); // must provide cryptojs if there is a cipher
3938
4232
 
3939
4233
  this.app_id = app_id;
3940
4234
  this.cipher = cipher;
4235
+ this.cryptoJS = cryptoJS;
3941
4236
  this.host = location ? location.hostname : '';
3942
-
3943
- // create an instance of CryptoJS for encrypted calls
3944
- if (cipher)
3945
- this.cryptoJS = this.CryptoJS();
3946
4237
 
3947
4238
  // get session id from url search params
3948
4239
  const url = new URL(location.href);
@@ -4046,38 +4337,6 @@ class Newgrounds
4046
4337
  debugMedals && console.log(xmlHttp.responseText);
4047
4338
  return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
4048
4339
  }
4049
-
4050
- CryptoJS()
4051
- {
4052
- ///////////////////////////////////////////////////////////////////////////////
4053
- // Crypto-JS - https://github.com/brix/crypto-js - MIT License
4054
- //
4055
- // [The MIT License (MIT)](http://opensource.org/licenses/MIT)
4056
- //
4057
- // Copyright (c) 2009-2013 Jeff Mott
4058
- // Copyright (c) 2013-2016 Evan Vosberg
4059
- //
4060
- // Permission is hereby granted, free of charge, to any person obtaining a copy
4061
- // of this software and associated documentation files (the "Software"), to deal
4062
- // in the Software without restriction, including without limitation the rights
4063
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4064
- // copies of the Software, and to permit persons to whom the Software is
4065
- // furnished to do so, subject to the following conditions:
4066
- //
4067
- // The above copyright notice and this permission notice shall be included in
4068
- // all copies or substantial portions of the Software.
4069
- //
4070
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4071
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4072
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4073
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4074
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4075
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4076
- // THE SOFTWARE.
4077
- return eval(Function("[M='GBMGXz^oVYPPKKbB`agTXU|LxPc_ZBcMrZvCr~wyGfWrwk@ATqlqeTp^N?p{we}jIpEnB_sEr`l?YDkDhWhprc|Er|XETG?pTl`e}dIc[_N~}fzRycIfpW{HTolvoPB_FMe_eH~BTMx]yyOhv?biWPCGc]kABencBhgERHGf{OL`Dj`c^sh@canhy[secghiyotcdOWgO{tJIE^JtdGQRNSCrwKYciZOa]Y@tcRATYKzv|sXpboHcbCBf`}SKeXPFM|RiJsSNaIb]QPc[D]Jy_O^XkOVTZep`ONmntLL`Qz~UupHBX_Ia~WX]yTRJIxG`ioZ{fefLJFhdyYoyLPvqgH?b`[TMnTwwfzDXhfM?rKs^aFr|nyBdPmVHTtAjXoYUloEziWDCw_suyYT~lSMksI~ZNCS[Bex~j]Vz?kx`gdYSEMCsHpjbyxQvw|XxX_^nQYue{sBzVWQKYndtYQMWRef{bOHSfQhiNdtR{o?cUAHQAABThwHPT}F{VvFmgN`E@FiFYS`UJmpQNM`X|tPKHlccT}z}k{sACHL?Rt@MkWplxO`ASgh?hBsuuP|xD~LSH~KBlRs]t|l|_tQAroDRqWS^SEr[sYdPB}TAROtW{mIkE|dWOuLgLmJrucGLpebrAFKWjikTUzS|j}M}szasKOmrjy[?hpwnEfX[jGpLt@^v_eNwSQHNwtOtDgWD{rk|UgASs@mziIXrsHN_|hZuxXlPJOsA^^?QY^yGoCBx{ekLuZzRqQZdsNSx@ezDAn{XNj@fRXIwrDX?{ZQHwTEfu@GhxDOykqts|n{jOeZ@c`dvTY?e^]ATvWpb?SVyg]GC?SlzteilZJAL]mlhLjYZazY__qcVFYvt@|bIQnSno@OXyt]OulzkWqH`rYFWrwGs`v|~XeTsIssLrbmHZCYHiJrX}eEzSssH}]l]IhPQhPoQ}rCXLyhFIT[clhzYOvyHqigxmjz`phKUU^TPf[GRAIhNqSOdayFP@FmKmuIzMOeoqdpxyCOwCthcLq?n`L`tLIBboNn~uXeFcPE{C~mC`h]jUUUQe^`UqvzCutYCgct|SBrAeiYQW?X~KzCz}guXbsUw?pLsg@hDArw?KeJD[BN?GD@wgFWCiHq@Ypp_QKFixEKWqRp]oJFuVIEvjDcTFu~Zz]a{IcXhWuIdMQjJ]lwmGQ|]g~c]Hl]pl`Pd^?loIcsoNir_kikBYyg?NarXZEGYspt_vLBIoj}LI[uBFvm}tbqvC|xyR~a{kob|HlctZslTGtPDhBKsNsoZPuH`U`Fqg{gKnGSHVLJ^O`zmNgMn~{rsQuoymw^JY?iUBvw_~mMr|GrPHTERS[MiNpY[Mm{ggHpzRaJaoFomtdaQ_?xuTRm}@KjU~RtPsAdxa|uHmy}n^i||FVL[eQAPrWfLm^ndczgF~Nk~aplQvTUpHvnTya]kOenZlLAQIm{lPl@CCTchvCF[fI{^zPkeYZTiamoEcKmBMfZhk_j_~Fjp|wPVZlkh_nHu]@tP|hS@^G^PdsQ~f[RqgTDqezxNFcaO}HZhb|MMiNSYSAnQWCDJukT~e|OTgc}sf[cnr?fyzTa|EwEtRG|I~|IO}O]S|rp]CQ}}DWhSjC_|z|oY|FYl@WkCOoPuWuqr{fJu?Brs^_EBI[@_OCKs}?]O`jnDiXBvaIWhhMAQDNb{U`bqVR}oqVAvR@AZHEBY@depD]OLh`kf^UsHhzKT}CS}HQKy}Q~AeMydXPQztWSSzDnghULQgMAmbWIZ|lWWeEXrE^EeNoZApooEmrXe{NAnoDf`m}UNlRdqQ@jOc~HLOMWs]IDqJHYoMziEedGBPOxOb?[X`KxkFRg@`mgFYnP{hSaxwZfBQqTm}_?RSEaQga]w[vxc]hMne}VfSlqUeMo_iqmd`ilnJXnhdj^EEFifvZyxYFRf^VaqBhLyrGlk~qowqzHOBlOwtx?i{m~`n^G?Yxzxux}b{LSlx]dS~thO^lYE}bzKmUEzwW^{rPGhbEov[Plv??xtyKJshbG`KuO?hjBdS@Ru}iGpvFXJRrvOlrKN?`I_n_tplk}kgwSXuKylXbRQ]]?a|{xiT[li?k]CJpwy^o@ebyGQrPfF`aszGKp]baIx~H?ElETtFh]dz[OjGl@C?]VDhr}OE@V]wLTc[WErXacM{We`F|utKKjgllAxvsVYBZ@HcuMgLboFHVZmi}eIXAIFhS@A@FGRbjeoJWZ_NKd^oEH`qgy`q[Tq{x?LRP|GfBFFJV|fgZs`MLbpPYUdIV^]mD@FG]pYAT^A^RNCcXVrPsgk{jTrAIQPs_`mD}rOqAZA[}RETFz]WkXFTz_m{N@{W@_fPKZLT`@aIqf|L^Mb|crNqZ{BVsijzpGPEKQQZGlApDn`ruH}cvF|iXcNqK}cxe_U~HRnKV}sCYb`D~oGvwG[Ca|UaybXea~DdD~LiIbGRxJ_VGheI{ika}KC[OZJLn^IBkPrQj_EuoFwZ}DpoBRcK]Q}?EmTv~i_Tul{bky?Iit~tgS|o}JL_VYcCQdjeJ_MfaA`FgCgc[Ii|CBHwq~nbJeYTK{e`CNstKfTKPzw{jdhp|qsZyP_FcugxCFNpKitlR~vUrx^NrSVsSTaEgnxZTmKc`R|lGJeX}ccKLsQZQhsFkeFd|ckHIVTlGMg`~uPwuHRJS_CPuN_ogXe{Ba}dO_UBhuNXby|h?JlgBIqMKx^_u{molgL[W_iavNQuOq?ap]PGB`clAicnl@k~pA?MWHEZ{HuTLsCpOxxrKlBh]FyMjLdFl|nMIvTHyGAlPogqfZ?PlvlFJvYnDQd}R@uAhtJmDfe|iJqdkYr}r@mEjjIetDl_I`TELfoR|qTBu@Tic[BaXjP?dCS~MUK[HPRI}OUOwAaf|_}HZzrwXvbnNgltjTwkBE~MztTQhtRSWoQHajMoVyBBA`kdgK~h`o[J`dm~pm]tk@i`[F~F]DBlJKklrkR]SNw@{aG~Vhl`KINsQkOy?WhcqUMTGDOM_]bUjVd|Yh_KUCCgIJ|LDIGZCPls{RzbVWVLEhHvWBzKq|^N?DyJB|__aCUjoEgsARki}j@DQXS`RNU|DJ^a~d{sh_Iu{ONcUtSrGWW@cvUjefHHi}eSSGrNtO?cTPBShLqzwMVjWQQCCFB^culBjZHEK_{dO~Q`YhJYFn]jq~XSnG@[lQr]eKrjXpG~L^h~tDgEma^AUFThlaR{xyuP@[^VFwXSeUbVetufa@dX]CLyAnDV@Bs[DnpeghJw^?UIana}r_CKGDySoRudklbgio}kIDpA@McDoPK?iYcG?_zOmnWfJp}a[JLR[stXMo?_^Ng[whQlrDbrawZeSZ~SJstIObdDSfAA{MV}?gNunLOnbMv_~KFQUAjIMj^GkoGxuYtYbGDImEYiwEMyTpMxN_LSnSMdl{bg@dtAnAMvhDTBR_FxoQgANniRqxd`pWv@rFJ|mWNWmh[GMJz_Nq`BIN@KsjMPASXORcdHjf~rJfgZYe_uulzqM_KdPlMsuvU^YJuLtofPhGonVOQxCMuXliNvJIaoC?hSxcxKVVxWlNs^ENDvCtSmO~WxI[itnjs^RDvI@KqG}YekaSbTaB]ki]XM@[ZnDAP~@|BzLRgOzmjmPkRE@_sobkT|SszXK[rZN?F]Z_u}Yue^[BZgLtR}FHzWyxWEX^wXC]MJmiVbQuBzkgRcKGUhOvUc_bga|Tx`KEM`JWEgTpFYVeXLCm|mctZR@uKTDeUONPozBeIkrY`cz]]~WPGMUf`MNUGHDbxZuO{gmsKYkAGRPqjc|_FtblEOwy}dnwCHo]PJhN~JoteaJ?dmYZeB^Xd?X^pOKDbOMF@Ugg^hETLdhwlA}PL@_ur|o{VZosP?ntJ_kG][g{Zq`Tu]dzQlSWiKfnxDnk}KOzp~tdFstMobmy[oPYjyOtUzMWdjcNSUAjRuqhLS@AwB^{BFnqjCmmlk?jpn}TksS{KcKkDboXiwK]qMVjm~V`LgWhjS^nLGwfhAYrjDSBL_{cRus~{?xar_xqPlArrYFd?pHKdMEZzzjJpfC?Hv}mAuIDkyBxFpxhstTx`IO{rp}XGuQ]VtbHerlRc_LFGWK[XluFcNGUtDYMZny[M^nVKVeMllQI[xtvwQnXFlWYqxZZFp_|]^oWX[{pOMpxXxvkbyJA[DrPzwD|LW|QcV{Nw~U^dgguSpG]ClmO@j_TENIGjPWwgdVbHganhM?ema|dBaqla|WBd`poj~klxaasKxGG^xbWquAl~_lKWxUkDFagMnE{zHug{b`A~IYcQYBF_E}wiA}K@yxWHrZ{[d~|ARsYsjeNWzkMs~IOqqp[yzDE|WFrivsidTcnbHFRoW@XpAV`lv_zj?B~tPCppRjgbbDTALeFaOf?VcjnKTQMLyp{NwdylHCqmo?oelhjWuXj~}{fpuX`fra?GNkDiChYgVSh{R[BgF~eQa^WVz}ATI_CpY?g_diae]|ijH`TyNIF}|D_xpmBq_JpKih{Ba|sWzhnAoyraiDvk`h{qbBfsylBGmRH}DRPdryEsSaKS~tIaeF[s]I~xxHVrcNe@Jjxa@jlhZueLQqHh_]twVMqG_EGuwyab{nxOF?`HCle}nBZzlTQjkLmoXbXhOtBglFoMz?eqre`HiE@vNwBulglmQjj]DB@pPkPUgA^sjOAUNdSu_`oAzar?n?eMnw{{hYmslYi[TnlJD'",...']charCodeAtUinyxpf',"for(;e<10359;c[e++]=p-=128,A=A?p-A&&A:p==34&&p)for(p=1;p<128;y=f.map((n,x)=>(U=r[n]*2+1,U=Math.log(U/(h-U)),t-=a[x]*U,U/500)),t=~-h/(1+Math.exp(t))|1,i=o%h<t,o=o%h+(i?t:h-t)*(o>>17)-!i*t,f.map((n,x)=>(U=r[n]+=(i*h/2-r[n]<<13)/((C[n]+=C[n]<5)+1/20)>>13,a[x]+=y[x]*(i-t/h))),p=p*2+i)for(f='010202103203210431053105410642065206541'.split(t=0).map((n,x)=>(U=0,[...n].map((n,x)=>(U=U*997+(c[e-n]|0)|0)),h*32-1&U*997+p+!!A*129)*12+x);o<h*32;o=o*64|M.charCodeAt(d++)&63);for(C=String.fromCharCode(...c);r=/[\0-#?@\\\\~]/.exec(C);)with(C.split(r))C=join(shift());return C")([],[],1<<17,[0,0,0,0,0,0,0,0,0,0,0,0],new Uint16Array(51e6).fill(1<<15),new Uint8Array(51e6),0,0,0,0));
4078
- // end of Crypto-JS
4079
- ///////////////////////////////////////////////////////////////////////////////
4080
- }
4081
4340
  }
4082
4341
  /**
4083
4342
  * LittleJS WebGL Interface
@@ -4087,6 +4346,7 @@ class Newgrounds
4087
4346
  * - Can be disabled with glEnable to revert to 2D canvas rendering
4088
4347
  * - Batches sprite rendering on GPU for incredibly fast performance
4089
4348
  * - Sprite transform math is done in the shader where possible
4349
+ * - Supports shadertoy style post processing shaders
4090
4350
  * @namespace WebGL
4091
4351
  */
4092
4352
 
@@ -4102,11 +4362,6 @@ let glCanvas;
4102
4362
  * @memberof WebGL */
4103
4363
  let glContext;
4104
4364
 
4105
- /** Main tile sheet texture automatically loaded by engine
4106
- * @type {WebGLTexture}
4107
- * @memberof WebGL */
4108
- let glTileTexture;
4109
-
4110
4365
  // WebGL internal variables not exposed to documentation
4111
4366
  let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
4112
4367
 
@@ -4115,32 +4370,34 @@ let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBat
4115
4370
  // Initalize WebGL, called automatically by the engine
4116
4371
  function glInit()
4117
4372
  {
4118
- // create the canvas and tile texture
4373
+ // create the canvas and textures
4119
4374
  glCanvas = document.createElement('canvas');
4120
- glContext = glCanvas.getContext('webgl', {antialias: false});
4121
- glTileTexture = glCreateTexture(tileImage);
4375
+ glContext = glCanvas.getContext('webgl2');
4122
4376
 
4123
4377
  // some browsers are much faster without copying the gl buffer so we just overlay it instead
4124
4378
  glOverlay && document.body.appendChild(glCanvas);
4125
4379
 
4126
4380
  // setup vertex and fragment shaders
4127
4381
  glShader = glCreateProgram(
4382
+ '#version 300 es\n' + // specify GLSL ES version
4128
4383
  'precision highp float;'+ // use highp for better accuracy
4129
4384
  'uniform mat4 m;'+ // transform matrix
4130
- 'attribute vec2 p,t;'+ // position, uv
4131
- 'attribute vec4 c,a;'+ // color, additiveColor
4132
- 'varying vec4 v,d,e;'+ // return uv, color, additiveColor
4385
+ 'in vec2 p,t;'+ // position, uv
4386
+ 'in vec4 c,a;'+ // color, additiveColor
4387
+ 'out vec4 v,d,e;'+ // return uv, color, additiveColor
4133
4388
  'void main(){'+ // shader entry point
4134
4389
  'gl_Position=m*vec4(p,1,1);'+ // transform position
4135
4390
  'v=vec4(t,p);d=c;e=a;'+ // pass stuff to fragment shader
4136
4391
  '}' // end of shader
4137
4392
  ,
4138
- 'precision highp float;'+ // use highp for better accuracy
4139
- 'varying vec4 v,d,e;'+ // uv, color, additiveColor
4140
- 'uniform sampler2D s;'+ // texture
4141
- 'void main(){'+ // shader entry point
4142
- 'gl_FragColor=texture2D(s,v.xy)*d+e;'+ // modulate texture by color plus additive
4143
- '}' // end of shader
4393
+ '#version 300 es\n' + // specify GLSL ES version
4394
+ 'precision highp float;'+ // use highp for better accuracy
4395
+ 'in vec4 v,d,e;'+ // uv, color, additiveColor
4396
+ 'uniform sampler2D s;'+ // texture
4397
+ 'out vec4 c;'+ // out color
4398
+ 'void main(){'+ // shader entry point
4399
+ 'c=texture(s,v.xy)*d+e;'+ // modulate texture by color plus additive
4400
+ '}' // end of shader
4144
4401
  );
4145
4402
 
4146
4403
  // init buffers
@@ -4161,7 +4418,7 @@ function glPreRender()
4161
4418
  // set up the shader
4162
4419
  glContext.useProgram(glShader);
4163
4420
  glContext.activeTexture(gl_TEXTURE0);
4164
- glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
4421
+ glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
4165
4422
  glContext.bindBuffer(gl_ARRAY_BUFFER, glArrayBuffer);
4166
4423
  glContext.bufferData(gl_ARRAY_BUFFER, gl_VERTEX_BUFFER_SIZE, gl_DYNAMIC_DRAW);
4167
4424
  glSetBlendMode();
@@ -4196,22 +4453,23 @@ function glPreRender()
4196
4453
  /** Set the WebGl blend mode, normally you should call setBlendMode instead
4197
4454
  * @param {Boolean} [additive=0]
4198
4455
  * @memberof WebGL */
4199
- function glSetBlendMode(additive)
4456
+ function glSetBlendMode(additive=0)
4200
4457
  {
4201
4458
  // setup blending
4202
4459
  glAdditive = additive;
4203
4460
  }
4204
4461
 
4205
- /** Set the WebGl texture, not normally necessary unless multiple tile sheets are used
4462
+ /** Set the WebGl texture, called automatically if using multiple textures
4206
4463
  * - This may also flush the gl buffer resulting in more draw calls and worse performance
4207
- * @param {WebGLTexture} [texture=glTileTexture]
4464
+ * @param {WebGLTexture} texture
4208
4465
  * @memberof WebGL */
4209
- function glSetTexture(texture=glTileTexture)
4466
+ function glSetTexture(texture)
4210
4467
  {
4211
4468
  // must flush cache with the old texture to set a new one
4212
- if (texture != glActiveTexture)
4213
- glFlush();
4469
+ if (texture == glActiveTexture)
4470
+ return;
4214
4471
 
4472
+ glFlush();
4215
4473
  glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = texture);
4216
4474
  }
4217
4475
 
@@ -4390,25 +4648,28 @@ function glInitPostProcess(shaderCode, includeOverlay)
4390
4648
  {
4391
4649
  ASSERT(!glPostShader); // can only have 1 post effects shader
4392
4650
 
4393
- if (!shaderCode) // default shader
4394
- shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture2D(iChannel0,p/iResolution.xy);}';
4651
+ if (!shaderCode) // default shader pass through
4652
+ shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
4395
4653
 
4396
4654
  // create the shader
4397
4655
  glPostShader = glCreateProgram(
4656
+ '#version 300 es\n' + // specify GLSL ES version
4398
4657
  'precision highp float;'+ // use highp for better accuracy
4399
- 'attribute vec2 p;'+ // position
4658
+ 'in vec2 p;'+ // position
4400
4659
  'void main(){'+ // shader entry point
4401
4660
  'gl_Position=vec4(p,1,1);'+ // set position
4402
4661
  '}' // end of shader
4403
4662
  ,
4663
+ '#version 300 es\n' + // specify GLSL ES version
4404
4664
  'precision highp float;'+ // use highp for better accuracy
4405
4665
  'uniform sampler2D iChannel0;'+ // input texture
4406
4666
  'uniform vec3 iResolution;'+ // size of output texture
4407
4667
  'uniform float iTime;'+ // time passed
4668
+ 'out vec4 c;'+ // out color
4408
4669
  '\n' + shaderCode + '\n'+ // insert custom shader code
4409
4670
  'void main(){'+ // shader entry point
4410
- 'mainImage(gl_FragColor,gl_FragCoord.xy);'+ // call post process function
4411
- 'gl_FragColor.a=1.;'+ // always use full alpha
4671
+ 'mainImage(c,gl_FragCoord.xy);'+ // call post process function
4672
+ 'c.a=1.;'+ // always use full alpha
4412
4673
  '}' // end of shader
4413
4674
  );
4414
4675
 
@@ -4481,7 +4742,6 @@ gl_ONE_MINUS_SRC_ALPHA = 771,
4481
4742
  gl_BLEND = 3042,
4482
4743
  gl_TEXTURE_2D = 3553,
4483
4744
  gl_UNSIGNED_BYTE = 5121,
4484
- gl_BYTE = 5120,
4485
4745
  gl_FLOAT = 5126,
4486
4746
  gl_RGBA = 6408,
4487
4747
  gl_NEAREST = 9728,
@@ -4493,7 +4753,6 @@ gl_TEXTURE_WRAP_T = 10243,
4493
4753
  gl_COLOR_BUFFER_BIT = 16384,
4494
4754
  gl_CLAMP_TO_EDGE = 33071,
4495
4755
  gl_TEXTURE0 = 33984,
4496
- gl_TEXTURE1 = 33985,
4497
4756
  gl_ARRAY_BUFFER = 34962,
4498
4757
  gl_STATIC_DRAW = 35044,
4499
4758
  gl_DYNAMIC_DRAW = 35048,
@@ -4504,7 +4763,6 @@ gl_LINK_STATUS = 35714,
4504
4763
  gl_UNPACK_FLIP_Y_WEBGL = 37440,
4505
4764
 
4506
4765
  // constants for batch rendering
4507
- gl_VERTICES_PER_QUAD = 6,
4508
4766
  gl_INDICIES_PER_VERT = 6,
4509
4767
  gl_MAX_BATCH = 1e5,
4510
4768
  gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
@@ -4541,7 +4799,7 @@ const engineName = 'LittleJS';
4541
4799
  * @type {String}
4542
4800
  * @default
4543
4801
  * @memberof Engine */
4544
- const engineVersion = '1.7.21';
4802
+ const engineVersion = '1.8.1';
4545
4803
 
4546
4804
  /** Frames per second to update objects
4547
4805
  * @type {Number}
@@ -4591,6 +4849,9 @@ let paused = 0;
4591
4849
  * @memberof Engine */
4592
4850
  function setPaused(_paused) { paused = _paused; }
4593
4851
 
4852
+ // Frame time tracking
4853
+ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
4854
+
4594
4855
  ///////////////////////////////////////////////////////////////////////////////
4595
4856
 
4596
4857
  /** Start up LittleJS engine with your callback functions
@@ -4599,52 +4860,13 @@ function setPaused(_paused) { paused = _paused; }
4599
4860
  * @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
4600
4861
  * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
4601
4862
  * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
4602
- * @param {String} [tileImageSource] - Tile image to use, everything starts when the image is finished loading
4863
+ * @param {String} [imageSources='tiles.png'] - Image to load
4603
4864
  * @memberof Engine */
4604
- function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, tileImageSource)
4865
+ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
4605
4866
  {
4606
- // init engine when tiles load or fail to load
4607
- tileImage.onerror = tileImage.onload = ()=>
4608
- {
4609
- // save tile image info
4610
- tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
4611
- debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
4612
-
4613
- // setup html
4614
- const styleBody =
4615
- 'margin:0;overflow:hidden;' + // fill the window
4616
- 'background:#000;' + // set background color
4617
- 'touch-action:none;' + // prevent mobile pinch to resize
4618
- 'user-select:none;' + // prevent mobile hold to select
4619
- '-webkit-user-select:none;' + // compatibility for ios
4620
- '-webkit-touch-callout:none'; // compatibility for ios
4621
- document.body.style = styleBody;
4622
- document.body.appendChild(mainCanvas = document.createElement('canvas'));
4623
- mainContext = mainCanvas.getContext('2d');
4624
-
4625
- // init stuff and start engine
4626
- debugInit();
4627
- glEnable && glInit();
4628
-
4629
- // create overlay canvas for hud to appear above gl canvas
4630
- document.body.appendChild(overlayCanvas = document.createElement('canvas'));
4631
- overlayContext = overlayCanvas.getContext('2d');
4632
-
4633
- // set canvas style
4634
- const styleCanvas =
4635
- 'position:absolute;' + // position canvas
4636
- 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center the canvas
4637
- (canvasPixelated?'image-rendering:pixelated':''); // set pixelated rendering
4638
- (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
4639
-
4640
- gameInit();
4641
- engineUpdate();
4642
- };
4643
-
4644
- // frame time tracking
4645
- let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
4867
+ ASSERT(Array.isArray(imageSources)); // pass in images as array
4646
4868
 
4647
- // main update loop
4869
+ // internal update loop for engine
4648
4870
  function engineUpdate(frameTimeMS=0)
4649
4871
  {
4650
4872
  // update time keeping
@@ -4675,9 +4897,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4675
4897
  }
4676
4898
  else
4677
4899
  {
4678
- // clear canvas and set size to same as window
4679
- mainCanvas.width = min(innerWidth, canvasMaxSize.x);
4680
- mainCanvas.height = min(innerHeight, canvasMaxSize.y);
4900
+ // clear canvas and set size to same as window
4901
+ mainCanvas.width = min(innerWidth, canvasMaxSize.x);
4902
+ mainCanvas.height = min(innerHeight, canvasMaxSize.y);
4681
4903
  }
4682
4904
 
4683
4905
  // clear overlay canvas and set size
@@ -4709,6 +4931,9 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4709
4931
  // update multiple frames if necessary in case of slow framerate
4710
4932
  for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
4711
4933
  {
4934
+ // increment frame and update time
4935
+ time = frame++ / frameRate;
4936
+
4712
4937
  // update game and objects
4713
4938
  inputUpdate();
4714
4939
  gameUpdate();
@@ -4756,8 +4981,51 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4756
4981
  requestAnimationFrame(engineUpdate);
4757
4982
  }
4758
4983
 
4759
- // set tile image source to load the image and start the engine
4760
- tileImageSource ? tileImage.src = tileImageSource : tileImage.onload();
4984
+ // setup html
4985
+ const styleBody =
4986
+ 'margin:0;overflow:hidden;' + // fill the window
4987
+ 'background:#000;' + // set background color
4988
+ 'touch-action:none;' + // prevent mobile pinch to resize
4989
+ 'user-select:none;' + // prevent mobile hold to select
4990
+ '-webkit-user-select:none;' + // compatibility for ios
4991
+ '-webkit-touch-callout:none'; // compatibility for ios
4992
+ document.body.style = styleBody;
4993
+ document.body.appendChild(mainCanvas = document.createElement('canvas'));
4994
+ mainContext = mainCanvas.getContext('2d');
4995
+
4996
+ // init stuff and start engine
4997
+ debugInit();
4998
+ glEnable && glInit();
4999
+
5000
+ // create overlay canvas for hud to appear above gl canvas
5001
+ document.body.appendChild(overlayCanvas = document.createElement('canvas'));
5002
+ overlayContext = overlayCanvas.getContext('2d');
5003
+
5004
+ // set canvas style
5005
+ const styleCanvas =
5006
+ 'position:absolute;' + // position
5007
+ 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center
5008
+ (canvasPixelated?'image-rendering:pixelated':''); // pixelated rendering
5009
+ (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
5010
+
5011
+ // load all of the images
5012
+ Promise.all(imageSources.map((src, textureIndex)=>
5013
+ new Promise((resolve, reject)=>
5014
+ {
5015
+ const image = new Image;
5016
+ image.onerror = image.onload = ()=>
5017
+ {
5018
+ textureInfos[textureIndex] = new TextureInfo(image);
5019
+ resolve();
5020
+ }
5021
+ image.src = src;
5022
+ })
5023
+ )).then(()=>
5024
+ {
5025
+ // start the engine
5026
+ gameInit();
5027
+ engineUpdate();
5028
+ });
4761
5029
  }
4762
5030
 
4763
5031
  // Called automatically by engine to setup render system
@@ -4795,9 +5063,6 @@ function engineObjectsUpdate()
4795
5063
 
4796
5064
  // remove destroyed objects
4797
5065
  engineObjects = engineObjects.filter(o=>!o.destroyed);
4798
-
4799
- // increment frame and update time
4800
- time = ++frame / frameRate;
4801
5066
  }
4802
5067
 
4803
5068
  /** Destroy and remove all objects
@@ -4840,196 +5105,6 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
4840
5105
  * - Export engine as a module with functions where necessary
4841
5106
  */
4842
5107
 
4843
- /** Set position of camera in world space
4844
- * @param {Vector2} pos
4845
- * @memberof Settings */
4846
- function setCameraPos(pos) { cameraPos = pos; }
4847
-
4848
- /** Set scale of camera in world space
4849
- * @param {Number} scale
4850
- * @memberof Settings */
4851
- function setCameraScale(scale) { cameraScale = scale; }
4852
-
4853
- /** Set max size of the canvas
4854
- * @param {Vector2} size
4855
- * @memberof Settings */
4856
- function setCanvasMaxSize(size) { canvasMaxSize = size; }
4857
-
4858
- /** Set fixed size of the canvas
4859
- * @param {Vector2} size
4860
- * @memberof Settings */
4861
- function setCanvasFixedSize(size) { canvasFixedSize = size; }
4862
-
4863
- /** Disables anti aliasing for pixel art if true
4864
- * @param {Boolean} pixelated
4865
- * @memberof Settings */
4866
- function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
4867
-
4868
- /** Set default font used for text rendering
4869
- * @param {String} font
4870
- * @memberof Settings */
4871
- function setFontDefault(font) { fontDefault = font; }
4872
-
4873
- /** Set if webgl rendering is enabled
4874
- * @param {Boolean} enable
4875
- * @memberof Settings */
4876
- function setGlEnable(enable) { glEnable = enable; }
4877
-
4878
- /** Set to not composite the WebGL canvas
4879
- * @param {Boolean} overlay
4880
- * @memberof Settings */
4881
- function setGlOverlay(overlay) { glOverlay = overlay; }
4882
-
4883
- /** Set default size of tiles in pixels
4884
- * @param {Vector2} size
4885
- * @memberof Settings */
4886
- function setTileSizeDefault(size) { tileSizeDefault = size; }
4887
-
4888
- /** Set to prevent tile bleeding from neighbors in pixels
4889
- * @param {Number} scale
4890
- * @memberof Settings */
4891
- function setTileFixBleedScale(scale) { tileFixBleedScale = scale; }
4892
-
4893
- /** Set if collisions between objects are enabled
4894
- * @param {Boolean} enable
4895
- * @memberof Settings */
4896
- function setEnablePhysicsSolver(enable) { enablePhysicsSolver = enable; }
4897
-
4898
- /** Set default object mass for collison calcuations
4899
- * @param {Number} mass
4900
- * @memberof Settings */
4901
- function setObjectDefaultMass(mass) { objectDefaultMass = mass; }
4902
-
4903
- /** Set how much to slow velocity by each frame
4904
- * @param {Number} damping
4905
- * @memberof Settings */
4906
- function setObjectDefaultDamping(damp) { objectDefaultDamping = damp; }
4907
-
4908
- /** Set how much to slow angular velocity each frame
4909
- * @param {Number} damping
4910
- * @memberof Settings */
4911
- function setObjectDefaultAngleDamping(damp) { objectDefaultAngleDamping = damp; }
4912
-
4913
- /** Set how much to bounce when a collision occur
4914
- * @param {Number} elasticity
4915
- * @memberof Settings */
4916
- function setObjectDefaultElasticity(elasticity) { objectDefaultElasticity = elasticity; }
4917
-
4918
- /** Set how much to slow when touching
4919
- * @param {Number} friction
4920
- * @memberof Settings */
4921
- function setObjectDefaultFriction(friction) { objectDefaultFriction = friction; }
4922
-
4923
- /** Set max speed to avoid fast objects missing collisions
4924
- * @param {Number} speed
4925
- * @memberof Settings */
4926
- function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
4927
-
4928
- /** Set how much gravity to apply to objects along the Y axis
4929
- * @param {Number} gravity
4930
- * @memberof Settings */
4931
- function setGravity(g) { gravity = g; }
4932
-
4933
- /** Set to scales emit rate of particles
4934
- * @param {Number} scale
4935
- * @memberof Settings */
4936
- function setParticleEmitRateScale(scale) { particleEmitRateScale = scale; }
4937
-
4938
- /** Set if gamepads are enabled
4939
- * @param {Boolean} enable
4940
- * @memberof Settings */
4941
- function setGamepadsEnable(enable) { gamepadsEnable = enable; }
4942
-
4943
- /** Set if the dpad input is also routed to the left analog stick
4944
- * @param {Boolean} enable
4945
- * @memberof Settings */
4946
- function setGamepadDirectionEmulateStick(enable) { gamepadDirectionEmulateStick = enable; }
4947
-
4948
- /** Set if true the WASD keys are also routed to the direction keys
4949
- * @param {Boolean} enable
4950
- * @memberof Settings */
4951
- function setInputWASDEmulateDirection(enable) { inputWASDEmulateDirection = enable; }
4952
-
4953
- /** Set if touch gamepad should appear on mobile devices
4954
- * @param {Boolean} enable
4955
- * @memberof Settings */
4956
- function setTouchGamepadEnable(enable) { touchGamepadEnable = enable; }
4957
-
4958
- /** Set if touch gamepad should be analog stick or 8 way dpad
4959
- * @param {Boolean} analog
4960
- * @memberof Settings */
4961
- function setTouchGamepadAnalog(analog) { touchGamepadAnalog = analog; }
4962
-
4963
- /** Set size of virutal gamepad for touch devices in pixels
4964
- * @param {Number} size
4965
- * @memberof Settings */
4966
- function setTouchGamepadSize(size) { touchGamepadSize = size; }
4967
-
4968
- /** Set transparency of touch gamepad overlay
4969
- * @param {Number} alpha
4970
- * @memberof Settings */
4971
- function setTouchGamepadAlpha(alpha) { touchGamepadAlpha = alpha; }
4972
-
4973
- /** Set to allow vibration hardware if it exists
4974
- * @param {Boolean} enable
4975
- * @memberof Settings */
4976
- function setVibrateEnable(enable) { vibrateEnable = enable; }
4977
-
4978
- /** Set to disable all audio code
4979
- * @param {Boolean} enable
4980
- * @memberof Settings */
4981
- function setSoundEnable(enable) { soundEnable = enable; }
4982
-
4983
- /** Set volume scale to apply to all sound, music and speech
4984
- * @param {Number} volume
4985
- * @memberof Settings */
4986
- function setSoundVolume(volume) { soundVolume = volume; }
4987
-
4988
- /** Set default range where sound no longer plays
4989
- * @param {Number} range
4990
- * @memberof Settings */
4991
- function setSoundDefaultRange(range) { soundDefaultRange = range; }
4992
-
4993
- /** Set default range percent to start tapering off sound
4994
- * @param {Number} taper
4995
- * @memberof Settings */
4996
- function setSoundDefaultTaper(taper) { soundDefaultTaper = taper; }
4997
-
4998
- /** Set how long to show medals for in seconds
4999
- * @param {Number} time
5000
- * @memberof Settings */
5001
- function setMedalDisplayTime(time) { medalDisplayTime = time; }
5002
-
5003
- /** Set how quickly to slide on/off medals in seconds
5004
- * @param {Number} time
5005
- * @memberof Settings */
5006
- function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
5007
-
5008
- /** Set size of medal display
5009
- * @param {Vector2} size
5010
- * @memberof Settings */
5011
- function setMedalDisplaySize(size) { medalDisplaySize = size; }
5012
-
5013
- /** Set size of icon in medal display
5014
- * @param {Number} size
5015
- * @memberof Settings */
5016
- function setMedalDisplayIconSize(size) { medalDisplayIconSize = size; }
5017
-
5018
- /** Set to stop medals from being unlockable
5019
- * @param {Boolean} preventUnlock
5020
- * @memberof Settings */
5021
- function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUnlock; }
5022
-
5023
- /** Set if watermark with FPS should be shown
5024
- * @param {Boolean} show
5025
- * @memberof Debug */
5026
- function setShowWatermark(show) { showWatermark = show; }
5027
-
5028
- /** Set key code used to toggle debug mode, Esc by default
5029
- * @param {Number} key
5030
- * @memberof Debug */
5031
- function setDebugKey(key) { debugKey = key; }
5032
-
5033
5108
  export {
5034
5109
  // Setters for global variables
5035
5110
  setCameraPos,
@@ -5164,7 +5239,10 @@ export {
5164
5239
  EngineObject,
5165
5240
 
5166
5241
  // Draw
5167
- tileImage,
5242
+ textureInfos,
5243
+ tile,
5244
+ TileInfo,
5245
+ TextureInfo,
5168
5246
  mainCanvas,
5169
5247
  mainContext,
5170
5248
  overlayCanvas,