q5play 4.1.1 → 4.1.2

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 (3) hide show
  1. package/package.json +2 -2
  2. package/q5play.d.ts +16 -17
  3. package/q5play.js +80 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5play",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "author": "quinton-ashley",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "description": "A beginner friendly, web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
@@ -25,6 +25,6 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "box2d3-wasm": "^5.2.0",
28
- "q5": "^4.6.3"
28
+ "q5": "^4.6.6"
29
29
  }
30
30
  }
package/q5play.d.ts CHANGED
@@ -105,6 +105,7 @@ declare global {
105
105
  type: string;
106
106
  geom: any;
107
107
  density: number;
108
+ applyWind(speed: number, angle: number, drag?: number, lift?: number): void;
108
109
  scaleBy(scaleX: number, scaleY?: number): void;
109
110
  delete(): void;
110
111
  }
@@ -678,11 +679,7 @@ declare global {
678
679
  get rotationDrag(): number;
679
680
  set rotationDrag(val: number);
680
681
  /**
681
- * Known issue, this doesn't work yet.
682
- * https://github.com/q5play/q5play/issues/36
683
- *
684
682
  * If true, the sprite can not rotate.
685
- * @deprecated
686
683
  * @default false
687
684
  */
688
685
  get rotationLock(): boolean;
@@ -870,6 +867,14 @@ declare global {
870
867
  * @param n The point the force is applied from, relative to the sprite's center of mass. Accepts a coordinate array or object with x and y properties. If not given, the force is applied at the center of mass.
871
868
  */
872
869
  applyForceScaled(amount: number, origin?: any): void;
870
+ /**
871
+ * Applies wind force to the sprite.
872
+ * @param strength the strength of the wind
873
+ * @param angle the angle the wind is blowing at
874
+ * @param drag the force that opposes the relative velocity
875
+ * @param lift the force that is perpendicular to the relative velocity
876
+ */
877
+ applyWind(strength: number, angle: number, drag?: number, lift?: number): void;
873
878
  /**
874
879
  * Applies a force to the sprite's center of mass attracting it to
875
880
  * the given position.
@@ -1521,10 +1526,7 @@ declare global {
1521
1526
  */
1522
1527
  rotationDrag: number;
1523
1528
  /**
1524
- * Known issue, this doesn't work.
1525
- *
1526
1529
  * If true, the group sprites can not rotate.
1527
- * @deprecated
1528
1530
  */
1529
1531
  rotationLock: boolean;
1530
1532
  /**
@@ -1732,6 +1734,7 @@ declare global {
1732
1734
  passes(target: Group): void;
1733
1735
  applyForce(amount: number, origin?: [] | { x: number; y: number } | Q5.Vector): void;
1734
1736
  applyForceScaled(amount: number, origin?: [] | { x: number; y: number } | Q5.Vector): void;
1737
+ applyWind(speed: number, angle: number, drag?: number, lift?: number): void;
1735
1738
  attractTo(x: number | any, y?: number, force?: number): void;
1736
1739
  applyTorque(torque: any): void;
1737
1740
  moveTowards(x: number | any, y?: number, tracking?: number): void;
@@ -1915,8 +1918,7 @@ declare global {
1915
1918
  set allowSleeping(val: boolean);
1916
1919
  /**
1917
1920
  * Finds the first sprite (with a physics body) that
1918
- * intersects a ray (line), excluding any sprites that intersect
1919
- * with the starting point.
1921
+ * intersects a ray (line).
1920
1922
  *
1921
1923
  * @param startPos starting position of the ray cast
1922
1924
  * @param direction direction of the ray
@@ -1926,8 +1928,7 @@ declare global {
1926
1928
  rayCast(startPos: any, direction: number, maxDistance: number): Sprite;
1927
1929
  /**
1928
1930
  * Finds sprites (with physics bodies) that intersect
1929
- * a line (ray), excluding any sprites that intersect the
1930
- * starting point.
1931
+ * a line (ray).
1931
1932
  *
1932
1933
  * @param startPos starting position of the ray cast
1933
1934
  * @param direction direction of the ray
@@ -2172,10 +2173,12 @@ declare global {
2172
2173
  set limitsEnabled(val: boolean);
2173
2174
  /**
2174
2175
  * The minimum length allowed when limits are enabled.
2176
+ * @readonly
2175
2177
  */
2176
2178
  get minLength(): number;
2177
2179
  /**
2178
2180
  * The maximum length allowed when limits are enabled.
2181
+ * @readonly
2179
2182
  */
2180
2183
  get maxLength(): number;
2181
2184
  /**
@@ -2354,12 +2357,12 @@ declare global {
2354
2357
  set limitsEnabled(val: boolean);
2355
2358
  /**
2356
2359
  * The lower limit of rotation.
2357
- * @default undefined
2360
+ * @readonly
2358
2361
  */
2359
2362
  get minAngle(): number;
2360
2363
  /**
2361
2364
  * The upper limit of rotation.
2362
- * @default undefined
2365
+ * @readonly
2363
2366
  */
2364
2367
  get maxAngle(): number;
2365
2368
  /**
@@ -2453,10 +2456,6 @@ declare global {
2453
2456
  * or an array with the lower and upper translation limits.
2454
2457
  */
2455
2458
  set range(val: [number, number] | number);
2456
- /**
2457
- * Alias for range.
2458
- */
2459
- set limits(val: [number, number] | number);
2460
2459
  /**
2461
2460
  * Whether spring behavior is enabled.
2462
2461
  * @default false
package/q5play.js CHANGED
@@ -85,6 +85,7 @@ async function q5playPreSetup(q) {
85
85
  b2World_OverlapShape,
86
86
  b2World_CastRay,
87
87
  b2World_CastRayClosest,
88
+ b2World_CastShape,
88
89
  b2World_SetCustomFilterCallback,
89
90
  b2World_SetPreSolveCallback,
90
91
  b2World_GetGravity,
@@ -538,6 +539,12 @@ async function q5playPreSetup(q) {
538
539
  this._density = val;
539
540
  b2Shape_SetDensity(this.id, val);
540
541
  }
542
+
543
+ applyWind(strength, angle, drag = 0, lift = 0) {
544
+ const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
545
+ b2Shape_ApplyWind(this.id, wind, drag, lift, true);
546
+ wind.delete();
547
+ }
541
548
  };
542
549
 
543
550
  const Sensor = class extends Shape {
@@ -2053,17 +2060,15 @@ async function q5playPreSetup(q) {
2053
2060
  }
2054
2061
  set rotationLock(val) {
2055
2062
  if (this.watch) this.mod[25] = true;
2063
+ this._rotationLock = val;
2064
+ if (!this._physicsEnabled) return;
2056
2065
 
2057
- // let mass = this.mass;
2058
-
2059
- // TODO: not working, shape is ignored by physics sim after this
2060
- let locks = new b2MotionLocks();
2066
+ const locks = new b2MotionLocks();
2061
2067
  locks.linearX = false;
2062
2068
  locks.linearY = false;
2063
2069
  locks.angularZ = val;
2064
2070
  b2Body_SetMotionLocks(this.bdID, locks);
2065
-
2066
- // this.mass = mass;
2071
+ locks.delete();
2067
2072
  }
2068
2073
 
2069
2074
  get rotationSpeed() {
@@ -2763,6 +2768,14 @@ async function q5playPreSetup(q) {
2763
2768
  b2Body_ApplyTorque(this.bdID, val, true);
2764
2769
  }
2765
2770
 
2771
+ applyWind(strength, angle, drag = 0, lift = 0) {
2772
+ const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
2773
+ for (let shape of this._shapes) {
2774
+ b2Shape_ApplyWind(shape.id, wind, drag, lift, true);
2775
+ }
2776
+ wind.delete();
2777
+ }
2778
+
2766
2779
  angleTo(x, y) {
2767
2780
  if (typeof x == 'object') {
2768
2781
  y = x.y;
@@ -4467,6 +4480,16 @@ async function q5playPreSetup(q) {
4467
4480
  }
4468
4481
  }
4469
4482
 
4483
+ applyWind(strength, angle, drag = 0, lift = 0) {
4484
+ const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
4485
+ for (let s of this) {
4486
+ for (let shape of s._shapes) {
4487
+ b2Shape_ApplyWind(shape.id, wind, drag, lift, true);
4488
+ }
4489
+ }
4490
+ wind.delete();
4491
+ }
4492
+
4470
4493
  _resetCentroid() {
4471
4494
  let x = 0;
4472
4495
  let y = 0;
@@ -4787,7 +4810,7 @@ async function q5playPreSetup(q) {
4787
4810
  $.Visuals.prototype.addAni = $.Group.prototype.addAni = $.Sprite.prototype.addAni;
4788
4811
  $.Visuals.prototype.addAnis = $.Group.prototype.addAnis = $.Sprite.prototype.addAnis;
4789
4812
 
4790
- class RayInfo {
4813
+ class CastInfo {
4791
4814
  constructor(sprite, px, py, nx, ny, fraction, maxDistance) {
4792
4815
  this.sprite = sprite;
4793
4816
  this._px = px;
@@ -5183,7 +5206,7 @@ async function q5playPreSetup(q) {
5183
5206
  if (shape?.sprite) {
5184
5207
  const s = shape.sprite;
5185
5208
 
5186
- s.ray = new RayInfo(
5209
+ s.cast = new CastInfo(
5187
5210
  s,
5188
5211
  castResult.point.x,
5189
5212
  castResult.point.y,
@@ -5200,7 +5223,54 @@ async function q5playPreSetup(q) {
5200
5223
  });
5201
5224
 
5202
5225
  // sort results by distance from start
5203
- results.sort((a, b) => a.ray.distance - b.ray.distance);
5226
+ results.sort((a, b) => a.cast.distance - b.cast.distance);
5227
+ return results;
5228
+ }
5229
+
5230
+ circleCast(startPos, endPos, radius) {
5231
+ let sprites = this.circleCastAll(startPos, endPos, radius, () => true);
5232
+ return sprites[0];
5233
+ }
5234
+
5235
+ circleCastAll(startPos, endPos, radius, limiter) {
5236
+ const startX = startPos.x ?? startPos[0];
5237
+ const startY = startPos.y ?? startPos[1];
5238
+ const endX = endPos.x ?? endPos[0];
5239
+ const endY = endPos.y ?? endPos[1];
5240
+
5241
+ const maxDistance = Math.sqrt((endX - startX) ** 2 + (endY - startY) ** 2);
5242
+
5243
+ const center = scaleTo(startX, startY);
5244
+ const proxy = b2MakeProxy(center, 1, radius / meterSize);
5245
+ const translation = scaleTo(endX - startX, endY - startY);
5246
+
5247
+ const results = [];
5248
+
5249
+ b2World_CastShape(wID, proxy, translation, NULL_FILTER, (castResult) => {
5250
+ const shape = shapeDict[castResult.shapeId.index1];
5251
+ if (shape?.sprite) {
5252
+ const s = shape.sprite;
5253
+
5254
+ s.cast = new CastInfo(
5255
+ s,
5256
+ castResult.point.x,
5257
+ castResult.point.y,
5258
+ castResult.normal.x,
5259
+ castResult.normal.y,
5260
+ castResult.fraction,
5261
+ maxDistance
5262
+ );
5263
+ results.push(s);
5264
+
5265
+ if (limiter && limiter(s)) return 0; // stop cast
5266
+ }
5267
+ return 1; // continue to collect all hits
5268
+ });
5269
+
5270
+ proxy.delete();
5271
+
5272
+ // sort results by distance from start
5273
+ results.sort((a, b) => a.cast.distance - b.cast.distance);
5204
5274
  return results;
5205
5275
  }
5206
5276
  };
@@ -5497,7 +5567,7 @@ async function q5playPreSetup(q) {
5497
5567
  }
5498
5568
 
5499
5569
  _draw(xA, yA, xB, yB) {
5500
- if (xB) $.line(xA, yA, xB, yB);
5570
+ if (xB || yB) $.line(xA, yA, xB, yB);
5501
5571
  else $.point(xA, yA);
5502
5572
  }
5503
5573
 
@@ -6045,20 +6115,6 @@ async function q5playPreSetup(q) {
6045
6115
  Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
6046
6116
  }
6047
6117
 
6048
- set limits(val) {
6049
- let min, max;
6050
- if (typeof val == 'number') {
6051
- val /= 2;
6052
- min = -val;
6053
- max = val;
6054
- } else {
6055
- min = val[0];
6056
- max = val[1];
6057
- }
6058
- Box2D.b2PrismaticJoint_SetLimits(this.jID, min / meterSize, max / meterSize);
6059
- Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
6060
- }
6061
-
6062
6118
  get springEnabled() {
6063
6119
  return Box2D.b2PrismaticJoint_IsSpringEnabled(this.jID);
6064
6120
  }