@maccesar/titools 5.2.0 → 5.2.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 (78) hide show
  1. package/CONTRIBUTING.md +69 -0
  2. package/README.md +80 -72
  3. package/agents/ti-pro.md +1 -1
  4. package/commands/ti-audit.md +2 -2
  5. package/docs/examples.md +91 -0
  6. package/docs/knowledge-index.md +57 -0
  7. package/lib/downloader.js +1 -1
  8. package/package.json +4 -2
  9. package/skills/purgetss/references/multi-density-images.md +1 -1
  10. package/skills/ti-admob-unified/SKILL.md +21 -7
  11. package/skills/ti-admob-unified/assets/example/app.js +229 -25
  12. package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
  13. package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
  14. package/skills/ti-admob-unified/references/api-reference.md +39 -16
  15. package/skills/ti-admob-unified/references/banners.md +9 -9
  16. package/skills/ti-admob-unified/references/contributing.md +14 -9
  17. package/skills/ti-admob-unified/references/dependencies.md +31 -25
  18. package/skills/ti-admob-unified/references/fullscreen.md +43 -28
  19. package/skills/ti-admob-unified/references/game-monetization.md +138 -7
  20. package/skills/ti-admob-unified/references/getting-started.md +75 -31
  21. package/skills/ti-admob-unified/references/index.md +5 -2
  22. package/skills/ti-admob-unified/references/integration.md +8 -6
  23. package/skills/ti-admob-unified/references/library.md +41 -10
  24. package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
  25. package/skills/ti-admob-unified/references/mediation.md +22 -9
  26. package/skills/ti-admob-unified/references/migration.md +11 -7
  27. package/skills/ti-admob-unified/references/module-changelog.md +41 -11
  28. package/skills/ti-admob-unified/references/privacy.md +35 -8
  29. package/skills/ti-admob-unified/references/production.md +67 -0
  30. package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
  31. package/skills/ti-admob-unified/references/roadmap.md +28 -31
  32. package/skills/ti-admob-unified/references/source-notes.md +51 -4
  33. package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
  34. package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
  35. package/skills/ti-game/SKILL.md +23 -6
  36. package/skills/ti-game/references/api.md +34 -30
  37. package/skills/ti-game/references/debugging-performance.md +18 -9
  38. package/skills/ti-game/references/examples.md +103 -20
  39. package/skills/ti-game/references/input.md +101 -0
  40. package/skills/ti-game/references/patterns.md +39 -15
  41. package/skills/ti-game/references/physics.md +80 -52
  42. package/skills/ti-game/references/project-setup.md +118 -56
  43. package/skills/ti-game/references/recipes-action.md +107 -8
  44. package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
  45. package/skills/ti-game/references/roadmap.md +10 -3
  46. package/skills/ti-game/references/source-notes.md +81 -0
  47. package/skills/ti-game/references/tilemaps.md +12 -4
  48. package/skills/ti-synthengine/SKILL.md +81 -95
  49. package/skills/ti-synthengine/assets/example/README.md +84 -0
  50. package/skills/ti-synthengine/assets/example/app.js +1178 -0
  51. package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
  52. package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
  53. package/skills/ti-synthengine/references/advanced-examples.md +174 -0
  54. package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
  55. package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
  56. package/skills/ti-synthengine/references/api.md +434 -380
  57. package/skills/ti-synthengine/references/audio-engine.md +157 -230
  58. package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
  59. package/skills/ti-synthengine/references/examples.md +123 -253
  60. package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
  61. package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
  62. package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
  63. package/skills/ti-synthengine/references/guide.md +188 -156
  64. package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
  65. package/skills/ti-synthengine/references/performance-review.json +1146 -0
  66. package/skills/ti-synthengine/references/physical-validation.json +1457 -0
  67. package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
  68. package/skills/ti-synthengine/references/recipes.md +423 -599
  69. package/skills/ti-synthengine/references/recommendations.md +63 -113
  70. package/skills/ti-synthengine/references/sound-design.md +134 -258
  71. package/skills/ti-synthengine/references/source-notes.md +93 -105
  72. package/skills/ti-synthengine/references/source-snapshot.json +379 -32
  73. package/skills/ti-synthengine/references/stats-performance.md +64 -20
  74. package/skills/ti-synthengine/references/studio-validation.json +6202 -0
  75. package/skills/ti-synthengine/references/troubleshooting.md +109 -121
  76. package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
  77. package/skills/ti-synthengine/references/unified-validation.json +314 -0
  78. package/AGENTS-VERCEL-RESEARCH.md +0 -175
