@maccesar/titools 5.1.1 → 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 (98) hide show
  1. package/CONTRIBUTING.md +69 -0
  2. package/README.md +112 -72
  3. package/agents/ti-pro.md +5 -1
  4. package/commands/ti-audit.md +5 -1
  5. package/docs/examples.md +91 -0
  6. package/docs/knowledge-index.md +57 -0
  7. package/lib/config.js +2 -0
  8. package/lib/downloader.js +1 -1
  9. package/package.json +4 -2
  10. package/skills/purgetss/references/multi-density-images.md +1 -1
  11. package/skills/ti-admob-unified/SKILL.md +58 -0
  12. package/skills/ti-admob-unified/agents/openai.yaml +4 -0
  13. package/skills/ti-admob-unified/assets/example/app.js +1207 -0
  14. package/skills/ti-admob-unified/assets/example/lib/ads.js +646 -0
  15. package/skills/ti-admob-unified/assets/example/library-usage.js +136 -0
  16. package/skills/ti-admob-unified/references/api-reference.md +615 -0
  17. package/skills/ti-admob-unified/references/banners.md +435 -0
  18. package/skills/ti-admob-unified/references/contributing.md +233 -0
  19. package/skills/ti-admob-unified/references/dependencies.md +91 -0
  20. package/skills/ti-admob-unified/references/fullscreen.md +238 -0
  21. package/skills/ti-admob-unified/references/game-monetization.md +148 -0
  22. package/skills/ti-admob-unified/references/getting-started.md +319 -0
  23. package/skills/ti-admob-unified/references/index.md +33 -0
  24. package/skills/ti-admob-unified/references/integration.md +87 -0
  25. package/skills/ti-admob-unified/references/library.md +220 -0
  26. package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
  27. package/skills/ti-admob-unified/references/mediation.md +145 -0
  28. package/skills/ti-admob-unified/references/migration.md +259 -0
  29. package/skills/ti-admob-unified/references/module-changelog.md +84 -0
  30. package/skills/ti-admob-unified/references/module-license.txt +21 -0
  31. package/skills/ti-admob-unified/references/privacy.md +281 -0
  32. package/skills/ti-admob-unified/references/production.md +67 -0
  33. package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
  34. package/skills/ti-admob-unified/references/roadmap.md +143 -0
  35. package/skills/ti-admob-unified/references/source-notes.md +80 -0
  36. package/skills/ti-admob-unified/references/source-snapshot.json +644 -0
  37. package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
  38. package/skills/ti-game/SKILL.md +31 -5
  39. package/skills/ti-game/references/api.md +36 -24
  40. package/skills/ti-game/references/debugging-performance.md +22 -7
  41. package/skills/ti-game/references/examples.md +113 -20
  42. package/skills/ti-game/references/input.md +101 -0
  43. package/skills/ti-game/references/patterns.md +56 -10
  44. package/skills/ti-game/references/physics.md +530 -0
  45. package/skills/ti-game/references/project-setup.md +131 -47
  46. package/skills/ti-game/references/recipes-action.md +149 -18
  47. package/skills/ti-game/references/recipes-adventure-effects.md +47 -4
  48. package/skills/ti-game/references/roadmap.md +11 -4
  49. package/skills/ti-game/references/source-notes.md +81 -0
  50. package/skills/ti-game/references/tilemaps.md +13 -4
  51. package/skills/ti-game-assets/SKILL.md +53 -0
  52. package/skills/ti-game-assets/agents/openai.yaml +4 -0
  53. package/skills/ti-game-assets/references/alpha.md +26 -0
  54. package/skills/ti-game-assets/references/art-direction.md +37 -0
  55. package/skills/ti-game-assets/references/integration.md +49 -0
  56. package/skills/ti-game-assets/references/manifest.example.json +429 -0
  57. package/skills/ti-game-assets/references/manifest.md +39 -0
  58. package/skills/ti-game-assets/references/offline-audio.md +33 -0
  59. package/skills/ti-game-assets/references/prompts.md +33 -0
  60. package/skills/ti-game-assets/references/repeating-textures.md +17 -0
  61. package/skills/ti-game-assets/references/responsible-ads.md +31 -0
  62. package/skills/ti-game-assets/references/sound-assets.md +42 -0
  63. package/skills/ti-game-assets/references/vector-sources.md +21 -0
  64. package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
  65. package/skills/ti-game-assets/scripts/requirements.txt +1 -0
  66. package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
  67. package/skills/ti-synthengine/SKILL.md +77 -67
  68. package/skills/ti-synthengine/assets/example/README.md +84 -0
  69. package/skills/ti-synthengine/assets/example/app.js +1178 -0
  70. package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
  71. package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
  72. package/skills/ti-synthengine/references/advanced-examples.md +174 -0
  73. package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
  74. package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
  75. package/skills/ti-synthengine/references/api.md +573 -153
  76. package/skills/ti-synthengine/references/audio-engine.md +272 -124
  77. package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
  78. package/skills/ti-synthengine/references/examples.md +355 -106
  79. package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
  80. package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
  81. package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
  82. package/skills/ti-synthengine/references/guide.md +426 -75
  83. package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
  84. package/skills/ti-synthengine/references/performance-review.json +1146 -0
  85. package/skills/ti-synthengine/references/physical-validation.json +1457 -0
  86. package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
  87. package/skills/ti-synthengine/references/recipes.md +434 -420
  88. package/skills/ti-synthengine/references/recommendations.md +72 -85
  89. package/skills/ti-synthengine/references/sound-design.md +375 -155
  90. package/skills/ti-synthengine/references/source-notes.md +109 -0
  91. package/skills/ti-synthengine/references/source-snapshot.json +452 -0
  92. package/skills/ti-synthengine/references/stats-performance.json +2992 -0
  93. package/skills/ti-synthengine/references/stats-performance.md +82 -0
  94. package/skills/ti-synthengine/references/studio-validation.json +6202 -0
  95. package/skills/ti-synthengine/references/troubleshooting.md +123 -78
  96. package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
  97. package/skills/ti-synthengine/references/unified-validation.json +314 -0
  98. package/AGENTS-VERCEL-RESEARCH.md +0 -175
