q5play 4.0.8 → 4.1.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 (4) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/q5play.d.ts +165 -68
  4. package/q5play.js +135 -112
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Visit [q5play.org][]! 🎮🧑‍💻
4
4
 
5
- [q5play][] is a beginner friendly, powerful, and cutting edge game engine for the web.
5
+ [q5play][] is a beginner friendly and powerful game engine for the web.
6
6
 
7
7
  It uses [q5.js WebGPU][] for graphics and [Box2D v3 WASM][] for physics.
8
8
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "q5play",
3
- "version": "4.0.8",
3
+ "version": "4.1.1",
4
4
  "author": "quinton-ashley",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
- "description": "A web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
6
+ "description": "A beginner friendly, web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
7
7
  "main": "q5play.js",
8
8
  "types": "q5play.d.ts",
9
9
  "homepage": "https://q5play.org",
@@ -25,6 +25,6 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "box2d3-wasm": "^5.2.0",
28
- "q5": "^4.4.4"
28
+ "q5": "^4.6.3"
29
29
  }
30
30
  }
package/q5play.d.ts CHANGED
@@ -33,10 +33,10 @@ declare global {
33
33
  * Friendly rounding makes some Sprite getters return nice rounded numbers
34
34
  * if a decimal value is within linear slop range (+/-0.005) or
35
35
  * angular slop range (+/-0.000582 radians) of a whole number.
36
- *
36
+ *
37
37
  * This is because Box2D physics calculations can result in
38
38
  * floating point drift, which beginners wouldn't expect.
39
- *
39
+ *
40
40
  * Setting to false can slightly improve performance.
41
41
  * @default true
42
42
  */
@@ -70,6 +70,11 @@ declare global {
70
70
  * @default false
71
71
  */
72
72
  renderStats: boolean;
73
+ /**
74
+ * "Made with q5play" [splash screen](https://en.wikipedia.org/wiki/Splash_screen) displayed during
75
+ * initial page load by default.
76
+ */
77
+ splashScreen(): Promise<void>;
73
78
  /**
74
79
  * Runs automatically before each draw function call.
75
80
  */
@@ -81,6 +86,16 @@ declare global {
81
86
  }
82
87
  const q5play: Q5Play;
83
88
 
89
+ /**
90
+ * Box2D v3 ported to WASM is the physics engine that
91
+ * q5play uses for its physics simulation.
92
+ *
93
+ * This variable enables direct access to the Box2D API for
94
+ * advanced users who want to do things that aren't wrapped
95
+ * by q5play.
96
+ */
97
+ const Box2D: any;
98
+
84
99
  /**
85
100
  * Don't create Shapes directly; use `sprite.addCollider()`
86
101
  * or `sprite.addSensor()` instead.
@@ -107,7 +122,7 @@ declare global {
107
122
  }
108
123
 
109
124
  /**
110
- * Sensor are added to a sprite's physics body to detect overlaps
125
+ * Sensor are added to a sprite's physics body to detect overlaps
111
126
  * without causing physical collisions.
112
127
  *
113
128
  * Don't create Sensors directly; use Sprite.addSensor() instead.
@@ -191,7 +206,7 @@ declare global {
191
206
  * - "!" plays it backwards
192
207
  * - ">" or "<" horizontally flips it
193
208
  * - "^" vertically flips it
194
- *
209
+ *
195
210
  * @param name the name of the animation to play
196
211
  * @returns A promise that fulfills when the animation completes
197
212
  */
@@ -1209,7 +1224,7 @@ declare global {
1209
1224
  }
1210
1225
  /**
1211
1226
  * Stores animations.
1212
- *
1227
+ *
1213
1228
  * Used internally to create `sprite.anis` and `group.anis`.
1214
1229
  *
1215
1230
  * In instances of this class, the keys are animation names,
@@ -1224,7 +1239,7 @@ declare global {
1224
1239
  /**
1225
1240
  * Cuts sprite sheet frames into separate images, instead of rendering
1226
1241
  * sections of the sprite sheet.
1227
- *
1242
+ *
1228
1243
  * Avoids edge bleeding artifacts caused by rotation and scaling,
1229
1244
  * but uses more memory and may cause longer load times.
1230
1245
  */
@@ -1917,8 +1932,8 @@ declare global {
1917
1932
  * @param startPos starting position of the ray cast
1918
1933
  * @param direction direction of the ray
1919
1934
  * @param maxDistance max distance the ray should check
1920
- * @param limiter limiter function that's run each time the ray intersects a sprite, return true to stop the ray
1921
- * @returns An array of sprites that the ray cast hit, sorted by distance. The sprite closest to the starting point will be at index 0.
1935
+ * @param limiter callback that's run each time the ray intersects a sprite, receives an intersected sprite as an input parameter, return true to stop the ray
1936
+ * @returns An array of sprites that the ray cast hit, sorted by distance. The sprite closest to the starting point will be at index 0. If a limiter is provided, this array includes the sprite that caused the ray to stop.
1922
1937
  */
1923
1938
  rayCastAll(startPos: any, direction: number, maxDistance: number, limiter?: Function): Sprite[];
1924
1939
  /**
@@ -2081,7 +2096,7 @@ declare global {
2081
2096
  get reactionTorque(): any;
2082
2097
  /**
2083
2098
  * The amount of force that must be applied to the joint before it breaks.
2084
- *
2099
+ *
2085
2100
  * Setting the threshold too high leads to instability. Use
2086
2101
  * `sprite.addCollider` to simulate unbreakable bonds between shapes.
2087
2102
  * @default 500
@@ -2100,7 +2115,7 @@ declare global {
2100
2115
  /**
2101
2116
  * This function is run when the joint's reaction force exceeds the
2102
2117
  * force threshold or its reaction torque exceeds the torque threshold.
2103
- *
2118
+ *
2104
2119
  * By default, the sprites' speed and rotation speed are set to 0
2105
2120
  * and the joint is deleted, simulating a break.
2106
2121
  */
@@ -2142,7 +2157,7 @@ declare global {
2142
2157
  get currentLength(): number;
2143
2158
  /**
2144
2159
  * The target length of the joint between the two joint anchors.
2145
- *
2160
+ *
2146
2161
  * It's set to the current distance between the two sprites
2147
2162
  * when the joint is created.
2148
2163
  */
@@ -2164,9 +2179,10 @@ declare global {
2164
2179
  */
2165
2180
  get maxLength(): number;
2166
2181
  /**
2167
- * Accepts an array that contains the minimum and maximum length limits.
2182
+ * Accepts a number to set a symmetric range
2183
+ * or an array with the minimum and maximum length limits.
2168
2184
  */
2169
- set range(val: [number, number]);
2185
+ set range(val: [number, number] | number);
2170
2186
  /**
2171
2187
  * Whether spring behavior is enabled for the joint.
2172
2188
  * @default true
@@ -2184,7 +2200,7 @@ declare global {
2184
2200
  /**
2185
2201
  * Damping is a 0-1 ratio describing how quickly the joint loses
2186
2202
  * vibrational energy.
2187
- *
2203
+ *
2188
2204
  * 0.0 means no damping, 1.0 means critical damping, which will stop
2189
2205
  * the joint from vibrating at all.
2190
2206
  *
@@ -2238,39 +2254,37 @@ declare global {
2238
2254
  get angle(): number;
2239
2255
  set angle(val: number);
2240
2256
  /**
2241
- * The current distance between the two joint anchors.
2242
- * @readonly
2243
- */
2244
- get currentLength(): number;
2245
- /**
2246
- * The target length of the joint between the two joint anchors.
2247
- *
2248
- * It's set to the current distance between the two sprites
2249
- * when the joint is created.
2250
- */
2251
- get length(): number;
2252
- set length(val: number);
2253
- /**
2254
- * Whether the joint's length limits are enabled.
2255
- * When enabled a min/max length range constrains the joint.
2257
+ * Whether the joint's suspension limits are enabled.
2258
+ * When enabled a min/max distance from resting constrains the joint.
2256
2259
  * @default false
2257
2260
  */
2258
2261
  get limitsEnabled(): boolean;
2259
2262
  set limitsEnabled(val: boolean);
2260
2263
  /**
2261
- * The minimum length allowed when limits are enabled.
2264
+ * The minimum distance the wheel's suspension can contract
2265
+ * from 0, which represents the resting position,
2266
+ * when limits are enabled.
2267
+ * @readonly
2262
2268
  */
2263
- get minLength(): number;
2269
+ get lowerLimit(): number;
2264
2270
  /**
2265
- * The maximum length allowed when limits are enabled.
2271
+ * The maximum distance the wheel's suspension can extend
2272
+ * from 0, which represents the resting position,
2273
+ * when limits are enabled.
2274
+ * @readonly
2266
2275
  */
2267
- get maxLength(): number;
2276
+ get upperLimit(): number;
2268
2277
  /**
2269
- * Accepts an array that contains the minimum and maximum length limits.
2278
+ * The distance the wheel's suspension can contract or extend
2279
+ * from 0, which represents the resting position.
2280
+ *
2281
+ * Accepts a number to set a symmetric range
2282
+ * or an array with the minimum and maximum length limits.
2270
2283
  */
2271
- set range(val: [number, number]);
2284
+ set range(val: [number, number] | number);
2272
2285
  /**
2273
- * Whether spring behavior is enabled for the joint.
2286
+ * Whether the wheel joint has suspension,
2287
+ * which can make it ride smoother over bumps.
2274
2288
  * @default true
2275
2289
  */
2276
2290
  get springEnabled(): boolean;
@@ -2325,7 +2339,7 @@ declare global {
2325
2339
  * Hinge joints attach two sprites together at a pivot point,
2326
2340
  * constraining them to rotate around this point, like a hinge.
2327
2341
  *
2328
- * A known as a revolute joint.
2342
+ * Also known as a revolute joint.
2329
2343
  *
2330
2344
  * @param spriteA
2331
2345
  * @param spriteB
@@ -2349,13 +2363,57 @@ declare global {
2349
2363
  */
2350
2364
  get maxAngle(): number;
2351
2365
  /**
2352
- * Accepts an array that contains the lower and upper limits of rotation.
2366
+ * Accepts a number to set a symmetric range
2367
+ * or an array with the lower and upper limits of rotation.
2353
2368
  */
2354
- set range(val: [number, number]);
2369
+ set range(val: [number, number] | number);
2355
2370
  /**
2356
- * Read only. The joint's current angle of rotation.
2371
+ * The joint's current angle of rotation.
2372
+ * @readonly
2357
2373
  */
2358
2374
  get angle(): number;
2375
+ /**
2376
+ * Whether spring behavior is enabled.
2377
+ * @default false
2378
+ */
2379
+ get springEnabled(): boolean;
2380
+ set springEnabled(val: boolean);
2381
+ /**
2382
+ * The springiness of the joint, a 0-1 ratio.
2383
+ *
2384
+ * 0 is rigid, 0.5 is bouncy, 1 is loose.
2385
+ * @default 0
2386
+ */
2387
+ get springiness(): number;
2388
+ set springiness(val: number);
2389
+ /**
2390
+ * Damping ratio, 0-1. Higher values reduce oscillation faster.
2391
+ * @default 0
2392
+ */
2393
+ get damping(): number;
2394
+ set damping(val: number);
2395
+ /**
2396
+ * Whether the joint's motor is enabled.
2397
+ * @default false
2398
+ */
2399
+ get motorEnabled(): boolean;
2400
+ set motorEnabled(val: boolean);
2401
+ /**
2402
+ * Motor speed.
2403
+ * @default 0
2404
+ */
2405
+ get speed(): number;
2406
+ set speed(val: number);
2407
+ /**
2408
+ * Maximum torque the motor can apply.
2409
+ */
2410
+ get maxPower(): number;
2411
+ set maxPower(val: number);
2412
+ /**
2413
+ * The current torque being applied by the motor.
2414
+ * @readonly
2415
+ */
2416
+ get power(): number;
2359
2417
  }
2360
2418
 
2361
2419
  class SliderJoint extends Joint {
@@ -2370,44 +2428,83 @@ declare global {
2370
2428
  */
2371
2429
  constructor(spriteA: Sprite, spriteB: Sprite);
2372
2430
  /**
2373
- * The joint's range of translation. Setting the range
2374
- * changes the joint's upper and lower limits.
2375
- * @default undefined
2431
+ * The current displacement of spriteB along the slide axis.
2432
+ * @readonly
2376
2433
  */
2377
- get range(): number;
2378
- set range(val: number);
2434
+ get translation(): number;
2379
2435
  /**
2380
- * The mathematical upper (not positionally higher)
2381
- * limit of translation.
2382
- * @default undefined
2436
+ * Whether the joint's translation limits are enabled.
2437
+ * @default false
2383
2438
  */
2384
- get upperLimit(): number;
2385
- set upperLimit(val: number);
2439
+ get limitsEnabled(): boolean;
2440
+ set limitsEnabled(val: boolean);
2386
2441
  /**
2387
- * The mathematical lower (not positionally lower)
2388
- * limit of translation.
2389
- * @default undefined
2442
+ * The mathematical lower limit of translation.
2443
+ * @readonly
2390
2444
  */
2391
2445
  get lowerLimit(): number;
2392
- set lowerLimit(val: number);
2393
- }
2394
-
2395
- class RopeJoint extends Joint {
2396
2446
  /**
2397
- * A Rope joint prevents two sprites from going further
2398
- * than a certain distance from each other, which is
2399
- * defined by the max length of the rope, but they do allow
2400
- * the sprites to get closer together.
2447
+ * The mathematical upper limit of translation.
2448
+ * @readonly
2449
+ */
2450
+ get upperLimit(): number;
2451
+ /**
2452
+ * Accepts a number to set a symmetric range
2453
+ * or an array with the lower and upper translation limits.
2454
+ */
2455
+ set range(val: [number, number] | number);
2456
+ /**
2457
+ * Alias for range.
2458
+ */
2459
+ set limits(val: [number, number] | number);
2460
+ /**
2461
+ * Whether spring behavior is enabled.
2462
+ * @default false
2463
+ */
2464
+ get springEnabled(): boolean;
2465
+ set springEnabled(val: boolean);
2466
+ /**
2467
+ * The springiness of the joint, a 0-1 ratio.
2401
2468
  *
2402
- * @param spriteA
2403
- * @param spriteB
2469
+ * 0 is rigid, 0.5 is bouncy, 1 is loose.
2470
+ * @default 0
2404
2471
  */
2405
- constructor(spriteA: Sprite, spriteB: Sprite);
2472
+ get springiness(): number;
2473
+ set springiness(val: number);
2406
2474
  /**
2407
- * The maximum length of the rope.
2475
+ * Damping ratio, 0-1. Higher values reduce oscillation faster.
2476
+ * @default 0
2408
2477
  */
2409
- get maxLength(): number;
2410
- set maxLength(val: number);
2478
+ get damping(): number;
2479
+ set damping(val: number);
2480
+ /**
2481
+ * Whether the joint's motor is enabled.
2482
+ * @default true
2483
+ */
2484
+ get motorEnabled(): boolean;
2485
+ set motorEnabled(val: boolean);
2486
+ /**
2487
+ * Motor speed.
2488
+ * @default 0
2489
+ */
2490
+ get speed(): number;
2491
+ set speed(val: number);
2492
+ /**
2493
+ * Maximum force the motor can apply.
2494
+ * @default 10
2495
+ */
2496
+ get maxPower(): number;
2497
+ set maxPower(val: number);
2498
+ /**
2499
+ * The current motor force being applied.
2500
+ * @readonly
2501
+ */
2502
+ get power(): number;
2503
+ /**
2504
+ * The current sliding speed of the joint.
2505
+ * @readonly
2506
+ */
2507
+ get energy(): number;
2411
2508
  }
2412
2509
 
2413
2510
  class GrabberJoint extends Joint {
@@ -2431,7 +2528,7 @@ declare global {
2431
2528
  set target(pos: any);
2432
2529
  /**
2433
2530
  * The maximum spring force that the joint can exert on the sprite.
2434
- *
2531
+ *
2435
2532
  * By default it's 500 * the sprite's mass.
2436
2533
  */
2437
2534
  get maxForce(): number;
package/q5play.js CHANGED
@@ -12,13 +12,12 @@
12
12
  * |__/ |__/ \______/
13
13
  *
14
14
  * @package q5play
15
- * @version 4.0
15
+ * @version 4.1
16
16
  * @author quinton-ashley
17
17
  * @website https://q5play.org
18
18
  */
19
19
 
20
- // will use semver minor after v4 is released
21
- let q5play_version = '4.0';
20
+ let q5play_version = '4.1';
22
21
 
23
22
  if (typeof globalThis.Q5 == 'undefined') {
24
23
  console.error('q5play requires q5.js to be loaded first. Visit https://q5js.org to learn more.');
@@ -324,6 +323,43 @@ async function q5playPreSetup(q) {
324
323
  set friendlyRounding(val) {
325
324
  friendlyRounding = val;
326
325
  }
326
+
327
+ async splashScreen() {
328
+ if (document.getElementById('made-with-q5play')) return;
329
+ if (!using_p5v2) $._incrementPreload();
330
+ let d = document.createElement('div');
331
+ d.id = 'made-with-q5play';
332
+ d.style =
333
+ 'position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; background-color: black;';
334
+ let logo = document.createElement('img');
335
+ logo.style = `position: absolute; top: 50%; left: 50%; width: 80vmin; height: 40vmin; margin-left: -40vmin; margin-top: -20vmin; z-index: 1001; opacity: 1; scale: 1; transition: scale 1.5s, opacity 0.4s ease-in-out;`;
336
+ logo.onerror = () => {
337
+ logo.style.imageRendering = 'pixelated';
338
+ logo.src = `data:image/png;base64,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`;
339
+ };
340
+ let src = window._q5play_intro_image;
341
+ if (src == '' || src?.includes('made_with_q5play')) {
342
+ if (src.includes('bit.') || src.includes('pixel')) {
343
+ logo.style.imageRendering = 'pixelated';
344
+ }
345
+ logo.src = src;
346
+ } else {
347
+ logo.src = 'https://q5play.org/assets/made_with_q5play.webp';
348
+ }
349
+ await new Promise((r) => (logo.onload = r));
350
+ d.append(logo);
351
+ document.body.append(d);
352
+ await $.delay();
353
+ logo.offsetHeight; // trigger css reflow
354
+ logo.style.scale = 1.2;
355
+ await $.delay(1100);
356
+ logo.style.opacity = 0;
357
+ await $.delay(400);
358
+ d.style.display = 'none';
359
+ d.remove();
360
+ document.getElementById('made-with-q5play')?.remove();
361
+ if (!using_p5v2) $._decrementPreload();
362
+ }
327
363
  };
328
364
 
329
365
  $.q5play = new $.Q5Play();
@@ -345,7 +381,10 @@ async function q5playPreSetup(q) {
345
381
  $.imageMode($.CENTER);
346
382
 
347
383
  const ZERO_VEC = new b2Vec2(0, 0),
348
- ZERO_ROT = b2MakeRot(0);
384
+ ZERO_ROT = b2MakeRot(0),
385
+ NULL_FILTER = new b2QueryFilter();
386
+ NULL_FILTER.categoryBits = 0xffffffff;
387
+ NULL_FILTER.maskBits = 0xffffffff;
349
388
 
350
389
  let meterSize = 60;
351
390
 
@@ -1005,7 +1044,6 @@ async function q5playPreSetup(q) {
1005
1044
  if (!group.visualOnly) {
1006
1045
  const def = new b2DefaultBodyDef();
1007
1046
  def.type = bodyTypes[this._phys];
1008
- def.allowFastRotation = true;
1009
1047
  this.bdID = b2CreateBody(wID, def);
1010
1048
  this._physicsEnabled = true;
1011
1049
 
@@ -1325,7 +1363,7 @@ async function q5playPreSetup(q) {
1325
1363
  if (typeof a3 == 'string') {
1326
1364
  rr = a4 || 0;
1327
1365
  path = getRegularPolygon(a2 - rr, a3);
1328
- } else if (Array.isArray(a2)) {
1366
+ } else if (typeof a2 == 'object') {
1329
1367
  path = a2;
1330
1368
  rr = a3;
1331
1369
  } else {
@@ -1336,6 +1374,12 @@ async function q5playPreSetup(q) {
1336
1374
  rr ??= 0;
1337
1375
 
1338
1376
  if (path) {
1377
+ if (!Array.isArray(path)) {
1378
+ let tmp = path.absoluteAngles;
1379
+ path = path.array;
1380
+ path.absoluteAngles = tmp;
1381
+ }
1382
+
1339
1383
  let start,
1340
1384
  vecs = [{ x: 0, y: 0 }],
1341
1385
  isLoop = (vecs.isLoop = false);
@@ -4743,6 +4787,27 @@ async function q5playPreSetup(q) {
4743
4787
  $.Visuals.prototype.addAni = $.Group.prototype.addAni = $.Sprite.prototype.addAni;
4744
4788
  $.Visuals.prototype.addAnis = $.Group.prototype.addAnis = $.Sprite.prototype.addAnis;
4745
4789
 
4790
+ class RayInfo {
4791
+ constructor(sprite, px, py, nx, ny, fraction, maxDistance) {
4792
+ this.sprite = sprite;
4793
+ this._px = px;
4794
+ this._py = py;
4795
+ this._nx = nx;
4796
+ this._ny = ny;
4797
+ this.distance = fraction * maxDistance;
4798
+ }
4799
+
4800
+ get intersect() {
4801
+ if (!this._intersect) this._intersect = scaleFrom(this._px, this._py);
4802
+ return this._intersect;
4803
+ }
4804
+
4805
+ get incidence() {
4806
+ if (this._incidence === undefined) this._incidence = $.atan2(this._ny, this._nx);
4807
+ return this._incidence;
4808
+ }
4809
+ }
4810
+
4746
4811
  $.World = class {
4747
4812
  constructor() {
4748
4813
  this.mod = {};
@@ -5013,14 +5078,9 @@ async function q5playPreSetup(q) {
5013
5078
 
5014
5079
  const point = scaleTo(x, y),
5015
5080
  proxy = b2MakeProxy(point, 1, radius / meterSize),
5016
- filter = new b2QueryFilter(),
5017
5081
  shapes = [];
5018
5082
 
5019
- // no filter
5020
- filter.categoryBits = 0xffffffff;
5021
- filter.maskBits = 0xffffffff;
5022
-
5023
- b2World_OverlapShape(wID, proxy, filter, (overlapResult) => {
5083
+ b2World_OverlapShape(wID, proxy, NULL_FILTER, (overlapResult) => {
5024
5084
  const { shapeId } = overlapResult;
5025
5085
  if (shapeId) {
5026
5086
  shapes.push(shapeDict[shapeId.index1]);
@@ -5029,7 +5089,6 @@ async function q5playPreSetup(q) {
5029
5089
  });
5030
5090
 
5031
5091
  proxy.delete();
5032
- filter.delete();
5033
5092
 
5034
5093
  if (!shapes.length) return [];
5035
5094
 
@@ -5099,57 +5158,50 @@ async function q5playPreSetup(q) {
5099
5158
  }
5100
5159
 
5101
5160
  rayCastAll(startPos, direction, maxDistance, limiter) {
5102
- // TODO
5103
- return [];
5104
- let start = scaleTo(startPos.x, startPos.y);
5105
-
5106
- let end;
5107
- if (typeof arguments[1] == 'number') {
5108
- end = scaleTo(startPos.x + maxDistance * $.cos(direction), startPos.y + maxDistance * $.sin(direction));
5161
+ const startX = startPos.x ?? startPos[0];
5162
+ const startY = startPos.y ?? startPos[1];
5163
+
5164
+ let endX, endY;
5165
+ if (typeof direction == 'number') {
5166
+ maxDistance ??= 10000;
5167
+ endX = startX + maxDistance * $.cos(direction);
5168
+ endY = startY + maxDistance * $.sin(direction);
5109
5169
  } else {
5110
- let endPos = arguments[1];
5111
- limiter ??= arguments[2];
5112
- end = scaleTo(endPos.x, endPos.y);
5113
- }
5114
-
5115
- let results = [];
5116
- let maxFraction = 1;
5117
-
5118
- super.rayCast(start, end, function (fixture, point, normal, fraction) {
5119
- let sprite = fixture.getBody().sprite;
5120
-
5121
- let shouldLimit = limiter && limiter(sprite);
5122
-
5123
- // TODO provide advanced info: point and angle of intersection
5124
- results.push({
5125
- sprite,
5126
- // point,
5127
- // normal,
5128
- fraction
5129
- });
5170
+ const endPos = direction;
5171
+ limiter = maxDistance;
5172
+ endX = endPos.x ?? endPos[0];
5173
+ endY = endPos.y ?? endPos[1];
5174
+ }
5175
+
5176
+ const origin = scaleTo(startX, startY);
5177
+ const translation = scaleTo(endX - startX, endY - startY);
5178
+
5179
+ const results = [];
5180
+
5181
+ b2World_CastRay(wID, origin, translation, NULL_FILTER, (castResult) => {
5182
+ const shape = shapeDict[castResult.shapeId.index1];
5183
+ if (shape?.sprite) {
5184
+ const s = shape.sprite;
5185
+
5186
+ s.ray = new RayInfo(
5187
+ s,
5188
+ castResult.point.x,
5189
+ castResult.point.y,
5190
+ castResult.normal.x,
5191
+ castResult.normal.y,
5192
+ castResult.fraction,
5193
+ maxDistance
5194
+ );
5195
+ results.push(s);
5130
5196
 
5131
- // limit the ray cast so it can't go beyond this sprite
5132
- if (shouldLimit) {
5133
- if (fraction < maxFraction) {
5134
- maxFraction = fraction;
5135
- }
5136
- return fraction;
5197
+ if (limiter && limiter(s)) return 0; // stop raycast
5137
5198
  }
5138
- return 1; // keep casting the full length of the ray
5199
+ return 1; // continue to collect all hits
5139
5200
  });
5140
5201
 
5141
- // sort results by the distance from the starting position
5142
- results.sort((a, b) => a.fraction - b.fraction);
5143
-
5144
- let sprites = [];
5145
-
5146
- for (let res of results) {
5147
- if (res.fraction <= maxFraction) {
5148
- sprites.push(res.sprite);
5149
- }
5150
- }
5151
-
5152
- return sprites;
5202
+ // sort results by distance from start
5203
+ results.sort((a, b) => a.ray.distance - b.ray.distance);
5204
+ return results;
5153
5205
  }
5154
5206
  };
5155
5207
 
@@ -5334,7 +5386,7 @@ async function q5playPreSetup(q) {
5334
5386
  this.isActive = cameraOn = false;
5335
5387
  }
5336
5388
  }
5337
- }; //end camera class
5389
+ }; // end camera class
5338
5390
 
5339
5391
  $.Joint = class {
5340
5392
  constructor(spriteA, spriteB, type) {
@@ -5359,8 +5411,6 @@ async function q5playPreSetup(q) {
5359
5411
 
5360
5412
  let _this = this;
5361
5413
 
5362
- // if (type != 'slider' && type != 'rope') {
5363
-
5364
5414
  if (type != 'wheel') {
5365
5415
  this._offsetA = {};
5366
5416
 
@@ -5775,15 +5825,24 @@ async function q5playPreSetup(q) {
5775
5825
  }
5776
5826
 
5777
5827
  get lowerLimit() {
5778
- return Box2D.b2WheelJoint_GetLowerLimit(this.jID);
5828
+ return Box2D.b2WheelJoint_GetLowerLimit(this.jID) * meterSize;
5779
5829
  }
5780
5830
 
5781
5831
  get upperLimit() {
5782
- return Box2D.b2WheelJoint_GetUpperLimit(this.jID);
5832
+ return Box2D.b2WheelJoint_GetUpperLimit(this.jID) * meterSize;
5783
5833
  }
5784
5834
 
5785
- set limits(val) {
5786
- Box2D.b2WheelJoint_SetLimits(this.jID, val[0], val[1]);
5835
+ set range(val) {
5836
+ let min, max;
5837
+ if (typeof val == 'number') {
5838
+ val /= 2;
5839
+ min = -val;
5840
+ max = val;
5841
+ } else {
5842
+ min = val[0];
5843
+ max = val[1];
5844
+ }
5845
+ Box2D.b2WheelJoint_SetLimits(this.jID, min / meterSize, max / meterSize);
5787
5846
  }
5788
5847
 
5789
5848
  get motorEnabled() {
@@ -6408,23 +6467,23 @@ async function q5playPreSetup(q) {
6408
6467
  if (!ani.looping) return;
6409
6468
  }
6410
6469
 
6411
- //going to target frame up
6470
+ // going to target frame up
6412
6471
  if (ani.targetFrame > ani._frame && ani.targetFrame !== -1) {
6413
6472
  ani._frame++;
6414
6473
  }
6415
- //going to target frame down
6474
+ // going to target frame down
6416
6475
  else if (ani.targetFrame < ani._frame && ani.targetFrame !== -1) {
6417
6476
  ani._frame--;
6418
6477
  } else if (ani.targetFrame === ani._frame && ani.targetFrame !== -1) {
6419
6478
  ani.playing = false;
6420
6479
  } else if (ani.looping) {
6421
- //advance frame
6422
- //if next frame is too high
6480
+ // advance frame
6481
+ // if next frame is too high
6423
6482
  if (ani._frame >= ani.lastFrame) {
6424
6483
  ani._frame = 0;
6425
6484
  } else ani._frame++;
6426
6485
  } else {
6427
- //if next frame is too high
6486
+ // if next frame is too high
6428
6487
  if (ani._frame < ani.lastFrame) ani._frame++;
6429
6488
  }
6430
6489
  } else {
@@ -6440,42 +6499,6 @@ async function q5playPreSetup(q) {
6440
6499
  });
6441
6500
  };
6442
6501
 
6443
- async function playIntro() {
6444
- if (document.getElementById('made-with-q5play')) return;
6445
- if (!using_p5v2) $._incrementPreload();
6446
- let d = document.createElement('div');
6447
- d.id = 'made-with-q5play';
6448
- d.style = 'position: absolute; width: 100%; height: 100%; top: 0; left: 0; z-index: 1000; background-color: black;';
6449
- let logo = document.createElement('img');
6450
- logo.style = `position: absolute; top: 50%; left: 50%; width: 80vmin; height: 40vmin; margin-left: -40vmin; margin-top: -20vmin; z-index: 1001; opacity: 1; scale: 1; transition: scale 1.5s, opacity 0.4s ease-in-out;`;
6451
- logo.onerror = () => {
6452
- logo.style.imageRendering = 'pixelated';
6453
- logo.src = `data:image/png;base64,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`;
6454
- };
6455
- let src = window._q5play_intro_image;
6456
- if (src == '' || src?.includes('made_with_q5play')) {
6457
- if (src.includes('bit.') || src.includes('pixel')) {
6458
- logo.style.imageRendering = 'pixelated';
6459
- }
6460
- logo.src = src;
6461
- } else {
6462
- logo.src = 'https://q5play.org/assets/made_with_q5play.webp';
6463
- }
6464
- await new Promise((r) => (logo.onload = r));
6465
- d.append(logo);
6466
- document.body.append(d);
6467
- await $.delay();
6468
- logo.offsetHeight; // trigger css reflow
6469
- logo.style.scale = 1.2;
6470
- await $.delay(1100);
6471
- logo.style.opacity = 0;
6472
- await $.delay(400);
6473
- d.style.display = 'none';
6474
- d.remove();
6475
- document.getElementById('made-with-q5play')?.remove();
6476
- if (!using_p5v2) $._decrementPreload();
6477
- }
6478
-
6479
6502
  if (window.location) {
6480
6503
  let lh = location.hostname;
6481
6504
  switch (lh) {
@@ -6508,7 +6531,7 @@ async function q5playPreSetup(q) {
6508
6531
  ) {
6509
6532
  break;
6510
6533
  }
6511
- playIntro();
6534
+ $.q5play.splashScreen();
6512
6535
  }
6513
6536
  }
6514
6537
 
@@ -7823,10 +7846,6 @@ async function q5playPreSetup(q) {
7823
7846
 
7824
7847
  s = $.q5play.sprites[uid];
7825
7848
 
7826
- if (s && !s.visible) {
7827
- continue;
7828
- }
7829
-
7830
7849
  let type = Box2D.HEAPU8[offset];
7831
7850
 
7832
7851
  if (type == 7) {
@@ -7855,6 +7874,10 @@ async function q5playPreSetup(q) {
7855
7874
  s._velSynced = false;
7856
7875
  s._vel._magCached = false;
7857
7876
 
7877
+ if (!s.visible) {
7878
+ continue;
7879
+ }
7880
+
7858
7881
  if (s._hasImagery || s._userDefinedDraw) {
7859
7882
  s._rotation = Math.atan2(data[2], data[3]) * RADTODEG;
7860
7883
  }