@@ -1,6 +1,7 @@
1
1
  # Cross-cutting ti.game patterns
2
2
 
3
- Verified against `ti.game@3bea2f4` and the runnable examples under `example/`. These are production-oriented extractions of the API combinations demonstrated upstream; they intentionally omit demo-launcher and back-navigation plumbing.
3
+ Reviewed against upstream native `88e181c` and the documentation/examples in
4
+ `macCesar/ti.game@2bf5f82` on 2026-10-23. These are production-oriented extractions of the API combinations demonstrated upstream; they intentionally omit demo-launcher and back-navigation plumbing.
4
5
 
5
6
  See [examples.md](examples.md) to choose the source demo, [api.md](api.md) for exact members, and the genre recipe files for complete game loops.
6
7
 
@@ -76,7 +77,14 @@ module.exports = function () {
76
77
  };
77
78
  ```
78
79
 
79
- Sizing everything as a fraction of `W`/`H` is not cosmetic — a jump tuned in absolute pixels becomes unplayable on a different device. The demos consistently express speeds as `W * f`, accelerations as `H * f` and sprite sizes as `Math.min(W, H) * f`.
80
+ Use the actual canvas for sizing. Current demos cap foreground art and text by
81
+ height as well as width so short desktop windows do not clip their controls.
82
+ For example, text can use `Math.max(1, Math.round(Math.min(W / 240, H / 360)))`.
83
+ Measure surface pixels per view point from `W / gameView.rect.width` when
84
+ converting native control margins; use a positive finite density fallback.
85
+ Keep a guarded first `resize` for world creation and relayout the HUD on later
86
+ resizes when the app supports changing windows. See
87
+ [project-setup.md](project-setup.md) for complete integration.
80
88
 
81
89
  ## Cross-cutting patterns
82
90
 
@@ -146,7 +154,7 @@ Recycling a sprite with `visible = false` fires `collisionend` on anything it wa
146
154
 
147
155
  ### Freeing a level's textures
148
156
 
149
- Pooling recycles sprites; it does not free the **texture** behind them. A game that streams worlds accumulates every atlas it has ever loaded until the GL context dies. `unload()` releases one on the next rendered frame.
157
+ Pooling recycles sprites; it does not free the **texture** behind them. A game that streams worlds accumulates every atlas it has ever loaded until scene resources are released. `unload()` releases one on the next rendered frame.
150
158
 
151
159
  ```javascript