@@ -0,0 +1,530 @@
1
+ # Physical models and equations
2
+
3
+ Use this reference to choose movement by its intended feel, derive parameters,
4
+ or explain frame-rate-dependent behavior. A camera angle is not a physics
5
+ model: a top-down character, car, spaceship and billiard ball use different
6
+ rules. The [demo catalog](examples.md) covers action, platforms, ball games,
7
+ adventures, board interactions and visual demonstrations.
8
+
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
+
14
+ Each equation is labeled **Engine** (native implementation), **Demo**
15
+ (application rule) or **Analytical** (a mathematical model for tuning).
16
+ An analytical equation does not imply a matching public property exists.
17
+ JS selects controls and reacts to events; frame-by-frame integration stays native.
18
+
19
+ <!-- TOC-START -->
20
+ ## Contents
21
+
22
+ - [Units and conventions](#units-and-conventions)
23
+ - [Model selection across the demos](#model-selection-across-the-demos)
24
+ - [Sprite integration and constant acceleration](#sprite-integration-and-constant-acceleration)
25
+ - [Thrust, inertia and linear damping](#thrust-inertia-and-linear-damping)
26
+ - [Projectiles and additive impulses](#projectiles-and-additive-impulses)
27
+ - [Arcade car equations](#arcade-car-equations)
28
+ - [Direct character control](#direct-character-control)
29
+ - [Jumps, flaps and scrolling runners](#jumps-flaps-and-scrolling-runners)
30
+ - [Landing and impact measurements](#landing-and-impact-measurements)
31
+ - [Static reflection and arcade ball sports](#static-reflection-and-arcade-ball-sports)
32
+ - [Equal-mass billiards and circular containment](#equal-mass-billiards-and-circular-containment)
33
+ - [Slopes and contact friction](#slopes-and-contact-friction)
34
+ - [Verlet ropes and tethers](#verlet-ropes-and-tethers)
35
+ - [Swept movement and periodic worlds](#swept-movement-and-periodic-worlds)
36
+ - [Paths, tweens and navigation](#paths-tweens-and-navigation)
37
+ - [Rhythm timing](#rhythm-timing)
38
+ - [Particle integration](#particle-integration)
39
+ - [Time step and frame rate](#time-step-and-frame-rate)
40
+ - [What needs a different model](#what-needs-a-different-model)
41
+
42
+ <!-- TOC-END -->
43
+
44
+ ## Units and conventions
45
+
46
+ | Quantity | Units |
47
+ | --- | --- |
48
+ | Position, width, height, path distance | Surface pixels, from guarded GameView `resize` |
49
+ | `velocityX/Y`, `maxSpeed` | px/s |
50
+ | `gravity`, `gravityX`, `thrust`, `enginePower` | px/s² |
51
+ | Native `dt` | Seconds |
52
+ | JS `duration`, `after`, `every`, particle lifetime | Milliseconds |
53
+ | `rotation` | Clockwise degrees; 0 points up |
54
+ | `angularVelocity`, `turnRate` | degrees/s |
55
+ | `linearDamping`, car `drag`, car `grip` | 1/s coefficients, not percentage strings |
56
+ | Rope `damping` | Retained displacement fraction per update |
57
+ | `restitution` | Dimensionless, 0..1 |
58
+
59
+ Origin is top-left; y increases downwards. Position is the sprite anchor.
60
+ For `θ = rotation·π/180`, forward `f=(sin θ,−cos θ)` and right
61
+ `r=(cos θ,sin θ)`. `n` below is a unit contact normal.
62
+
63
+ ## Model selection across the demos
64
+
65
+ | Model | Examples | What determines movement |
66
+ | --- | --- | --- |
67
+ | Inertial ship | Asteroids | Thrust adds velocity along heading; rotation does not redirect existing momentum |
68
+ | Gravity and replacement jump/flap | Platformer, Flappy, Skate, Flip | Vertical acceleration plus a chosen takeoff velocity |
69
+ | Direct character control | Topdown | Velocity follows input immediately, including diagonal normalization |
70
+ | Arcade car | Racing | Forward/lateral velocity, throttle, steering, drag and grip |
71
+ | Ball with static-surface reflection | Volley, Plinko, Circles, Slopes | Gravity, geometry normals and restitution |
72
+ | Equal-mass circle bodies | Pool, Plinko, Drum, Circles | Bilateral normal impulse and separation |
73
+ | Circular containment | Drum | Keep ball centers inside radius `R−r` |
74
+ | Position constraints | Rope | Verlet displacement and link-distance relaxation |
75
+ | Fast body or instant query | Swept, Raycast, Worldwrap | Swept movement versus a discrete ray query |
76
+ | Prescribed route | Path, Maze, Pointclick, Tilemap, Demoscene | Distance traveled along a path; A* chooses waypoints |
77
+ | Prescribed interpolation | Cards, Puzzle, Basic | Tween destination and easing; drag/snap rules |
78
+ | Timing window | Rhythm | Constant velocity and distance-to-pad acceptance |
79
+ | Ballistic effects | Particles | Native emitter rate, gravity and lifetime; no Sprite solid response |
80
+ | Cross-cutting systems | Camera, Blend, Text, Hitbox, Zones, Timescale | Presentation, geometry, events or clock; not separate locomotion models |
81
+
82
+ Names map to `example/<name>.js`, with lowercase spelling. These groups cover
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
85
+ composition, not an extra upstream demo. Use [examples.md](examples.md) for
86
+ the exact filenames and [api.md](api.md) for public properties.
87
+
88
+ ## Sprite integration and constant acceleration
89
+
90
+ **Engine**, without contacts or position overrides:
91
+
92
+ ```text
93
+ vNext = v + a·dt
94
+ pNext = p + vNext·dt
95
+ ```
96
+
97
+ This is semi-implicit Euler: acceleration before position. In Sprite, the
98
+ order is car integration, angular velocity, thrust and its speed cap,
99
+ gravity, linear damping, position, scroll wrap, path, animation, tween and
100
+ idle wobble. Scene solid resolution, post-contact damping, and attachment
101
+ passes can change that result again.
102
+ Do not combine several position writers expecting their displacements to add.
103
+
104
+ **Analytical**, constant acceleration with no cap/contact:
105
+
106
+ ```text
107
+ v(t) = v0 + a·t
108
+ p(t) = p0 + v0·t + 0.5·a·t²
109
+ ```
110
+
111
+ That position is the continuous solution, not the exact discrete output.
112
+ At constant step, `t=N·dt`, Euler gives
113
+ `pN=p0+v0·t+0.5·a·(t²+t·dt)`. Multiplying by dt is necessary but does
114
+ not eliminate integration error.
115
+
116
+ **Demo Wind**: gravityX values −900..900 px/s², leaves with vertical gravity
117
+ 260 and a puck with gravity 0. Constant sideways acceleration is an arcade
118
+ wind field; it is not drag calculated from relative air velocity.
119
+
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).
122
+
123
+ ## Thrust, inertia and linear damping
124
+
125
+ **Engine**:
126
+
127
+ ```text
128
+ rotation += angularVelocity·dt
129
+ f = (sin θ,−cos θ) # updated heading
130
+ v += f·thrust·dt
131
+ if |v| > maxSpeed: v *= maxSpeed/|v|
132
+ v += (gravityX,gravity)·dt
133
+ if linearDamping > 0:
134
+ v *= max(0,1−linearDamping·dt)
135
+ if effectively coasting and |v| < 4 px/s: v = (0,0)
136
+ p += v·dt
137
+ ```
138
+
139
+ The thrust cap runs only in its active branch; direct velocity writes are
140
+ not capped. Asteroids uses 220 degrees/s turning and both thrust and speed
141
+ cap `0.9·min(W,H)`, with no ship damping. Turning changes the nose, not the
142
+ velocity already accumulated. Assisted flight can deliberately add damping,
143
+ but it is resistance everywhere, not contact-only friction.
144
+
145
+ **Analytical** damping: `dv/dt=−cv`, hence `v(t)=v0·exp(−ct)` and speed
146
+ half-life `ln(2)/c`. With constant aligned acceleration, `dv/dt=a−cv`
147
+ tends to `a/c`. The engine approximates damping using `1−c·dt`, then
148
+ applies its rest threshold. Box2D documents the same continuous decay but
149
+ uses `1/(1+c·dt)`; do not substitute that for ti.game's implemented formula.
150
+ [Box2D damping reference](https://box2d.org/documentation/md_simulation.html).
151
+
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.
178
+
179
+ ### Coasting brake feel reference
180
+
181
+ **Application profile**, Titanium Asteroids autopilot, recorded 2026-10-12:
182
+ set `linearDamping: 0` while thrust is active and `linearDamping: 1.2` s⁻¹
183
+ when thrust stops. Native braking then multiplies velocity by
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.
187
+
188
+ César liked the speed of this braking as a reference for other games,
189
+ especially arcade racing, although he did not choose it as the preferred
190
+ feel for Asteroids. Keep the coefficient and activation condition together
191
+ when comparing the feel. This is an optional tuning example, not a default
192
+ for all games or an upstream demo preset. In a racer, evaluate longitudinal
193
+ braking and lateral grip separately before adopting it.
194
+
195
+ ## Projectiles and additive impulses
196
+
197
+ **Demo Asteroids**, nose launch:
198
+
199
+ ```text
200
+ pBullet = pShip + f·0.55·SHIP_SIZE
201
+ vBullet = vShip + f·BULLET_SPEED
202
+ BULLET_SPEED = 1.2·min(W,H)
203
+ ```
204
+
205
+ Inherited ship velocity is a deliberate application choice. A fixed
206
+ world-speed projectile instead assigns only `f·speed`.
207
+
208
+ **Analytical** impulse: `Δv=J/m`. The demos add a chosen velocity change
209
+ directly; Sprite has no configurable rigid-body mass. `v+=Δv` bends existing
210
+ motion, whereas `v=vNew` replaces it. Pool strikes are additive, Flappy's
211
+ vertical flap is a replacement, and Drum's SHAKE replaces velocities while
212
+ its contact-driven TUMBLE adds increments.
213
+
214
+ ## Arcade car equations
215
+
216
+ **Engine carMode**, forward/right axes from the old heading:
217
+
218
+ ```text
219
+ vForward = v·f; vLateral = v·r
220
+ steerFactor = clamp(vForward/(0.25·maxSpeed),−1,1)
221
+ rotation += steering·turnRate·steerFactor·dt
222
+ vForward += throttle·enginePower·dt
223
+ vForward *= max(0,1−drag·dt)
224
+ vLateral *= max(0,1−grip·dt)
225
+ vForward = clamp(vForward,−0.4·maxSpeed,maxSpeed)
226
+ v = fNew·vForward + rNew·vLateral
227
+ ```
228
+
229
+ Recomposition uses the updated heading. A stopped car with zero forward
230
+ speed does not steer; reverse forward speed reverses steering direction.
231
+ Lower grip retains more lateral drift; drag reduces forward speed.
232
+ Negative throttle can brake and then reverse. It is an arcade model,
233
+ without wheelbase geometry, individual tires or weight transfer.
234
+
235
+ **Demo Racing** uses `maxSpeed=0.8·min(W,TH)`, where TH is track height,
236
+ `enginePower=1.1·maxSpeed`, turnRate 210, grip 3.2 and drag 0.8. These numeric
237
+ values have the units in the table above, not an interchangeable percentage.
238
+ No JS loop should rotate the velocity to reproduce this native model.
239
+
240
+ Sources: Android Sprite.updateCar and iOS TGSprite.updateCar, at the same
241
+ links as [Sprite integration](#sprite-integration-and-constant-acceleration).
242
+
243
+ ## Direct character control
244
+
245
+ **Demo Topdown** uses speed `S=3.5·TILE`. For cardinal input it assigns
246
+ `v=(moveX,moveY)·S`; for diagonal input each axis uses `S/sqrt(2)` so
247
+ the combined magnitude remains S. Releasing input writes zero velocity:
248
+ there is no ship-like inertia. For a general nonzero input vector d, the
249
+ application formula is `v=S·d/|d|`; distinguish this from analog magnitude
250
+ control, which may intentionally retain the stick's partial travel.
251
+
252
+ The demo dog normalizes its target direction at `1.1S` on a decision timer.
253
+ The timer chooses direction; native velocity integrates movement between
254
+ decisions. [Top-down recipe](recipes-action.md#top-down--zelda).
255
+
256
+ ## Jumps, flaps and scrolling runners
257
+
258
+ **Demo** Platformer assigns `vx=direction·RUN_SPEED` and, on permitted
259
+ grounded input, `vy=−JUMP`. Flappy assigns `vy=−0.6H` per tap; it does not
260
+ accumulate upward impulses. Its tilt tween is visual, not aerodynamic lift.
261
+ Skate keeps player x fixed and moves terrain/obstacles at `vx=−0.5W`.
262
+
263
+ **Analytical**, gravity `g>0`, takeoff speed magnitude `J`:
264
+
265
+ ```text
266
+ timeToApex = J/g
267
+ height = J²/(2g)
268
+ sameLevelFlightTime = 2J/g
269
+ J for desired height h = sqrt(2gh)
270
+ g for height h and apex time T = 2h/T²; J = 2h/T
271
+ ```
272
+
273
+ | Demo | g | J | Continuous prediction |
274
+ | --- | --- | --- | --- |
275
+ | Platformer | 2.2H | 0.95H | Apex 0.432 s; rise 0.205H |
276
+ | Skate | 2.4H | 0.9H | Apex 0.375 s; same-level flight 0.75 s |
277
+ | Flappy | 1.9H | 0.6H | Isolated flap apex0.316 s; rise 0.095H |
278
+ | Volley players | 2.2H | 0.85H | Apex 0.386 s; rise 0.164H |
279
+
280
+ Contact geometry, ceilings and discrete integration change actual results.
281
+ `oneWay` allows top landings through otherwise passable platforms; exact
282
+ conditions differ by resolver. Moving solids carry grounded riders using
283
+ their frame delta. Skate's raised scrolling road disables `carryRiders` so
284
+ the player is not dragged off screen. [Action recipes](recipes-action.md).
285
+
286
+ ## Landing and impact measurements
287
+
288
+ **Engine** `land` reports a grounding transition after solid response.
289
+ Its payload does not include the original velocity. Live `velocityY` can
290
+ already be zero or reflected, and JS event delivery can be later still.
291
+ Do not classify a hard touchdown as gentle by reading that zero value.
292
+
293
+ For sprite solids, `solidimpact.speed` snapshots relative closing normal
294
+ speed before response, compensated for the frame's own acceleration.
295
+ Set `impactThreshold:0` to include gentle positive impacts and check the
296
+ contact normal/group. It is not full vector speed or an exact post-gravity
297
+ pre-contact sample. A rule involving tangential velocity needs separate
298
+ evidence; solid TileLayer cells do not emit `solidimpact`.
299
+
300
+ The [Lunar Lander recipe](recipes-action.md#lunar-lander) uses a static sprite
301
+ pad and this normal-impact contract. A resting body with no positive impact
302
+ does not receive an invented impact merely because its threshold is0.
303
+
304
+ Sources: Android `Scene.resolveSolids`, `dispatchSolidImpact`, `finishContacts`;
305
+ iOS `TGScene.resolveSolids`, `dispatchSolidImpactFor`, `finishContactsFor`;
306
+ see [event payload](api.md#sprite-events).
307
+
308
+ ## Static reflection and arcade ball sports
309
+
310
+ **Engine**, circle against a static solid, outward normal:
311
+
312
+ ```text
313
+ e = max(eMover,eSolid)
314
+ vn = v·n
315
+ if vn < 0:
316
+ if e > 0 and −e·vn > 40 px/s: v -= (1+e)·vn·n
317
+ else: v -= vn·n
318
+ ```
319
+
320
+ This preserves tangential velocity. The small-rebound cutoff belongs to
321
+ this response, not the equal-mass bilateral impulse below. No contact
322
+ friction or collision-generated spin is implied.
323
+
324
+ **Demo Volley** replaces the ball's velocity on player contact:
325
+ `u=normalize(pBall−pPlayer)`, `S=0.85H`,
326
+ `vx=u.x·S`, `vy=min(u.y·S,−0.45S)`. It guarantees an upward component
327
+ instead of simulating two masses. Scenario walls use native restitution;
328
+ player hits use an arcade rule. Plinko uses gravity 1100, restitution 0.42,
329
+ circle pegs and `push` chips.
330
+
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).
333
+
334
+ ## Equal-mass billiards and circular containment
335
+
336
+ **Engine**, two visible circles, both `push`, each listing the other's group:
337
+
338
+ ```text
339
+ n = normalize(centerA−centerB)
340
+ penetration = radiusA+radiusB−distance
341
+ if penetration > slop: pA+=n·penetration/2; pB−=n·penetration/2
342
+ vn = (vA−vB)·n
343
+ e = max(eA,eB)
344
+ if vn < 0:
345
+ j = −(1+e)·vn/2
346
+ vA += j·n; vB -= j·n
347
+ ```
348
+
349
+ Each pair resolves once. Restitution1 exchanges normal components; tangent
350
+ components remain unchanged. Even differently sized circles are equal-mass
351
+ bodies. Without mutual groups the response falls back to a one-sided solid.
352
+
353
+ Pool uses restitution 0.96, damping 0.62, and a cue velocity increment
354
+ `2600·min(1,tapDistance/(0.55W))` along the tap direction. That limits the
355
+ strike increment, not total speed when a moving cue is hit again. Pockets
356
+ are triggers, not 3D drops.
357
+
358
+ **Analytical**, unequal masses would require
359
+ `j=−(1+e)·vn/(1/mA+1/mB)`, then `vA+=j·n/mA`, `vB−=j·n/mB`.
360
+ That is not the current `push` implementation.
361
+
362
+ **Engine contain**: a ball of radius r inside a container of radius R has
363
+ allowed center distance `R−r`. If exceeded, the correcting normal points
364
+ toward the container center and penetration is `distance−(R−r)`.
365
+ The ball must fit. Drum uses gravity 1400, restitution 0.55, damping 0.35 and
366
+ mutual `push` balls. It mixes impulses/contact response, not a spinning
367
+ rigid drum or fluid simulation. [Shaped-solid patterns](patterns.md#shaped-solids-ramps-round-posts-and-bodies-that-push-back--slopesjs-circlesjs-pooljs-drumjs).
368
+
369
+ ## Slopes and contact friction
370
+
371
+ Rotated ramps require `rotatedRect` geometry for the real face normal.
372
+ The normal component is cancelled/reflected while tangent velocity remains.
373
+ **Analytical**, on a fixed frictionless slope at angle α from horizontal,
374
+ downslope acceleration magnitude is `g·abs(sin α)`.
375
+
376
+ Slopes uses gravity 1100, crate restitution 0, ball0.1 and floor/walls0.5.
377
+ Its ball slides; a circle shape does not add rolling torque. Sprite damping
378
+ acts off the slope too and cannot substitute contact-only tangent friction.
379
+ Smooth uphill walking, connected terrain, mass and torque remain outside
380
+ this model. [Limitations](roadmap.md).
381
+
382
+ ## Verlet ropes and tethers
383
+
384
+ **Engine**, a free node:
385
+
386
+ ```text
387
+ displacement = (pCurrent−pPrevious)·damping
388
+ pPrevious = pCurrent
389
+ pCurrent += displacement + (0,gravity·dt²)
390
+ ```
391
+
392
+ For adjacent nodes, `Δ=pB−pA`, `d=|Δ|`, target length L, correction
393
+ `Δ·(d−L)/d` is split between free nodes, or applied fully to the free node
394
+ when the other is pinned. Division has an epsilon guard. `iterations` repeats
395
+ relaxation: more passes stiffen the result at a CPU cost.
396
+
397
+ The first Rope demo uses 14 links of length 0.04H, gravity 1.6H,
398
+ damping 0.985 and 3 constraint passes. Nodes do not automatically receive
399
+ Sprite solid collisions. `tail` plus positive `maxLength` additionally
400
+ clamps head-to-tail distance and removes outward radial velocity. With two
401
+ free sprite ends the correction is split; dragging an end makes the other
402
+ yield. It is a positional tether, not a mass/stiffness Hooke spring.
403
+
404
+ Rope damping is **per update**, not1/s. Before forces and constraints, the
405
+ retained displacement factor over one second at 0.985 would be about 0.635
406
+ at 30 updates,0.404 at 60 and 0.163 at 120. These are not predictions of the
407
+ whole rope's amplitude. An analytical time-normalized alternative would be
408
+ `exp(−c·dt)` or `dRef^(dt/dtRef)`; the engine uses the fixed supplied factor.
409
+
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.
423
+
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).
426
+
427
+ ## Swept movement and periodic worlds
428
+
429
+ **Analytical** per-step movement is `Δp=v·dt`:9600 px/s travels160 px
430
+ at 60 FPS. A final-position overlap can skip a thin wall.
431
+ Swept tests examine the traveled segment; circle pairs have circular sweeps,
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
437
+ sweep skips `contain`/`push`; those still use discrete responses. It is not
438
+ a general multiple-substep rigid-body simulation.
439
+
440
+ Raycast is an instantaneous query, not a gravity-driven projectile.
441
+ `wrapAround` uses screen edges; `wrapX/wrapShift` loops scrolling copies;
442
+ `worldWrapX` defines a periodic horizontal world with participating sprites.
443
+ **Analytical** normalization for `L=maxX−minX` is
444
+ `x=minX+((x−minX)%L+L)%L`; nearest image displacement is
445
+ `dx−L·round(dx/L)`. Raycast, A*, particles, ropes and skid trails do not
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).
452
+
453
+ ## Paths, tweens and navigation
454
+
455
+ **Engine Path** accumulates `distance+=speed·dt`. Within a segment,
456
+ `u=(distance−segmentStart)/segmentLength`,
457
+ `p=pStart+u·(pEnd−pStart)`. Optional rotation is
458
+ `atan2(dx,−dy)·180/π`. A* chooses the waypoints; it does not apply a force.
459
+ Maze/Tilemap use4·TILE px/s. Path motion overrides integrated position;
460
+ a later position tween can override the path again.
461
+
462
+ **Engine Tween** uses `u=clamp(elapsed/duration,0,1)` after its delay,
463
+ then `value=from+(to−from)·Easing(u)`. Cards, puzzles and Basic use
464
+ prescribed destinations/gestures, not conservation of momentum.
465
+ Camera/parallax, blend, text, flip and pixel snapping are presentation;
466
+ they do not change those motion equations. Snapping rounds the drawn anchor,
467
+ not physical position, and cannot eliminate every rotational raster change.
468
+
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).
471
+
472
+ ## Rhythm timing
473
+
474
+ **Demo**, `vy=(PAD_Y+NOTE)/1.6`, starting at `y=−NOTE`, gives a 1.6 s
475
+ trip to the pad. GOOD allows 0.08H distance and PERFECT0.03H.
476
+ **Analytical** temporal error is `abs(y−PAD_Y)/abs(vy)`; changing fall speed
477
+ changes the milliseconds represented by a fixed pixel window.
478
+
479
+ At the demo's stated 150 BPM, quarter duration is `60000/150=400 ms`;
480
+ an eighth is200 ms. The400 ms spawn interval and 1.6s travel are one and four
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.
484
+
485
+ This is a comment/recipe error, not evidence of a faulty native beat engine.
486
+ The demo uses `setInterval(spawn,SPAWN_MS)` and independent `music.play()`;
487
+ it has no audio-position-driven scheduling. Exact musical synchronization,
488
+ actual asset tempo and phase would need separate measurement. A comment-only
489
+ upstream patch should not halve SPAWN_MS or replace the motor.
490
+
491
+ Source: [Rhythm demo](https://github.com/m1ga/ti.game/blob/88e181cb85526d8146d0be6e09b2b90c2551baad/example/rhythm.js).
492
+
493
+ ## Particle integration
494
+
495
+ **Engine** accumulates `rate·dt`, emits the integer part and keeps the
496
+ remainder. Live particles age, receive `vy+=gravity·dt`, then position
497
+ updates with the new velocity. At lifetime they retire. They are effects,
498
+ not Sprite solids or automatic damage sensors. [Particle recipes](recipes-adventure-effects.md#particles).
499
+
500
+ ## Time step and frame rate
501
+
502
+ Both renderers derive a variable real dt, cap it at 0.1s and then scale by
503
+ `max(0,timeScale)`. They do not run a fixed-step accumulator with physics
504
+ substeps. `maxFps` is a cap, not a guarantee of exact dt. Differences can
505
+ come from Euler error, rest/rebound thresholds, per-update Rope damping,
506
+ surface-based parameters, input/lifecycle or different installed binaries.
507
+
508
+ GameView timers follow scene time, conventional JS timers do not. Rendering
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.
512
+
513
+ For tuning, capture the build, actual FPS, W/H, timeScale, forces and
514
+ velocities. Check rest-to-powered motion, releases, turns, contacts and pauses
515
+ at 30/60/120 FPS, then validate on the target hardware. Scalar equations and
516
+ source parity are useful evidence, not certificates of identical device feel.
517
+ [Telemetry workflow](debugging-performance.md).
518
+
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).
521
+
522
+ ## What needs a different model
523
+
524
+ Assisted spacecraft flight can use deliberate resistance, but it is not
525
+ airplane aerodynamics. A fuller aircraft model needs relative airspeed,
526
+ lift, drag, stall, altitude and bank response. Ships, tires, springs,
527
+ variable masses and collision torque also need separate modeling choices.
528
+ Do not claim those simulations from a sprite's artwork or camera angle.
529
+ See [roadmap.md](roadmap.md) before naming a missing API, and retain the
530
+ distinction between analytical tuning, application rules and native support.