152
160
  function unloadLevel(level) {
@@ -262,7 +270,14 @@ bindHold(leftButton, () => {
262
270
  });
263
271
  ```
264
272
 
265
- The guard in the release handler is what keeps two opposite buttons from fighting: releasing left only zeroes steering if left is still the active direction.
273
+ The guard avoids releasing one control cancelling a newer opposite command.
274
+ It does not restore a still-held older direction. For mixed touch, keyboard and
275
+ controller input, track each source's held state and derive direction from the
276
+ active set, as current `platformer.js`/`volley.js` do. They cancel opposing
277
+ directions and resume the remaining one on release. `inputmap.js` deliberately
278
+ uses a different last-pressed-wins rule. Choose the policy explicitly; do not
279
+ confuse either with the native action aggregator, which combines bindings for
280
+ one named action. See [input.md](input.md) for native input maps and focus rules.
266
281
 
267
282
  ### HUD — `text.js`, `flappy.js`
268
283
 
@@ -316,7 +331,11 @@ dialog.addEventListener('tap', () => {
316
331
 
317
332
  `maxWidth` is measured before `scale`, which is the only trap here — pass screen pixels straight in and the block comes out `scale` times too wide. `align` positions the lines against each other (the block's own width), not inside the wrap column, so anchor the sprite where the text should sit. A word longer than `maxWidth` overflows instead of breaking mid-word.
318
333
 
319
- Overlaid `Ti.UI.Label` views remain the answer for text the engine's font cannot draw (accents, system fonts, right-to-left) and for real UI: menus, dialogs, text inputs.
334
+ The built-in font now includes the accented characters listed in
335
+ [api.md](api.md#font); do not assume all accents need an overlay. Ship a custom
336
+ bitmap font for other supported glyph sets. Overlaid `Ti.UI.Label` views remain
337
+ appropriate for system fonts, right-to-left interfaces and native UI such as
338
+ menus, dialogs and text inputs.
320
339
 
321
340
  ### Labels, bars and turrets pinned to a sprite — `attachTo`, `text.js`
322
341
 
@@ -495,11 +514,15 @@ const ball = Game.createSprite({
495
514
 
496
515
  Two things bite here. The sweep **ignores** anything that is not `solidMode: 'block'`, so `swept: true` will not stop a fast enough ball crossing the boundary between frames — keep the speeds inside a container sane. And a `'contain'` solid only holds *circles*: a rect mover runs a resolver that never reads `solidMode` and gets pushed **out** of the boundary instead of held in.
497
516
 
498
- **`linearDamping` is the missing friction, with a caveat.** `drag` only ever worked inside `carMode`; this is the same idea for ordinary sprites, and it is what brings a pool table to rest. But nothing checks contact — it bleeds speed on both axes, in the air as much as on the felt, and along a slope as much as across it. Right for a table, wrong for a hill. Below a combined 4 px/s the engine zeroes the velocity outright, so a deliberate 3 px/s drift dies the moment that sprite also carries damping.
499
-
500
- That threshold can also erase the first increment of an accelerating ship.
501
- Read [thrust and damping](physics.md#thrust-inertia-and-linear-damping) before
502
- using a permanent coasting brake on a powered sprite.
517
+ **`linearDamping` attenuates velocity everywhere.** Car `drag` works inside
518
+ `carMode`; linear damping also works for ordinary sprites and can bring a pool
519
+ table to rest. The attenuation is not contact-only friction: it bleeds speed
520
+ on both axes, in the air as much as on felt, and along a slope as much as
521
+ across it. Below a combined 4 px/s the engine settles coasting or blocked motion, but
522
+ allows effective acceleration to build from rest; the decision is completed
523
+ after contact response. A deliberate unpowered 3 px/s drift with damping still
524
+ stops. See [thrust and damping](physics.md#thrust-inertia-and-linear-damping)
525
+ for the current rest policy and the older powered-launch bug.
503
526
 
504
527
  **A bouncy surface is now a property of the surface.** `restitution` is read off both sides and mixed as `max`, so a springy floor rebounds riders that are not themselves bouncy:
505
528
 
@@ -571,7 +594,7 @@ ship.followPath(diamond, {
571
594
 
572
595
  A non-looping run fires `pathcomplete` and clears itself; `followPath(null)` stops in place. Two points is the minimum — one point is ignored with a warning, not treated as "go there".
573
596
 
574
- The path writes `x`/`y` outright, after velocity and gravity have been integrated, so **a sprite on a path ignores its own physics for placement**. It still feeds the frame delta, though, which means a platform on a looping circuit carries whatever is standing on it — a moving platform is now a path, not a tween ping-pong.
597
+ The path writes `x`/`y` outright, after velocity and gravity have been integrated, so **a sprite on a path ignores its own physics for placement**. In a periodic world the engine aligns the cursor before advancing so crossing the seam retains the intended frame delta; this does not make pathfinding periodic. It still feeds the frame delta, though, which means a platform on a looping circuit carries whatever is standing on it — a moving platform is now a path, not a tween ping-pong.
575
598
 
576
599
  Animation chains are native in the same spirit:
577
600
 
@@ -611,7 +634,7 @@ Run these from a coarse timer or a tap handler — the demo probes at 150 ms —
611
634
 
612
635
  ### Pathfinding around obstacles — `maze.js`, `pointclick.js`
613
636
 
614
- `gameView.findPath(from, to, options)` is grid A\* over the same sprites `raycast` sees: visible, tagged with a `collisionGroup`, nothing else to set up. It returns `{ x, y }` waypoints — or `null` — and those waypoints go straight into `followPath`, so the walk itself runs natively.
637
+ `gameView.findPath(from, to, options)` is grid A\* over the same sprites `raycast` sees: visible, non-`screenFixed`, tagged with a `collisionGroup`. It also includes fully solid cells from matching TileLayers; one-way cells remain walkable. It returns `{ x, y }` waypoints — or `null` — and those waypoints go straight into `followPath`, so the walk itself runs natively.
615
638
 
616
639
  ```javascript
617
640
  gameView.addEventListener('tap', (e) => {
@@ -692,9 +715,10 @@ explosion starts, while it keeps drawing and accepting touches. This avoids
692
715
  restarting the render loop. (Pattern from Titanium Lander: 140 ms on a crash,
693
716
  90 ms when a shield absorbs a hit.)
694
717
 
695
- This sets scene dt to zero, not a universal suspension of every update.
696
- Rope's previous-displacement and constraint pass still run in the pinned
697
- source; verify hit-stop separately for rope scenes. See
718
+ This sets scene dt to zero while touch and explicit property writes remain
719
+ active. Current Rope integration, history, constraints and tether response
720
+ freeze as well; newly created geometry can still initialize. The older
721
+ `3bea2f4` rope exception is fixed. See
698
722
  [physics.md](physics.md#verlet-ropes-and-tethers).
699
723
 
700
724
  ```javascript
@@ -3,13 +3,13 @@
3
3
  Use this reference to choose movement by its intended feel, derive parameters,
4
4
  or explain frame-rate-dependent behavior. A camera angle is not a physics
5
5
  model: a top-down character, car, spaceship and billiard ball use different
6
- rules. The [34-demo catalog](examples.md) covers action, platforms, ball games,
6
+ rules. The [demo catalog](examples.md) covers action, platforms, ball games,
7
7
  adventures, board interactions and visual demonstrations.
8
8
 
9
- Verified on 2026-10-12 against upstream
10
- [`3bea2f4007d4aa515e432ec2a016dd14c5c0f2ba`](https://github.com/m1ga/ti.game/tree/3bea2f4007d4aa515e432ec2a016dd14c5c0f2ba).
11
- Upstream HEAD still has that commit. Local forks and installed binaries can
12
- contain additional changes; do not silently treat them as this snapshot.
9
+ Reviewed on 2026-10-23 against native upstream
10
+ [`88e181c`](https://github.com/m1ga/ti.game/tree/88e181cb85526d8146d0be6e09b2b90c2551baad)
11
+ and the documentation refresh in `macCesar/ti.game@2bf5f82`. These source
12
+ contracts do not identify the contents of an installed binary.
13
13
 
14
14
  Each equation is labeled **Engine** (native implementation), **Demo**
15
15
  (application rule) or **Analytical** (a mathematical model for tuning).
@@ -80,7 +80,8 @@ For `θ = rotation·π/180`, forward `f=(sin θ,−cos θ)` and right
80
80
  | Cross-cutting systems | Camera, Blend, Text, Hitbox, Zones, Timescale | Presentation, geometry, events or clock; not separate locomotion models |
81
81
 
82
82
  Names map to `example/<name>.js`, with lowercase spelling. These groups cover
83
- all 34 launcher entries; Lunar Lander in the action recipes is a curated
83
+ the launcher entries; the Configurable input/Keyboard demos exercise input rather
84
+ than add physics models. Lunar Lander in the action recipes is a curated
84
85
  composition, not an extra upstream demo. Use [examples.md](examples.md) for
85
86
  the exact filenames and [api.md](api.md) for public properties.
86
87
 
@@ -96,7 +97,8 @@ pNext = p + vNext·dt
96
97
  This is semi-implicit Euler: acceleration before position. In Sprite, the
97
98
  order is car integration, angular velocity, thrust and its speed cap,
98
99
  gravity, linear damping, position, scroll wrap, path, animation, tween and
99
- idle wobble. Scene collision/attachment passes can change that result again.
100
+ idle wobble. Scene solid resolution, post-contact damping, and attachment
101
+ passes can change that result again.
100
102
  Do not combine several position writers expecting their displacements to add.
101
103
 
102
104
  **Analytical**, constant acceleration with no cap/contact:
@@ -115,8 +117,8 @@ not eliminate integration error.
115
117
  260 and a puck with gravity 0. Constant sideways acceleration is an arcade
116
118
  wind field; it is not drag calculated from relative air velocity.
117
119
 
118
- Sources: [Android Sprite.update](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/Sprite.java),
119
- [iOS TGSprite.update](https://github.com/m1ga/ti.game/blob/3bea2f4/ios/Classes/TGSprite.m).
120
+ Sources: [Android Sprite.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Sprite.java),
121
+ [iOS TGSprite.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGSprite.m).
120
122
 
121
123
  ## Thrust, inertia and linear damping
122
124
 
@@ -130,7 +132,7 @@ if |v| > maxSpeed: v *= maxSpeed/|v|
130
132
  v += (gravityX,gravity)·dt
131
133
  if linearDamping > 0:
132
134
  v *= max(0,1−linearDamping·dt)
133
- if |v| < 4 px/s: v = (0,0)
135
+ if effectively coasting and |v| < 4 px/s: v = (0,0)
134
136
  p += v·dt
135
137
  ```
136
138
 
@@ -147,32 +149,41 @@ applies its rest threshold. Box2D documents the same continuous decay but
147
149
  uses `1/(1+c·dt)`; do not substitute that for ti.game's implemented formula.
148
150
  [Box2D damping reference](https://box2d.org/documentation/md_simulation.html).
149
151
 
150
- ### Powered launch below the rest threshold
151
-
152
- At rest, thrust220 and damping 1.2 at a 1/60 s step give
153
- `220/60·(1−1.2/60)=3.593` px/s. The threshold erases it before position
154
- integration, every step. At120 FPS the problem also occurs; thrust100 can
155
- be erased even at 30 FPS. This follows from the same formula on both platforms.
156
-
157
- For an application that should brake only while coasting, set damping 0
158
- while thrust is active and restore its chosen coefficient without thrust.
159
- This changes powered-flight drag. If resistance must remain during thrust,
160
- the intended correction belongs in the native rest policy, with tests of
161
- accelerating bodies and resting bodies on both platforms. Do not invent a
162
- per-frame JS position loop, give an artificial initial velocity, or increase
163
- thrust solely to escape the cutoff.
164
-
165
- Acceleration and resistance together are physically valid. The cutoff is
166
- the problematic numerical condition. A flame animation proves a visual
167
- state, not nonzero motion; different device behavior still requires FPS,
168
- surface, input and installed-build evidence.
152
+ ### Powered launch and post-contact rest
153
+
154
+ The current engine preserves acceleration below the 4 px/s cutoff. At rest,
155
+ thrust 220 and damping 1.2 at a 1/60 s step produce
156
+ `220/60·(1−1.2/60)=3.593` px/s; that powered step now survives. Damping
157
+ still applies during acceleration. An unopposed powered equilibrium may remain
158
+ below 4 px/s without being forced to zero.
159
+
160
+ **Engine** tracks the vector sum of thrust, gravity, gravityX and car motor
161
+ acceleration. A tolerance of `1e−6·sum(abs(input acceleration magnitudes))`
162
+ treats floating-point cancellation residue as coasting. After solid resolution,
163
+ `finishDamping()` evaluates the remaining speed and work `v·a`. Below 4 px/s,
164
+ nonpositive or negligible work allows the body to settle. A force cancelled
165
+ by another force, or pressing into a blocked surface, therefore does not
166
+ protect residual tangential drift. An inward rect-wall velocity retained for
167
+ held input is excluded from that tangential test and preserved separately.
168
+
169
+ **Historical build boundary:** `3bea2f4` erased every damped velocity below
170
+ 4 px/s before position integration, including powered launch. Verify the build
171
+ before diagnosing that behavior. Do not carry its mandatory damping-off
172
+ workaround into current source. Choosing damping only while coasting remains
173
+ a valid application feel, but intentionally removes powered-flight drag.
174
+
175
+ `tests/test_linear_damping.py` executes production Java/Objective-C update,
176
+ car and post-contact methods at 30/60/120 FPS. Contacts in that fixture are
177
+ velocity projections; it does not certify complete scene/device behavior.
169
178
 
170
179
  ### Coasting brake feel reference
171
180
 
172
181
  **Application profile**, Titanium Asteroids autopilot, recorded 2026-10-12:
173
182
  set `linearDamping: 0` while thrust is active and `linearDamping: 1.2` s⁻¹
174
183
  when thrust stops. Native braking then multiplies velocity by
175
- `max(0, 1-1.2*dt)`, with the installed engine's rest cutoff.
184
+ `max(0, 1-1.2*dt)`, with the installed engine's rest policy. The current source preserves
185
+ acceleration below the cutoff; this older application profile remains an
186
+ optional control feel rather than a required engine workaround.
176
187
 
177
188
  César liked the speed of this braking as a reference for other games,
178
189
  especially arcade racing, although he did not choose it as the preferred
@@ -317,8 +328,8 @@ instead of simulating two masses. Scenario walls use native restitution;
317
328
  player hits use an arcade rule. Plinko uses gravity 1100, restitution 0.42,
318
329
  circle pegs and `push` chips.
319
330
 
320
- Sources: [Android Scene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/Scene.java),
321
- [iOS TGScene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/3bea2f4/ios/Classes/TGScene.m).
331
+ Sources: [Android Scene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Scene.java),
332
+ [iOS TGScene.resolveCircleSolids](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGScene.m).
322
333
 
323
334
  ## Equal-mass billiards and circular containment
324
335
 
@@ -396,23 +407,33 @@ at 30 updates,0.404 at 60 and 0.163 at 120. These are not predictions of the
396
407
  whole rope's amplitude. An analytical time-normalized alternative would be
397
408
  `exp(−c·dt)` or `dRef^(dt/dtRef)`; the engine uses the fixed supplied factor.
398
409
 
399
- At the pinned source, Rope.update has no zero-delta early return. Existing
400
- Verlet displacement and constraints still run at dt 0; `timeScale=0` is not
401
- a guarantee that rope nodes freeze. This differs from merely setting the
402
- gravity term to zero. Scene/hardware acceptance is still needed for a
403
- pause design; stopping the render loop with `pause()` is a different action
404
- because it also stops rendering. Do not silently change the global pause
405
- UX to avoid this boundary.
410
+ At zero or negative dt, the current Rope update returns before tether response,
411
+ Verlet displacement, endpoint pinning and constraint relaxation. Existing node
412
+ positions and their history remain unchanged, so positive dt resumes the saved
413
+ motion. A new rope, or a changed segment count, still rebuilds geometry before
414
+ that return so it is drawable and its endpoint is readable while paused.
415
+
416
+ `timeScale=0` now freezes rope simulation while drawing and touch continue.
417
+ A user drag or explicit property write can still move a sprite independently;
418
+ zero scene time does not disable input. `pause()` stops the render loop as well.
419
+ The earlier `3bea2f4` source lacked the early return and could keep relaxing or
420
+ integrating at zero dt; do not apply that historical warning to current builds.
421
+ `tests/test_rope_pause.py` checks node/history/tether state in both native twins
422
+ without a renderer.
406
423
 
407
- Sources: [Android Rope.update](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/Rope.java),
408
- [iOS TGRope.update](https://github.com/m1ga/ti.game/blob/3bea2f4/ios/Classes/TGRope.m).
424
+ Sources: [Android Rope.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Rope.java),
425
+ [iOS TGRope.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGRope.m).
409
426
 
410
427
  ## Swept movement and periodic worlds
411
428
 
412
429
  **Analytical** per-step movement is `Δp=v·dt`:9600 px/s travels160 px
413
430
  at 60 FPS. A final-position overlap can skip a thin wall.
414
431
  Swept tests examine the traveled segment; circle pairs have circular sweeps,
415
- other shape pairs use their respective geometry/approximations. The solid
432
+ overlap events for a circle/rect or circle/rotatedRect use an expanded box
433
+ with rounded corners, including relative target movement and periodic offsets.
434
+ Rectangle-pair overlap sweeps retain AABBs. Orientation and size are fixed at
435
+ the end-of-frame values: these are translation sweeps, not rotational sweeps.
436
+ Solid blocking has a separate implementation. The solid
416
437
  sweep skips `contain`/`push`; those still use discrete responses. It is not
417
438
  a general multiple-substep rigid-body simulation.
418
439
 
@@ -422,7 +443,12 @@ Raycast is an instantaneous query, not a gravity-driven projectile.
422
443
  **Analytical** normalization for `L=maxX−minX` is
423
444
  `x=minX+((x−minX)%L+L)%L`; nearest image displacement is
424
445
  `dx−L·round(dx/L)`. Raycast, A*, particles, ropes and skid trails do not
425
- repeat at the seam. [Worldwrap contract](api.md#physics).
446
+ repeat at the seam. Native path cursors are aligned to the sprite's nearest
447
+ periodic image **before** advancing, preserving signed frame deltas across the
448
+ seam, including steps longer than half a world. This does not make A* itself
449
+ periodic. Rect resting-contact sweeps also defer shallow normal entry to the
450
+ static resolver so tangential sliding survives float-sized contact gaps;
451
+ deep entries and full crossings still sweep. [Worldwrap contract](api.md#physics).
426
452
 
427
453
  ## Paths, tweens and navigation
428
454
 
@@ -440,8 +466,8 @@ Camera/parallax, blend, text, flip and pixel snapping are presentation;
440
466
  they do not change those motion equations. Snapping rounds the drawn anchor,
441
467
  not physical position, and cannot eliminate every rotational raster change.
442
468
 
443
- Sources: [Path.advance](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/Path.java),
444
- [Tween.update](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/Tween.java).
469
+ Sources: [Path.advance](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Path.java),
470
+ [Tween.update](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/Tween.java).
445
471
 
446
472
  ## Rhythm timing
447
473
 
@@ -452,8 +478,9 @@ changes the milliseconds represented by a fixed pixel window.
452
478
 
453
479
  At the demo's stated 150 BPM, quarter duration is `60000/150=400 ms`;
454
480
  an eighth is200 ms. The400 ms spawn interval and 1.6s travel are one and four
455
- quarters, respectively. The upstream comments call them eighths, and the
456
- old action recipe repeated that error. The corrected recipe preserves timing.
481
+ quarters, respectively. The older `3bea2f4` comments called them eighths, and the old action recipe
482
+ repeated that error. Current source comments and the corrected recipe preserve
483
+ the same timing and identify the quarter-note spacing.
457
484
 
458
485
  This is a comment/recipe error, not evidence of a faulty native beat engine.
459
486
  The demo uses `setInterval(spawn,SPAWN_MS)` and independent `music.play()`;
@@ -461,7 +488,7 @@ it has no audio-position-driven scheduling. Exact musical synchronization,
461
488
  actual asset tempo and phase would need separate measurement. A comment-only
462
489
  upstream patch should not halve SPAWN_MS or replace the motor.
463
490
 
464
- Source: [Rhythm demo](https://github.com/m1ga/ti.game/blob/3bea2f4/example/rhythm.js).
491
+ Source: [Rhythm demo](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/example/rhythm.js).
465
492
 
466
493
  ## Particle integration
467
494
 
@@ -479,8 +506,9 @@ come from Euler error, rest/rebound thresholds, per-update Rope damping,
479
506
  surface-based parameters, input/lifecycle or different installed binaries.
480
507
 
481
508
  GameView timers follow scene time, conventional JS timers do not. Rendering
482
- and touch continue under timeScale 0; the Rope exception above prevents an
483
- unqualified promise that every position stays unchanged.
509
+ and touch continue under timeScale 0; current Rope nodes/constraints also
510
+ freeze. Explicit writes, dragging and solid/attachment resolution still need
511
+ to be considered when a pause design permits interaction.
484
512
 
485
513
  For tuning, capture the build, actual FPS, W/H, timeScale, forces and
486
514
  velocities. Check rest-to-powered motion, releases, turns, contacts and pauses
@@ -488,8 +516,8 @@ at 30/60/120 FPS, then validate on the target hardware. Scalar equations and
488
516
  source parity are useful evidence, not certificates of identical device feel.
489
517
  [Telemetry workflow](debugging-performance.md).
490
518
 
491
- Sources: [Android SceneRenderer](https://github.com/m1ga/ti.game/blob/3bea2f4/android/src/ti/game/engine/SceneRenderer.java),
492
- [iOS TGSceneRenderer](https://github.com/m1ga/ti.game/blob/3bea2f4/ios/Classes/TGSceneRenderer.m).
519
+ Sources: [Android SceneRenderer](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/android/src/ti/game/engine/SceneRenderer.java),
520
+ [iOS TGSceneRenderer](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/ios/Classes/TGSceneRenderer.m).
493
521
 
494
522
  ## What needs a different model
495
523