dsh-drawai 0.3.4 → 0.4.0

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 (533) hide show
  1. package/LICENSE +4142 -0
  2. package/README.md +43 -10
  3. package/lib/audio-media.js +147 -0
  4. package/lib/audio-service.js +66 -0
  5. package/lib/canvas-format.js +134 -0
  6. package/lib/client.js +5075 -181
  7. package/lib/comfyui-default-workflow.mjs +1825 -0
  8. package/lib/comfyui-workflow.mjs +426 -0
  9. package/lib/document-commands.js +12 -0
  10. package/lib/document-core.js +29 -0
  11. package/lib/drawing-tasks.js +26 -0
  12. package/lib/imagegen.mjs +596 -0
  13. package/lib/index.js +1758 -129
  14. package/lib/media.js +26 -13
  15. package/lib/mgstyle-core.js +75 -0
  16. package/lib/motion/apply.js +180 -0
  17. package/lib/motion/asset-pack-lock.js +10 -0
  18. package/lib/motion/asset-pack.js +335 -0
  19. package/lib/motion/assets.js +290 -0
  20. package/lib/motion/audio.js +103 -0
  21. package/lib/motion/audiolevel.js +63 -0
  22. package/lib/motion/audiomix.js +144 -0
  23. package/lib/motion/beats.js +337 -0
  24. package/lib/motion/builtin-assets.js +62 -0
  25. package/lib/motion/canvas-spec.js +442 -0
  26. package/lib/motion/capture.js +95 -0
  27. package/lib/motion/cards.js +80 -0
  28. package/lib/motion/collect.js +447 -0
  29. package/lib/motion/delivery.js +112 -0
  30. package/lib/motion/edge-speech.py +90 -0
  31. package/lib/motion/entry.jsx +73 -0
  32. package/lib/motion/export.js +200 -0
  33. package/lib/motion/ffmpeg.js +42 -0
  34. package/lib/motion/fonts.js +71 -0
  35. package/lib/motion/jobs.js +113 -0
  36. package/lib/motion/json.js +4 -0
  37. package/lib/motion/media-catalog.js +59 -0
  38. package/lib/motion/mgstyles/BRIEF-TEMPLATE.md +63 -0
  39. package/lib/motion/mgstyles/briefs/01-flat-vector.md +104 -0
  40. package/lib/motion/mgstyles/briefs/02-line-art.md +97 -0
  41. package/lib/motion/mgstyles/briefs/03-isometric.md +108 -0
  42. package/lib/motion/mgstyles/briefs/05-cel-boil.md +145 -0
  43. package/lib/motion/mgstyles/briefs/06-collage.md +149 -0
  44. package/lib/motion/mgstyles/briefs/07-liquid.md +106 -0
  45. package/lib/motion/mgstyles/briefs/08-morph.md +111 -0
  46. package/lib/motion/mgstyles/briefs/09-bauhaus.md +150 -0
  47. package/lib/motion/mgstyles/briefs/10-synthwave.md +151 -0
  48. package/lib/motion/mgstyles/briefs/12-aurora-glass.md +147 -0
  49. package/lib/motion/mgstyles/briefs/18-hanazi.md +140 -0
  50. package/lib/motion/mgstyles/briefs/19-paperclip.md +156 -0
  51. package/lib/motion/mgstyles/briefs/20-pixel.md +118 -0
  52. package/lib/motion/mgstyles/briefs/22-hud.md +118 -0
  53. package/lib/motion/mgstyles/catalog.d/01-flat-vector.json +34 -0
  54. package/lib/motion/mgstyles/catalog.d/02-line-art.json +34 -0
  55. package/lib/motion/mgstyles/catalog.d/03-isometric.json +35 -0
  56. package/lib/motion/mgstyles/catalog.d/05-cel-boil.json +28 -0
  57. package/lib/motion/mgstyles/catalog.d/06-collage.json +28 -0
  58. package/lib/motion/mgstyles/catalog.d/07-liquid.json +28 -0
  59. package/lib/motion/mgstyles/catalog.d/08-morph.json +28 -0
  60. package/lib/motion/mgstyles/catalog.d/09-bauhaus.json +28 -0
  61. package/lib/motion/mgstyles/catalog.d/10-synthwave.json +28 -0
  62. package/lib/motion/mgstyles/catalog.d/12-aurora-glass.json +28 -0
  63. package/lib/motion/mgstyles/catalog.d/18-hanazi.json +28 -0
  64. package/lib/motion/mgstyles/catalog.d/19-paperclip.json +28 -0
  65. package/lib/motion/mgstyles/catalog.d/20-pixel.json +28 -0
  66. package/lib/motion/mgstyles/catalog.d/22-hud.json +28 -0
  67. package/lib/motion/mgstyles/catalog.json +1356 -0
  68. package/lib/motion/mgstyles/upstream/README.md +147 -0
  69. package/lib/motion/mgstyles/upstream/README.zh-CN.md +147 -0
  70. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/cues.json +1 -0
  71. package/lib/motion/mgstyles/upstream/demos/01-flat-vector/index.html +712 -0
  72. package/lib/motion/mgstyles/upstream/demos/02-line-art/cues.json +331 -0
  73. package/lib/motion/mgstyles/upstream/demos/02-line-art/film.js +676 -0
  74. package/lib/motion/mgstyles/upstream/demos/02-line-art/index.html +18 -0
  75. package/lib/motion/mgstyles/upstream/demos/02-line-art/tools/dump_timing.mjs +26 -0
  76. package/lib/motion/mgstyles/upstream/demos/03-isometric/cues.json +109 -0
  77. package/lib/motion/mgstyles/upstream/demos/03-isometric/index.html +25 -0
  78. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/anim.js +82 -0
  79. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/main.js +219 -0
  80. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/night5.js +180 -0
  81. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/render.js +202 -0
  82. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title.js +167 -0
  83. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/title5.js +294 -0
  84. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/ui.js +141 -0
  85. package/lib/motion/mgstyles/upstream/demos/03-isometric/js/world.js +1399 -0
  86. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/cues.json +157 -0
  87. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/index.html +34 -0
  88. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/engine.js +326 -0
  89. package/lib/motion/mgstyles/upstream/demos/05-cel-boil/js/scene.js +2036 -0
  90. package/lib/motion/mgstyles/upstream/demos/06-collage/assets/gen/layout.json +157 -0
  91. package/lib/motion/mgstyles/upstream/demos/06-collage/cues.json +80 -0
  92. package/lib/motion/mgstyles/upstream/demos/06-collage/index.html +889 -0
  93. package/lib/motion/mgstyles/upstream/demos/07-liquid/cues.json +100 -0
  94. package/lib/motion/mgstyles/upstream/demos/07-liquid/index.html +108 -0
  95. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/engine.js +153 -0
  96. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/links.js +64 -0
  97. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/lookdev.js +46 -0
  98. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/scene.js +963 -0
  99. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/shaders.js +647 -0
  100. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/textsdf.js +114 -0
  101. package/lib/motion/mgstyles/upstream/demos/07-liquid/js/type.js +50 -0
  102. package/lib/motion/mgstyles/upstream/demos/08-morph/cues.json +131 -0
  103. package/lib/motion/mgstyles/upstream/demos/08-morph/glyphs.json +1 -0
  104. package/lib/motion/mgstyles/upstream/demos/08-morph/index.html +901 -0
  105. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/cues.json +270 -0
  106. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/film.js +697 -0
  107. package/lib/motion/mgstyles/upstream/demos/09-bauhaus/index.html +12 -0
  108. package/lib/motion/mgstyles/upstream/demos/10-synthwave/cues.json +44 -0
  109. package/lib/motion/mgstyles/upstream/demos/10-synthwave/index.html +1028 -0
  110. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/cues.json +67 -0
  111. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/index.html +16 -0
  112. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/main.js +967 -0
  113. package/lib/motion/mgstyles/upstream/demos/12-aurora-glass/shaders.js +468 -0
  114. package/lib/motion/mgstyles/upstream/demos/18-hanazi/cues.json +117 -0
  115. package/lib/motion/mgstyles/upstream/demos/18-hanazi/index.html +541 -0
  116. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/countries-110m.json +1 -0
  117. package/lib/motion/mgstyles/upstream/demos/19-paperclip/assets/stickers/meta.json +34 -0
  118. package/lib/motion/mgstyles/upstream/demos/19-paperclip/cues.json +285 -0
  119. package/lib/motion/mgstyles/upstream/demos/19-paperclip/index.html +599 -0
  120. package/lib/motion/mgstyles/upstream/demos/20-pixel/cues.json +42 -0
  121. package/lib/motion/mgstyles/upstream/demos/20-pixel/film.js +914 -0
  122. package/lib/motion/mgstyles/upstream/demos/20-pixel/index.html +18 -0
  123. package/lib/motion/mgstyles/upstream/demos/22-hud/cues.json +108 -0
  124. package/lib/motion/mgstyles/upstream/demos/22-hud/index.html +125 -0
  125. package/lib/motion/mgstyles/upstream/demos/22-hud/js/holo.js +275 -0
  126. package/lib/motion/mgstyles/upstream/demos/22-hud/js/hud.js +855 -0
  127. package/lib/motion/mgstyles/upstream/demos/22-hud/js/post.js +150 -0
  128. package/lib/motion/mgstyles/upstream/demos/22-hud/js/util.js +65 -0
  129. package/lib/motion/mgstyles/upstream/demos/22-hud/js/v5.js +142 -0
  130. package/lib/motion/mgstyles/upstream/harness/render.mjs +319 -0
  131. package/lib/motion/mgstyles/upstream/manifest.json +269 -0
  132. package/lib/motion/mgstyles/upstream/prompts/01-flat-vector.md +98 -0
  133. package/lib/motion/mgstyles/upstream/prompts/02-line-art.md +95 -0
  134. package/lib/motion/mgstyles/upstream/prompts/03-isometric.md +97 -0
  135. package/lib/motion/mgstyles/upstream/prompts/05-cel-boil.md +95 -0
  136. package/lib/motion/mgstyles/upstream/prompts/06-collage.md +95 -0
  137. package/lib/motion/mgstyles/upstream/prompts/07-liquid.md +98 -0
  138. package/lib/motion/mgstyles/upstream/prompts/08-morph.md +95 -0
  139. package/lib/motion/mgstyles/upstream/prompts/09-bauhaus.md +96 -0
  140. package/lib/motion/mgstyles/upstream/prompts/10-synthwave.md +95 -0
  141. package/lib/motion/mgstyles/upstream/prompts/12-aurora-glass.md +96 -0
  142. package/lib/motion/mgstyles/upstream/prompts/18-hanazi.md +95 -0
  143. package/lib/motion/mgstyles/upstream/prompts/19-paperclip.md +96 -0
  144. package/lib/motion/mgstyles/upstream/prompts/20-pixel.md +96 -0
  145. package/lib/motion/mgstyles/upstream/prompts/22-hud.md +95 -0
  146. package/lib/motion/mgstyles/upstream/rubric.md +51 -0
  147. package/lib/motion/mgstyles/upstream/template.md +36 -0
  148. package/lib/motion/pixelqa.js +198 -0
  149. package/lib/motion/preview.js +95 -0
  150. package/lib/motion/project.js +395 -0
  151. package/lib/motion/qa.js +290 -0
  152. package/lib/motion/reference-media.jsx +10 -0
  153. package/lib/motion/render-settings.js +112 -0
  154. package/lib/motion/render.js +159 -0
  155. package/lib/motion/scene-kit.js +183 -0
  156. package/lib/motion/sfx.js +232 -0
  157. package/lib/motion/shotcraft/ClipCard.tsx +204 -0
  158. package/lib/motion/shotcraft/catalog.json +2461 -0
  159. package/lib/motion/shotcraft/upstream/SKILL.md +286 -0
  160. package/lib/motion/shotcraft/upstream/assets/lib/Caption.tsx +54 -0
  161. package/lib/motion/shotcraft/upstream/assets/lib/ClipCard.tsx +200 -0
  162. package/lib/motion/shotcraft/upstream/assets/lib/DigitRoll.tsx +45 -0
  163. package/lib/motion/shotcraft/upstream/assets/lib/FlashCut.tsx +20 -0
  164. package/lib/motion/shotcraft/upstream/assets/lib/FlatPanel.tsx +65 -0
  165. package/lib/motion/shotcraft/upstream/assets/lib/PageCam.tsx +151 -0
  166. package/lib/motion/shotcraft/upstream/assets/lib/VerticalTicker.tsx +129 -0
  167. package/lib/motion/shotcraft/upstream/assets/lib/helpers/camera.tsx +75 -0
  168. package/lib/motion/shotcraft/upstream/assets/lib/helpers/motion.ts +40 -0
  169. package/lib/motion/shotcraft/upstream/assets/lib/helpers/rand.ts +11 -0
  170. package/lib/motion/shotcraft/upstream/assets/lib/helpers/shake.ts +10 -0
  171. package/lib/motion/shotcraft/upstream/assets/scripts/__pycache__/sfx-source.cpython-313.pyc +0 -0
  172. package/lib/motion/shotcraft/upstream/assets/scripts/build-readme-cover.mjs +49 -0
  173. package/lib/motion/shotcraft/upstream/assets/scripts/capture-template.mjs +210 -0
  174. package/lib/motion/shotcraft/upstream/assets/scripts/sfx-source.py +423 -0
  175. package/lib/motion/shotcraft/upstream/assets/scripts/smoke-render-demos.py +170 -0
  176. package/lib/motion/shotcraft/upstream/assets/scripts/test_sfx_source.py +157 -0
  177. package/lib/motion/shotcraft/upstream/demos/README.md +120 -0
  178. package/lib/motion/shotcraft/upstream/demos/_fixtures/Brand.tsx +112 -0
  179. package/lib/motion/shotcraft/upstream/demos/_fixtures/Fixtures.tsx +647 -0
  180. package/lib/motion/shotcraft/upstream/demos/_fixtures/Look.tsx +316 -0
  181. package/lib/motion/shotcraft/upstream/demos/_fixtures/Motion.tsx +82 -0
  182. package/lib/motion/shotcraft/upstream/demos/_fixtures/PageCam2D.tsx +115 -0
  183. package/lib/motion/shotcraft/upstream/demos/_fixtures/Polish.tsx +243 -0
  184. package/lib/motion/shotcraft/upstream/demos/_textures/live-layout.json +389 -0
  185. package/lib/motion/shotcraft/upstream/demos/camera/basic-3d-scene/Basic3DScene.tsx +250 -0
  186. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashImpactReal.tsx +305 -0
  187. package/lib/motion/shotcraft/upstream/demos/camera/crash-zoom-punch/CrashZoomReal.tsx +246 -0
  188. package/lib/motion/shotcraft/upstream/demos/camera/cursor-flyover/CursorFlyover.tsx +390 -0
  189. package/lib/motion/shotcraft/upstream/demos/camera/depth-layer-moves/MultiplaneReal.tsx +138 -0
  190. package/lib/motion/shotcraft/upstream/demos/camera/graze-face-tour/GrazeFaceTour.tsx +542 -0
  191. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/OverheadTabletopDrop.tsx +315 -0
  192. package/lib/motion/shotcraft/upstream/demos/camera/overhead-camera-moves/TiltReveal.tsx +299 -0
  193. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/DroneDiveLanding.tsx +173 -0
  194. package/lib/motion/shotcraft/upstream/demos/camera/space-camera-moves/ExplodedView.tsx +348 -0
  195. package/lib/motion/shotcraft/upstream/demos/camera/steep-tilt-glide/SteepTiltGlide.tsx +366 -0
  196. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/BulletTimeFreezeOrbit.tsx +279 -0
  197. package/lib/motion/shotcraft/upstream/demos/camera/tension-camera-moves/PullBackIsolation.tsx +184 -0
  198. package/lib/motion/shotcraft/upstream/demos/camera/terminal-3d/Terminal3D.tsx +390 -0
  199. package/lib/motion/shotcraft/upstream/demos/data/before-after-slider-scrub/BeforeAfterSliderScrub.tsx +155 -0
  200. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/AxisRescaleShockV2.tsx +303 -0
  201. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/OscilloscopeStreamV2.tsx +290 -0
  202. package/lib/motion/shotcraft/upstream/demos/data/chart-live-moves/UnitDotSwarmRegroupV2.tsx +278 -0
  203. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/NeedleSweepSelftest.tsx +307 -0
  204. package/lib/motion/shotcraft/upstream/demos/data/gauge-readout-moves/TapeScrollFixedPointer.tsx +215 -0
  205. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/BrakeReticleLock.tsx +299 -0
  206. package/lib/motion/shotcraft/upstream/demos/data/scroll-brake-moves/ChangelogScrollBrake.tsx +232 -0
  207. package/lib/motion/shotcraft/upstream/demos/data/timeline-travel/TimelineTravel.tsx +290 -0
  208. package/lib/motion/shotcraft/upstream/demos/effects/assemble-then-type-flyin/AssembleThenTypeFlyin.tsx +273 -0
  209. package/lib/motion/shotcraft/upstream/demos/effects/aurora-bloom-bg-flip/AuroraBloomBgFlip.tsx +167 -0
  210. package/lib/motion/shotcraft/upstream/demos/effects/brand-frame-snap/BrandFrameSnap.tsx +536 -0
  211. package/lib/motion/shotcraft/upstream/demos/effects/fui-hud-moves/ReticleLockOn.tsx +164 -0
  212. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/FlylineArc.tsx +175 -0
  213. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/GlowOrbAmbient.tsx +197 -0
  214. package/lib/motion/shotcraft/upstream/demos/effects/glow-flyline-moves/OrbFlylineRelay.tsx +241 -0
  215. package/lib/motion/shotcraft/upstream/demos/effects/icon-performance-moves/AttentionBounce.tsx +279 -0
  216. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/AnimeImpact.tsx +230 -0
  217. package/lib/motion/shotcraft/upstream/demos/effects/impact-feedback/HitCounter.tsx +345 -0
  218. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SheenSweepRetry.tsx +275 -0
  219. package/lib/motion/shotcraft/upstream/demos/effects/light-play-moves/SpotlightSweepReveal.tsx +229 -0
  220. package/lib/motion/shotcraft/upstream/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx +312 -0
  221. package/lib/motion/shotcraft/upstream/demos/effects/riso-print-hits/RisoMisregistrationHit.tsx +222 -0
  222. package/lib/motion/shotcraft/upstream/demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx +295 -0
  223. package/lib/motion/shotcraft/upstream/demos/effects/scanline-annotate-focus/ScanlineAnnotateFocus.tsx +329 -0
  224. package/lib/motion/shotcraft/upstream/demos/effects/scanline-assemble-flyin/ScanlineAssembleFlyin.tsx +315 -0
  225. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ImpactBurstKit.tsx +264 -0
  226. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/KanadaPerspectiveSnap.tsx +260 -0
  227. package/lib/motion/shotcraft/upstream/demos/effects/slam-entrance-moves/ScoreSlam.tsx +263 -0
  228. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/CornerSpotlightReveal.tsx +123 -0
  229. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/GlowWakeSleepPanel.tsx +187 -0
  230. package/lib/motion/shotcraft/upstream/demos/effects/spotlight-sweep-moves/SlideSpotlightPan.tsx +175 -0
  231. package/lib/motion/shotcraft/upstream/demos/interaction/autolayout-gap-dial/AutolayoutGapDial.tsx +305 -0
  232. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/DiagramCascadeBuild.tsx +350 -0
  233. package/lib/motion/shotcraft/upstream/demos/interaction/canvas-materialize-moves/PanelToCanvasMaterialize.tsx +352 -0
  234. package/lib/motion/shotcraft/upstream/demos/interaction/chip-grid-single-select-blackout/ChipGridSingleSelectBlackout.tsx +218 -0
  235. package/lib/motion/shotcraft/upstream/demos/interaction/collab-cursor-moves/CursorCastEnsemble.tsx +297 -0
  236. package/lib/motion/shotcraft/upstream/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx +157 -0
  237. package/lib/motion/shotcraft/upstream/demos/interaction/hashtag-to-pill-materialize/HashtagToPillMaterialize.tsx +218 -0
  238. package/lib/motion/shotcraft/upstream/demos/interaction/picker-carousel-feature-cycle/PickerCarouselFeatureCycle.tsx +202 -0
  239. package/lib/motion/shotcraft/upstream/demos/interaction/segmented-thumb-hero/SegmentedThumbHero.tsx +265 -0
  240. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/PaletteThemeRipple.tsx +457 -0
  241. package/lib/motion/shotcraft/upstream/demos/interaction/theme-switch-moves/ThemeSweepToggle.tsx +446 -0
  242. package/lib/motion/shotcraft/upstream/demos/interaction/type-and-filter/TypeAndFilter.tsx +249 -0
  243. package/lib/motion/shotcraft/upstream/demos/interaction/voice-waveform-live/VoiceWaveformLive.tsx +278 -0
  244. package/lib/motion/shotcraft/upstream/demos/opening/crane-rise-reveal/CraneRiseReveal.tsx +332 -0
  245. package/lib/motion/shotcraft/upstream/demos/opening/dataviz-landscape-open/DatavizLandscapeOpen.tsx +358 -0
  246. package/lib/motion/shotcraft/upstream/demos/opening/fracture/Fracture.tsx +201 -0
  247. package/lib/motion/shotcraft/upstream/demos/opening/icon-field-colorize/IconFieldColorize.tsx +200 -0
  248. package/lib/motion/shotcraft/upstream/demos/opening/letterspace-materialize/LetterspaceMaterialize.tsx +208 -0
  249. package/lib/motion/shotcraft/upstream/demos/opening/magician-card-flourish/MagicianCardFlourish.tsx +341 -0
  250. package/lib/motion/shotcraft/upstream/demos/opening/orbit-ring-title-open/OrbitRingTitleOpen.tsx +389 -0
  251. package/lib/motion/shotcraft/upstream/demos/opening/spotlight-hero-card/SpotlightHeroCard.tsx +293 -0
  252. package/lib/motion/shotcraft/upstream/demos/opening/stroke-segment-build/StrokeSegmentBuild.tsx +183 -0
  253. package/lib/motion/shotcraft/upstream/demos/opening/text-as-mask/TextAsMask.tsx +245 -0
  254. package/lib/motion/shotcraft/upstream/demos/outro/grain-dissolve/GrainDissolve.tsx +222 -0
  255. package/lib/motion/shotcraft/upstream/demos/outro/logo-shrink-wordmark-lockup/LogoShrinkWordmarkLockup.tsx +223 -0
  256. package/lib/motion/shotcraft/upstream/demos/outro/neon-triple-marquee/NeonTripleMarquee.tsx +217 -0
  257. package/lib/motion/shotcraft/upstream/demos/outro/outro-group-photo-launch/OutroGroupPhotoLaunch.tsx +343 -0
  258. package/lib/motion/shotcraft/upstream/demos/outro/ui-strip-away-outro/UiStripAwayOutro.tsx +357 -0
  259. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/IconFlipBloomLogo.tsx +187 -0
  260. package/lib/motion/shotcraft/upstream/demos/outro/ui-to-brand-morph/InputMorphsIntoLogo.tsx +333 -0
  261. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-cut-moves/PaparazziFlash.tsx +274 -0
  262. package/lib/motion/shotcraft/upstream/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx +270 -0
  263. package/lib/motion/shotcraft/upstream/demos/rhythm/panel-grid-moves/ComicPanelSplit.tsx +430 -0
  264. package/lib/motion/shotcraft/upstream/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx +252 -0
  265. package/lib/motion/shotcraft/upstream/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx +106 -0
  266. package/lib/motion/shotcraft/upstream/demos/rhythm/sakuga-timing-shift/SakugaTimingShift.tsx +288 -0
  267. package/lib/motion/shotcraft/upstream/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx +362 -0
  268. package/lib/motion/shotcraft/upstream/demos/rhythm/trailer-grammar-moves/TrailerBumper.tsx +168 -0
  269. package/lib/motion/shotcraft/upstream/demos/transition/bottom-push-stack-wipe/BottomPushStackWipe.tsx +310 -0
  270. package/lib/motion/shotcraft/upstream/demos/transition/card-flip-reveal/CardFlipReveal.tsx +261 -0
  271. package/lib/motion/shotcraft/upstream/demos/transition/card-flock-tumble/CardFlockTumble.tsx +459 -0
  272. package/lib/motion/shotcraft/upstream/demos/transition/circle-match-iris/CircleMatchIris.tsx +294 -0
  273. package/lib/motion/shotcraft/upstream/demos/transition/gradient-transition/GradientTransition.tsx +161 -0
  274. package/lib/motion/shotcraft/upstream/demos/transition/line-carry-transition/LineCarryTransition.tsx +248 -0
  275. package/lib/motion/shotcraft/upstream/demos/transition/mosaic-reframe/MosaicReframe.tsx +359 -0
  276. package/lib/motion/shotcraft/upstream/demos/transition/page-turn-transitions/BarnDoorSplit.tsx +227 -0
  277. package/lib/motion/shotcraft/upstream/demos/transition/print-texture-transitions/InkBleedReveal.tsx +162 -0
  278. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/PortalWipeV2.tsx +248 -0
  279. package/lib/motion/shotcraft/upstream/demos/transition/shot-transitions/WhipBrakeReal.tsx +186 -0
  280. package/lib/motion/shotcraft/upstream/demos/transition/tear-streak-transitions/GlitchDisplace.tsx +355 -0
  281. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/InvisibleCut.tsx +272 -0
  282. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/LightLeakBurn.tsx +262 -0
  283. package/lib/motion/shotcraft/upstream/demos/transition/transition-hidden-cut/VersusSlam.tsx +217 -0
  284. package/lib/motion/shotcraft/upstream/demos/transition/transition-travel/LetterformZoom.tsx +285 -0
  285. package/lib/motion/shotcraft/upstream/demos/transition/white-flash-logo-simplify-cut/WhiteFlashLogoSimplifyCut.tsx +205 -0
  286. package/lib/motion/shotcraft/upstream/demos/transition/wipe-transitions/BlindsSlice.tsx +178 -0
  287. package/lib/motion/shotcraft/upstream/demos/typography/blur-slide/BlurSlide.tsx +147 -0
  288. package/lib/motion/shotcraft/upstream/demos/typography/brace-expand/BraceExpand.tsx +143 -0
  289. package/lib/motion/shotcraft/upstream/demos/typography/brand-ink-open/BrandInkOpen.tsx +134 -0
  290. package/lib/motion/shotcraft/upstream/demos/typography/countdown-arc-scatter/CountdownArcScatter.tsx +217 -0
  291. package/lib/motion/shotcraft/upstream/demos/typography/flying-words/FlyingWords.tsx +198 -0
  292. package/lib/motion/shotcraft/upstream/demos/typography/glitch-cycle/GlitchCycle.tsx +221 -0
  293. package/lib/motion/shotcraft/upstream/demos/typography/gradient-word-sweep/GradientWordSweep.tsx +312 -0
  294. package/lib/motion/shotcraft/upstream/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx +261 -0
  295. package/lib/motion/shotcraft/upstream/demos/typography/marker-underline-title/MarkerUnderlineTitle.tsx +121 -0
  296. package/lib/motion/shotcraft/upstream/demos/typography/outline-word-fill/OutlineWordFill.tsx +153 -0
  297. package/lib/motion/shotcraft/upstream/demos/typography/paper-title-card/PaperTitleCard.tsx +179 -0
  298. package/lib/motion/shotcraft/upstream/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx +140 -0
  299. package/lib/motion/shotcraft/upstream/demos/typography/pill-slot-cycle/PillSlotCycle.tsx +309 -0
  300. package/lib/motion/shotcraft/upstream/demos/typography/text-column-converge/TextColumnConverge.tsx +191 -0
  301. package/lib/motion/shotcraft/upstream/demos/typography/title-demote-to-label/TitleDemoteToLabel.tsx +292 -0
  302. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/LetterformDriftAssembly.tsx +135 -0
  303. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/SplitTextStagger.tsx +124 -0
  304. package/lib/motion/shotcraft/upstream/demos/typography/type-assembly-moves/TextOnPath.tsx +255 -0
  305. package/lib/motion/shotcraft/upstream/demos/typography/type-entrance-moves/LetterDropPhysics.tsx +173 -0
  306. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/FontWeightPump.tsx +225 -0
  307. package/lib/motion/shotcraft/upstream/demos/typography/type-rhythm-sync/KaraokeFillSync.tsx +141 -0
  308. package/lib/motion/shotcraft/upstream/demos/typography/typewriter-moves/TypewriterErrorRetype.tsx +137 -0
  309. package/lib/motion/shotcraft/upstream/demos/typography/vertical-word-roll-blur-cycle/VerticalWordRollBlurCycle.tsx +147 -0
  310. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-filmstrip/WordRelayFilmstrip.tsx +409 -0
  311. package/lib/motion/shotcraft/upstream/demos/typography/word-relay-geometry/WordRelayGeometry.tsx +280 -0
  312. package/lib/motion/shotcraft/upstream/demos/ui-entrance/avatar-bracket-carousel/AvatarBracketCarousel.tsx +271 -0
  313. package/lib/motion/shotcraft/upstream/demos/ui-entrance/bezier-source-converge-merge/BezierSourceConvergeMerge.tsx +385 -0
  314. package/lib/motion/shotcraft/upstream/demos/ui-entrance/card-stack/CardStack.tsx +312 -0
  315. package/lib/motion/shotcraft/upstream/demos/ui-entrance/carousel-3d/Carousel3D.tsx +303 -0
  316. package/lib/motion/shotcraft/upstream/demos/ui-entrance/clipcard-looping/ClipCardLooping.tsx +77 -0
  317. package/lib/motion/shotcraft/upstream/demos/ui-entrance/cloner-depth-echo/ClonerDepthEcho.tsx +311 -0
  318. package/lib/motion/shotcraft/upstream/demos/ui-entrance/deck-deal-flyin/DeckDealFlyin.tsx +307 -0
  319. package/lib/motion/shotcraft/upstream/demos/ui-entrance/doc-park-left-pill-deal/DocParkLeftPillDeal.tsx +349 -0
  320. package/lib/motion/shotcraft/upstream/demos/ui-entrance/document-typewriter-reveal/DocumentTypewriterReveal.tsx +255 -0
  321. package/lib/motion/shotcraft/upstream/demos/ui-entrance/draw-svg-trace/DrawSvgTrace.tsx +244 -0
  322. package/lib/motion/shotcraft/upstream/demos/ui-entrance/element-body-moves/ContactShadowLift.tsx +217 -0
  323. package/lib/motion/shotcraft/upstream/demos/ui-entrance/integration-hub-map/IntegrationHubMap.tsx +652 -0
  324. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-reveal/ListReveal.tsx +153 -0
  325. package/lib/motion/shotcraft/upstream/demos/ui-entrance/list-stack-press/ListStackPress.tsx +238 -0
  326. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-forerun/NeonFrameForerun.tsx +324 -0
  327. package/lib/motion/shotcraft/upstream/demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx +354 -0
  328. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/PageWaterfallWall.tsx +101 -0
  329. package/lib/motion/shotcraft/upstream/demos/ui-entrance/page-waterfall-wall/VerticalTicker.tsx +129 -0
  330. package/lib/motion/shotcraft/upstream/demos/ui-entrance/product-card-progressive-assemble/ProductCardProgressiveAssemble.tsx +297 -0
  331. package/lib/motion/shotcraft/upstream/demos/ui-entrance/radial-wave/RadialWave.tsx +57 -0
  332. package/lib/motion/shotcraft/upstream/demos/ui-entrance/research-card-stack-scroll/ResearchCardStackScroll.tsx +155 -0
  333. package/lib/motion/shotcraft/upstream/demos/ui-entrance/row-embed/RowEmbed.tsx +175 -0
  334. package/lib/motion/shotcraft/upstream/demos/ui-entrance/value-stagger-gradient/ValueStaggerGradient.tsx +153 -0
  335. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx +146 -0
  336. package/lib/motion/shotcraft/upstream/demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx +226 -0
  337. package/lib/motion/shotcraft/upstream/gallery/api/library.json +1 -0
  338. package/lib/motion/shotcraft/upstream/manifest.json +2715 -0
  339. package/lib/motion/shotcraft/upstream/references/aesthetic-rules.md +193 -0
  340. package/lib/motion/shotcraft/upstream/references/final-review.md +121 -0
  341. package/lib/motion/shotcraft/upstream/references/guided-free-creation.md +199 -0
  342. package/lib/motion/shotcraft/upstream/references/jianying-export.md +183 -0
  343. package/lib/motion/shotcraft/upstream/references/music-beat-sync.md +257 -0
  344. package/lib/motion/shotcraft/upstream/references/pipeline.md +382 -0
  345. package/lib/motion/shotcraft/upstream/references/publish-upload-post.md +111 -0
  346. package/lib/motion/shotcraft/upstream/references/sequences/promo-energy-arc.md +50 -0
  347. package/lib/motion/shotcraft/upstream/references/sfx-sourcing.md +95 -0
  348. package/lib/motion/shotcraft/upstream/references/shots/camera/basic-3d-scene.md +41 -0
  349. package/lib/motion/shotcraft/upstream/references/shots/camera/crash-zoom-punch.md +52 -0
  350. package/lib/motion/shotcraft/upstream/references/shots/camera/cursor-flyover.md +39 -0
  351. package/lib/motion/shotcraft/upstream/references/shots/camera/depth-layer-moves.md +39 -0
  352. package/lib/motion/shotcraft/upstream/references/shots/camera/graze-face-tour.md +57 -0
  353. package/lib/motion/shotcraft/upstream/references/shots/camera/overhead-camera-moves.md +51 -0
  354. package/lib/motion/shotcraft/upstream/references/shots/camera/space-camera-moves.md +46 -0
  355. package/lib/motion/shotcraft/upstream/references/shots/camera/steep-tilt-glide.md +52 -0
  356. package/lib/motion/shotcraft/upstream/references/shots/camera/tension-camera-moves.md +43 -0
  357. package/lib/motion/shotcraft/upstream/references/shots/camera/terminal-3d.md +42 -0
  358. package/lib/motion/shotcraft/upstream/references/shots/data/before-after-slider-scrub.md +48 -0
  359. package/lib/motion/shotcraft/upstream/references/shots/data/chart-live-moves.md +52 -0
  360. package/lib/motion/shotcraft/upstream/references/shots/data/gauge-readout-moves.md +45 -0
  361. package/lib/motion/shotcraft/upstream/references/shots/data/scroll-brake-moves.md +46 -0
  362. package/lib/motion/shotcraft/upstream/references/shots/data/timeline-travel.md +44 -0
  363. package/lib/motion/shotcraft/upstream/references/shots/effects/assemble-then-type-flyin.md +63 -0
  364. package/lib/motion/shotcraft/upstream/references/shots/effects/aurora-bloom-bg-flip.md +54 -0
  365. package/lib/motion/shotcraft/upstream/references/shots/effects/brand-frame-snap.md +58 -0
  366. package/lib/motion/shotcraft/upstream/references/shots/effects/fui-hud-moves.md +35 -0
  367. package/lib/motion/shotcraft/upstream/references/shots/effects/glow-flyline-moves.md +53 -0
  368. package/lib/motion/shotcraft/upstream/references/shots/effects/icon-performance-moves.md +42 -0
  369. package/lib/motion/shotcraft/upstream/references/shots/effects/impact-feedback.md +50 -0
  370. package/lib/motion/shotcraft/upstream/references/shots/effects/light-play-moves.md +46 -0
  371. package/lib/motion/shotcraft/upstream/references/shots/effects/radial-ripple-phone-chips.md +56 -0
  372. package/lib/motion/shotcraft/upstream/references/shots/effects/riso-print-hits.md +49 -0
  373. package/lib/motion/shotcraft/upstream/references/shots/effects/scan-bracket-sweep.md +50 -0
  374. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-annotate-focus.md +53 -0
  375. package/lib/motion/shotcraft/upstream/references/shots/effects/scanline-assemble-flyin.md +53 -0
  376. package/lib/motion/shotcraft/upstream/references/shots/effects/slam-entrance-moves.md +55 -0
  377. package/lib/motion/shotcraft/upstream/references/shots/effects/spotlight-sweep-moves.md +52 -0
  378. package/lib/motion/shotcraft/upstream/references/shots/interaction/autolayout-gap-dial.md +60 -0
  379. package/lib/motion/shotcraft/upstream/references/shots/interaction/canvas-materialize-moves.md +57 -0
  380. package/lib/motion/shotcraft/upstream/references/shots/interaction/chip-grid-single-select-blackout.md +51 -0
  381. package/lib/motion/shotcraft/upstream/references/shots/interaction/collab-cursor-moves.md +41 -0
  382. package/lib/motion/shotcraft/upstream/references/shots/interaction/glass-pill-dictation-typing.md +53 -0
  383. package/lib/motion/shotcraft/upstream/references/shots/interaction/hashtag-to-pill-materialize.md +60 -0
  384. package/lib/motion/shotcraft/upstream/references/shots/interaction/picker-carousel-feature-cycle.md +55 -0
  385. package/lib/motion/shotcraft/upstream/references/shots/interaction/segmented-thumb-hero.md +57 -0
  386. package/lib/motion/shotcraft/upstream/references/shots/interaction/theme-switch-moves.md +47 -0
  387. package/lib/motion/shotcraft/upstream/references/shots/interaction/type-and-filter.md +37 -0
  388. package/lib/motion/shotcraft/upstream/references/shots/interaction/voice-waveform-live.md +54 -0
  389. package/lib/motion/shotcraft/upstream/references/shots/opening/brand-ink-open.md +36 -0
  390. package/lib/motion/shotcraft/upstream/references/shots/opening/crane-rise-reveal.md +56 -0
  391. package/lib/motion/shotcraft/upstream/references/shots/opening/dataviz-landscape-open.md +56 -0
  392. package/lib/motion/shotcraft/upstream/references/shots/opening/fracture.md +40 -0
  393. package/lib/motion/shotcraft/upstream/references/shots/opening/icon-field-colorize.md +60 -0
  394. package/lib/motion/shotcraft/upstream/references/shots/opening/letterspace-materialize.md +57 -0
  395. package/lib/motion/shotcraft/upstream/references/shots/opening/magician-card-flourish.md +73 -0
  396. package/lib/motion/shotcraft/upstream/references/shots/opening/orbit-ring-title-open.md +87 -0
  397. package/lib/motion/shotcraft/upstream/references/shots/opening/spotlight-hero-card.md +55 -0
  398. package/lib/motion/shotcraft/upstream/references/shots/opening/stroke-segment-build.md +50 -0
  399. package/lib/motion/shotcraft/upstream/references/shots/opening/text-as-mask.md +59 -0
  400. package/lib/motion/shotcraft/upstream/references/shots/outro/grain-dissolve.md +38 -0
  401. package/lib/motion/shotcraft/upstream/references/shots/outro/logo-shrink-wordmark-lockup.md +38 -0
  402. package/lib/motion/shotcraft/upstream/references/shots/outro/neon-triple-marquee.md +59 -0
  403. package/lib/motion/shotcraft/upstream/references/shots/outro/outro-group-photo-launch.md +39 -0
  404. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-strip-away-outro.md +61 -0
  405. package/lib/motion/shotcraft/upstream/references/shots/outro/ui-to-brand-morph.md +52 -0
  406. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-cut-moves.md +50 -0
  407. package/lib/motion/shotcraft/upstream/references/shots/rhythm/beat-step-list-theme-cycle.md +66 -0
  408. package/lib/motion/shotcraft/upstream/references/shots/rhythm/panel-grid-moves.md +43 -0
  409. package/lib/motion/shotcraft/upstream/references/shots/rhythm/quad-split-parallel-scenes.md +38 -0
  410. package/lib/motion/shotcraft/upstream/references/shots/rhythm/rhythm-interrupt-moves.md +38 -0
  411. package/lib/motion/shotcraft/upstream/references/shots/rhythm/sakuga-timing-shift.md +56 -0
  412. package/lib/motion/shotcraft/upstream/references/shots/rhythm/speed-ramp-freeze.md +44 -0
  413. package/lib/motion/shotcraft/upstream/references/shots/rhythm/trailer-grammar-moves.md +38 -0
  414. package/lib/motion/shotcraft/upstream/references/shots/transition/bottom-push-stack-wipe.md +63 -0
  415. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flip-reveal.md +57 -0
  416. package/lib/motion/shotcraft/upstream/references/shots/transition/card-flock-tumble.md +63 -0
  417. package/lib/motion/shotcraft/upstream/references/shots/transition/circle-match-iris.md +58 -0
  418. package/lib/motion/shotcraft/upstream/references/shots/transition/gradient-transition.md +45 -0
  419. package/lib/motion/shotcraft/upstream/references/shots/transition/line-carry-transition.md +55 -0
  420. package/lib/motion/shotcraft/upstream/references/shots/transition/mosaic-reframe.md +49 -0
  421. package/lib/motion/shotcraft/upstream/references/shots/transition/page-turn-transitions.md +42 -0
  422. package/lib/motion/shotcraft/upstream/references/shots/transition/print-texture-transitions.md +42 -0
  423. package/lib/motion/shotcraft/upstream/references/shots/transition/shot-transitions.md +45 -0
  424. package/lib/motion/shotcraft/upstream/references/shots/transition/tear-streak-transitions.md +48 -0
  425. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-hidden-cut.md +55 -0
  426. package/lib/motion/shotcraft/upstream/references/shots/transition/transition-travel.md +43 -0
  427. package/lib/motion/shotcraft/upstream/references/shots/transition/white-flash-logo-simplify-cut.md +41 -0
  428. package/lib/motion/shotcraft/upstream/references/shots/transition/wipe-transitions.md +42 -0
  429. package/lib/motion/shotcraft/upstream/references/shots/typography/blur-slide.md +59 -0
  430. package/lib/motion/shotcraft/upstream/references/shots/typography/brace-expand.md +55 -0
  431. package/lib/motion/shotcraft/upstream/references/shots/typography/countdown-arc-scatter.md +65 -0
  432. package/lib/motion/shotcraft/upstream/references/shots/typography/document-typewriter-reveal.md +42 -0
  433. package/lib/motion/shotcraft/upstream/references/shots/typography/flying-words.md +64 -0
  434. package/lib/motion/shotcraft/upstream/references/shots/typography/glitch-cycle.md +64 -0
  435. package/lib/motion/shotcraft/upstream/references/shots/typography/gradient-word-sweep.md +54 -0
  436. package/lib/motion/shotcraft/upstream/references/shots/typography/lead-word-zoom-assemble.md +91 -0
  437. package/lib/motion/shotcraft/upstream/references/shots/typography/marker-underline-title.md +51 -0
  438. package/lib/motion/shotcraft/upstream/references/shots/typography/outline-word-fill.md +66 -0
  439. package/lib/motion/shotcraft/upstream/references/shots/typography/paper-title-card.md +38 -0
  440. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-chip-slot-cycle-handled.md +66 -0
  441. package/lib/motion/shotcraft/upstream/references/shots/typography/pill-slot-cycle.md +58 -0
  442. package/lib/motion/shotcraft/upstream/references/shots/typography/text-column-converge.md +61 -0
  443. package/lib/motion/shotcraft/upstream/references/shots/typography/title-demote-to-label.md +64 -0
  444. package/lib/motion/shotcraft/upstream/references/shots/typography/type-assembly-moves.md +45 -0
  445. package/lib/motion/shotcraft/upstream/references/shots/typography/type-entrance-moves.md +41 -0
  446. package/lib/motion/shotcraft/upstream/references/shots/typography/type-rhythm-sync.md +51 -0
  447. package/lib/motion/shotcraft/upstream/references/shots/typography/typewriter-moves.md +42 -0
  448. package/lib/motion/shotcraft/upstream/references/shots/typography/vertical-word-roll-blur-cycle.md +63 -0
  449. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-filmstrip.md +57 -0
  450. package/lib/motion/shotcraft/upstream/references/shots/typography/word-relay-geometry.md +75 -0
  451. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/avatar-bracket-carousel.md +59 -0
  452. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/bezier-source-converge-merge.md +67 -0
  453. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/card-stack.md +60 -0
  454. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/carousel-3d.md +66 -0
  455. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/cloner-depth-echo.md +40 -0
  456. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/deck-deal-flyin.md +45 -0
  457. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/doc-park-left-pill-deal.md +62 -0
  458. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/draw-svg-trace.md +55 -0
  459. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/element-body-moves.md +37 -0
  460. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/integration-hub-map.md +55 -0
  461. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-reveal.md +50 -0
  462. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/list-stack-press.md +43 -0
  463. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-forerun.md +55 -0
  464. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/neon-frame-orbit-drop.md +51 -0
  465. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/page-waterfall-wall.md +51 -0
  466. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/product-card-progressive-assemble.md +56 -0
  467. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/radial-wave.md +49 -0
  468. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/research-card-stack-scroll.md +57 -0
  469. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/row-embed.md +36 -0
  470. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/value-stagger-gradient.md +64 -0
  471. package/lib/motion/shotcraft/upstream/references/shots/ui-entrance/wall-reveal-moves.md +47 -0
  472. package/lib/motion/shotcraft/upstream/references/sound-design.md +170 -0
  473. package/lib/motion/shotcraft/upstream/references/workbench.md +157 -0
  474. package/lib/motion/shotcraft/upstream/template/TEMPLATE.md +103 -0
  475. package/lib/motion/shotcraft/upstream/template/THEMES.md +44 -0
  476. package/lib/motion/shotcraft/upstream/template/package.json +20 -0
  477. package/lib/motion/shotcraft/upstream/template/remotion.config.ts +6 -0
  478. package/lib/motion/shotcraft/upstream/template/src/Root.tsx +17 -0
  479. package/lib/motion/shotcraft/upstream/template/src/aifl/Caption.tsx +65 -0
  480. package/lib/motion/shotcraft/upstream/template/src/aifl/DigitRoll.tsx +48 -0
  481. package/lib/motion/shotcraft/upstream/template/src/aifl/FlashCut.tsx +24 -0
  482. package/lib/motion/shotcraft/upstream/template/src/aifl/Main.tsx +192 -0
  483. package/lib/motion/shotcraft/upstream/template/src/aifl/PaperTitleCard.tsx +111 -0
  484. package/lib/motion/shotcraft/upstream/template/src/aifl/live/PageCam.tsx +150 -0
  485. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneDetail.tsx +158 -0
  486. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneFlyIn.tsx +468 -0
  487. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOpen.tsx +552 -0
  488. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneOutroLive.tsx +411 -0
  489. package/lib/motion/shotcraft/upstream/template/src/aifl/live/ScenePapers.tsx +200 -0
  490. package/lib/motion/shotcraft/upstream/template/src/aifl/live/SceneWbr.tsx +324 -0
  491. package/lib/motion/shotcraft/upstream/template/src/aifl/live-layout.json +389 -0
  492. package/lib/motion/shotcraft/upstream/template/src/index.ts +4 -0
  493. package/lib/motion/shotcraft/upstream/template/src/themes/ThemedFilm.tsx +7 -0
  494. package/lib/motion/shotcraft/upstream/template/src/themes/manifest.tsx +68 -0
  495. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.json +1 -0
  496. package/lib/motion/shotcraft/upstream/template/src/themes/palette-assets.ts +24 -0
  497. package/lib/motion/shotcraft/upstream/template/src/themes/palettes.json +74 -0
  498. package/lib/motion/shotcraft/upstream/template/src/themes/visual-theme.tsx +91 -0
  499. package/lib/motion/shotcraft/upstream/template/src/workbench.ts +213 -0
  500. package/lib/motion/shotcraft/upstream/template/tsconfig.json +15 -0
  501. package/lib/motion/spec.js +113 -0
  502. package/lib/motion/style-engine.js +153 -0
  503. package/lib/motion/style-frames.js +268 -0
  504. package/lib/motion/sync.js +214 -0
  505. package/lib/motion/theme.js +121 -0
  506. package/lib/motion/voice.js +417 -0
  507. package/lib/motion-agent-files.js +82 -0
  508. package/lib/motion-agent-protocol.js +66 -0
  509. package/lib/motion-agent-tools.js +196 -0
  510. package/lib/motion-agent.js +171 -0
  511. package/lib/motion-capture.js +92 -0
  512. package/lib/motion-core.js +238 -0
  513. package/lib/motion-film-review.js +71 -0
  514. package/lib/motion-library.js +210 -0
  515. package/lib/motion-planner.js +254 -0
  516. package/lib/motion-project.js +80 -0
  517. package/lib/motion-render-worker.js +145 -0
  518. package/lib/motion-session.js +32 -0
  519. package/lib/motion-source.js +49 -0
  520. package/lib/motion-tasks.js +452 -0
  521. package/lib/motion-vision.js +43 -0
  522. package/lib/mxfile.js +27 -7
  523. package/lib/routing/node-runtime.js +14 -0
  524. package/lib/routing/router.js +211 -0
  525. package/lib/routing/vendor/README.md +96 -0
  526. package/lib/routing/vendor/libavoid-routing.js +753 -0
  527. package/lib/routing/vendor/libavoid.min.js +59 -0
  528. package/lib/shotcard-core.js +39 -0
  529. package/lib/shotcard-reference.js +56 -0
  530. package/lib/style-kernel.js +37 -3
  531. package/lib/task-patches.js +26 -2
  532. package/lib/video-export.js +17 -2
  533. package/package.json +29 -4
@@ -0,0 +1,914 @@
1
+ // PIXEL QUEST 像素冒险 — 20-pixel demo.
2
+ // Indexed 320x180 frame (semantic slots -> per-stage LUT -> QUEST-32 master palette), WebGL2 CRT pass x6.
3
+ // Every frame is a pure function of the frame number f = round(t*30).
4
+ const W = 320, H = 180, FPS = 30;
5
+ const Q = new URLSearchParams(location.search);
6
+ const SHEET = Q.has('sheet');
7
+ const NOCRT = Q.has('nocrt') || SHEET;
8
+ // reel v5 (?v5=1): 「像素风」 title logo, round crystal ORB = the reel dot, window.linkAt(t), ?nolink=dot,sun,pool,title
9
+ const V5 = Q.get('v5') === '1';
10
+ const NOLINK = new Set(V5 ? (Q.get('nolink') || '').split(',').map(s => s.trim()).filter(Boolean) : []);
11
+ let linkPass = false; // linkAt() redraws the frame with every element present
12
+ const hidden = id => !linkPass && NOLINK.has(id);
13
+ const cues = await (await fetch('/demos/20-pixel/cues.json')).json();
14
+ const F = t => Math.round(t * FPS);
15
+
16
+ // ------------------------------------------------------------------ palette
17
+ const HEX = ['0d0b1a','1b1a33','2a2f5a','3d4a8a','4f6fc0','5b9bea','8cc8f5','c8ecff','ffffff','3b1f47','6b2d5c','b0415f',
18
+ 'e8665a','f59a4a','ffd166','fff3b0','0f2a2a','1d4a3a','2e7a45','4fae4a','93d65a','2b1a1f','5a3326','8a5236','c07a45',
19
+ 'e8b27c','7a1f2e','d23c3c','ffc9a0','4a5270','7d88a8','b8c2d9'];
20
+ const MP = HEX.map(h => [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)]);
21
+ const NSLOT = 128;
22
+ const SM = []; for (let i = 0; i < NSLOT; i++) SM[i] = [0, 0, 0, 0, 0, 0];
23
+ const M = i => 64 + i; // direct (sprite) colours
24
+ for (let i = 0; i < 32; i++) SM[64 + i] = [i, i, i, i, i, i];
25
+ const E = {}; let nS = 1;
26
+ const env = (n, a) => { E[n] = nS; SM[nS] = a; nS++; };
27
+ // DAY AFT GOLD SUNS DUSK NIGHT
28
+ env('s0', [ 4, 4, 3, 3, 2, 1]);
29
+ env('s1', [ 5, 5, 4, 10, 3, 2]);
30
+ env('s2', [ 6, 6, 25, 11, 10, 3]);
31
+ env('s3', [ 7, 15, 14, 12, 11, 9]);
32
+ env('s4', [ 7, 14, 13, 13, 12, 10]);
33
+ env('sun', [15, 15, 15, 14, 13, 13]);
34
+ env('sunG', [ 8, 15, 14, 13, 12, 11]);
35
+ env('clL', [ 8, 8, 15, 13, 11, 3]);
36
+ env('clD', [31, 25, 24, 11, 10, 2]);
37
+ env('mD', [ 5, 5, 29, 10, 9, 1]);
38
+ env('mL', [ 6, 25, 24, 11, 10, 2]);
39
+ env('mS', [ 8, 15, 15, 12, 11, 3]);
40
+ env('mist', [ 6, 15, 14, 12, 10, 2]);
41
+ env('rD', [29, 22, 22, 9, 9, 1]);
42
+ env('rM', [30, 23, 24, 10, 10, 2]);
43
+ env('rL', [31, 25, 28, 11, 11, 3]);
44
+ env('fD', [17, 17, 17, 9, 9, 0]);
45
+ env('fM', [18, 18, 18, 17, 16, 16]);
46
+ env('fL', [19, 20, 20, 24, 17, 17]);
47
+ env('hD', [17, 17, 17, 9, 16, 0]);
48
+ env('hM', [18, 18, 18, 17, 17, 16]);
49
+ env('hL', [19, 20, 20, 24, 18, 17]);
50
+ env('gD', [18, 18, 18, 17, 16, 16]);
51
+ env('gM', [19, 19, 19, 18, 17, 17]);
52
+ env('gL', [20, 20, 20, 24, 18, 3]);
53
+ env('dD', [22, 22, 22, 21, 21, 0]);
54
+ env('dM', [23, 23, 23, 22, 22, 21]);
55
+ env('dL', [24, 24, 24, 23, 23, 22]);
56
+ env('flw', [ 8, 8, 15, 14, 11, 3]);
57
+ env('rvB', [ 5, 5, 5, 10, 2, 1]); // river body (not cycled)
58
+ env('rvD', [ 4, 4, 4, 9, 1, 0]);
59
+ env('rvG', [ 8, 8, 15, 15, 11, 6]); // rare glint dash
60
+ const WAT = [[4, 4, 4, 10, 2, 1], [5, 5, 5, 11, 3, 2], [6, 6, 15, 10, 10, 2], [8, 8, 8, 12, 11, 30]];
61
+ WAT.forEach((a, k) => env('w' + k, a));
62
+ const WS = [E.w0, E.w1, E.w2, E.w3];
63
+ for (let k = 0; k < 4; k++) env('st' + k, [8, 8, 8, 8, 8, 8]);
64
+ const WFSEQ = [[0, 1, 1, 2, 3, 3, 2, 1], [1, 0, 0, 1, 1, 2, 3, 1]]; // per-group colour sequence (index into WAT ramp)
65
+ const WFG = WFSEQ.map((q, g) => q.map((_, k) => { env('wf' + g + k, [0, 0, 0, 0, 0, 0]); return nS - 1; }));
66
+ const STS = [E.st0, E.st1, E.st2, E.st3];
67
+ const STAR_SEQ = [8, 31, 29, 31];
68
+
69
+ function nearest(r, g, b) {
70
+ let bi = 0, bd = 1e18;
71
+ for (let i = 0; i < 32; i++) { const p = MP[i]; const d = 0.3 * (p[0] - r) ** 2 + 0.59 * (p[1] - g) ** 2 + 0.11 * (p[2] - b) ** 2; if (d < bd) { bd = d; bi = i; } }
72
+ return bi;
73
+ }
74
+ const mixIdx = (a, b, t) => { const A = MP[a], B = MP[b]; return nearest(A[0] + (B[0] - A[0]) * t, A[1] + (B[1] - A[1]) * t, A[2] + (B[2] - A[2]) * t); };
75
+ const FLASH = F(cues.chest_open);
76
+ const TRANS = [[F(cues.palette.afternoon), 0, 1], [F(cues.palette.golden), 1, 2], [F(cues.palette.sunset), 2, 3]];
77
+ const FNIGHT = F(cues.palette.night);
78
+ function stageOf(f) {
79
+ for (const [T, a, b] of TRANS) if (f >= T && f < T + 4) return [a, b, f < T + 2 ? 1 / 3 : 2 / 3];
80
+ const s = f < TRANS[0][0] ? 0 : f < TRANS[1][0] ? 1 : f < TRANS[2][0] ? 2 : f < FLASH ? 3 : 4;
81
+ return [s, s, 0];
82
+ }
83
+ function makeLUT(f, st = null, out = null) {
84
+ const lut = out || new Uint8Array(NSLOT);
85
+ const [a, b, t] = st === null ? stageOf(f) : st;
86
+ for (let s = 0; s < NSLOT; s++) lut[s] = t ? mixIdx(SM[s][a], SM[s][b], t) : SM[s][a];
87
+ const ph = Math.floor(f / 3); // colour cycling @10 fps
88
+ for (let k = 0; k < 4; k++) lut[WS[k]] = t ? mixIdx(SM[WS[(k - ph) & 3]][a], SM[WS[(k - ph) & 3]][b], t) : SM[WS[(k - ph) & 3]][a];
89
+ const sp = Math.floor(f / 4);
90
+ for (let k = 0; k < 4; k++) lut[STS[k]] = STAR_SEQ[(k + sp) & 3];
91
+ for (let g = 0; g < 2; g++) for (let k = 0; k < 8; k++) { const w = WAT[WFSEQ[g][(k - f) & 7]]; lut[WFG[g][k]] = t ? mixIdx(w[a], w[b], t) : w[a]; }
92
+ if (V5 && (f === FLASH || f === FLASH + 1)) { const k = f === FLASH ? 0.5 : 0.25; for (let s = 0; s < 64; s++) lut[s] = mixIdx(lut[s], 15, k); } // v5: palette lift, no white wash
93
+ else {
94
+ if (f === FLASH) { for (let s = 0; s < NSLOT; s++) lut[s] = s === M(0) ? 0 : 8; }
95
+ if (f === FLASH + 1) { for (let s = 0; s < 64; s++) lut[s] = 15; }
96
+ }
97
+ return lut;
98
+ }
99
+
100
+ // ------------------------------------------------------------------ buffer + helpers
101
+ const IB = new Uint8Array(W * H);
102
+ const px = (x, y, c) => { if (x >= 0 && x < W && y >= 0 && y < H) IB[y * W + x] = c; };
103
+ const rect = (x, y, w, h, c) => { for (let j = Math.max(0, y); j < Math.min(H, y + h); j++) for (let i = Math.max(0, x); i < Math.min(W, x + w); i++) IB[j * W + i] = c; };
104
+ const BAY = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
105
+ const bay = (x, y) => (BAY[(y & 3) * 4 + (x & 3)] + 0.5) / 16;
106
+ function hash(x, y) { let h = (x * 374761393 + y * 668265263) | 0; h = Math.imul(h ^ (h >>> 13), 1274126177); return ((h ^ (h >>> 16)) >>> 0) / 4294967296; }
107
+ function rng(a) { return () => { a |= 0; a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
108
+ const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
109
+
110
+ // ------------------------------------------------------------------ sprites
111
+ const LEG = { k: M(0), w: M(8), r: M(27), R: M(26), o: M(12), s: M(28), S: M(25), b: M(4), B: M(3), y: M(14), Y: M(13),
112
+ u: M(15), l: M(23), L: M(22), e: M(21), t: M(24), p: M(11), P: M(10), g: M(19), G: M(18), q: M(20), c: M(5), v: M(6),
113
+ z: M(7), C: M(3), n: M(2), m: M(9), i: M(29), I: M(30), h: M(31), x: M(1) };
114
+ function spr(rows) { const h = rows.length, w = Math.max(...rows.map(r => r.length)); const d = new Uint8Array(w * h);
115
+ rows.forEach((r, y) => { for (let x = 0; x < r.length; x++) { const c = LEG[r[x]]; if (c) d[y * w + x] = c; } }); return { w, h, d }; }
116
+ function outline(s, col = M(0)) {
117
+ const w = s.w + 2, h = s.h + 2, d = new Uint8Array(w * h);
118
+ for (let y = 0; y < s.h; y++) for (let x = 0; x < s.w; x++) if (s.d[y * s.w + x]) d[(y + 1) * w + x + 1] = s.d[y * s.w + x];
119
+ const o = d.slice();
120
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (!d[y * w + x]) {
121
+ if ((x > 0 && d[y * w + x - 1]) || (x < w - 1 && d[y * w + x + 1]) || (y > 0 && d[(y - 1) * w + x]) || (y < h - 1 && d[(y + 1) * w + x])) o[y * w + x] = col;
122
+ }
123
+ return { w, h, d: o };
124
+ }
125
+ function blit(s, x, y, flip = false, remap = null) {
126
+ for (let j = 0; j < s.h; j++) { const yy = y + j; if (yy < 0 || yy >= H) continue;
127
+ for (let i = 0; i < s.w; i++) { const c = s.d[j * s.w + (flip ? s.w - 1 - i : i)]; if (!c) continue; const xx = x + i; if (xx < 0 || xx >= W) continue;
128
+ IB[yy * W + xx] = remap ? (remap[c] ?? c) : c; } }
129
+ }
130
+ const O = rows => outline(spr(rows));
131
+
132
+ const HEAD = spr([
133
+ '...rrrrr...',
134
+ '.rrrorrrrr.',
135
+ 'rrrorrrrrrr',
136
+ 'rrrrrRRRRrr',
137
+ 'rRrrRssssRr',
138
+ 'rRrRsssskss',
139
+ 'RRrRsssskss',
140
+ '.RRRSssssS.',
141
+ '..RRRSSSS..',
142
+ ]);
143
+ const HEAD_BLINK = { ...HEAD, d: HEAD.d.map(c => (c === M(0) ? M(25) : c)) };
144
+
145
+ // pose: bob, back foot [dx,dy], front foot, back hand, front hand, tail mode
146
+ const RUN = [
147
+ { bob: 0, bf: [-3, 3], ff: [3, 4], bh: [3, 1], fh: [-2, 2] },
148
+ { bob: 1, bf: [-3, 2], ff: [1, 4], bh: [2, 2], fh: [-1, 3] },
149
+ { bob: 0, bf: [0, 2], ff: [-1, 4], bh: [0, 3], fh: [1, 3] },
150
+ { bob: 0, bf: [3, 4], ff: [-3, 3], bh: [-2, 2], fh: [3, 1] },
151
+ { bob: 1, bf: [1, 4], ff: [-3, 2], bh: [-1, 3], fh: [2, 2] },
152
+ { bob: 0, bf: [-1, 4], ff: [0, 2], bh: [1, 3], fh: [0, 3] },
153
+ ];
154
+ const POSE = {
155
+ up: { bob: 0, bf: [-2, 2], ff: [2, 3], bh: [-3, -1], fh: [3, -3], tail: 'up' },
156
+ down: { bob: 0, bf: [-2, 4], ff: [2, 3], bh: [-3, -2], fh: [2, -3], tail: 'down' },
157
+ land: { bob: 2, bf: [-2, 4], ff: [2, 4], bh: [-3, 2], fh: [3, 2], tail: 'hang' },
158
+ skid: { bob: 1, bf: [-1, 4], ff: [4, 4], bh: [-3, 0], fh: [-2, 1], tail: 'run1', lean: -1 },
159
+ idle0: { bob: 0, bf: [-1, 4], ff: [1, 4], bh: [0, 3], fh: [0, 3], tail: 'hang' },
160
+ idle1: { bob: 1, bf: [-1, 4], ff: [1, 4], bh: [0, 3], fh: [0, 3], tail: 'hang' },
161
+ cheer: { bob: 0, bf: [-2, 4], ff: [2, 4], bh: [-5, -5], fh: [5, -5], tail: 'hang' },
162
+ get0: { bob: 0, bf: [-2, 4], ff: [2, 4], be: [-5, -8], bh: [-2, -14], fe: [6, -8], fh: [2, -14], tail: 'hang', noHands: 1, lit: 1 }, // item-get: crystal overhead
163
+ get1: { bob: 1, bf: [-2, 4], ff: [2, 4], be: [-5, -7], bh: [-2, -14], fe: [6, -7], fh: [2, -14], tail: 'hang', noHands: 1, hdy: -1, lit: 1 }, // head tilts up 1 px
164
+ flinch0: { bob: 2, bf: [-2, 4], ff: [2, 3], bh: [-2, 1], fe: [4, -3], fh: [3, -6], tail: 'hang', hdx: -1 }, // brace: 2 px down, head away, arm up
165
+ flinch1: { bob: 2, bf: [-2, 4], ff: [2, 3], bh: [-2, 1], fe: [4, -4], fh: [3, -8], tail: 'hang', hdx: -1 },
166
+ };
167
+ const TAILS = {
168
+ run0: [0, 0, 1, 1, 1, 2], run1: [0, 1, 1, 2, 2, 3], run2: [0, 0, 0, 1, 2, 2],
169
+ up: [0, 1, 2, 3, 4, 5], down: [0, -1, -1, -2, -2, -3],
170
+ };
171
+ function line(x0, y0, x1, y1, fn) { let dx = Math.abs(x1 - x0), dy = -Math.abs(y1 - y0), sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1, e = dx + dy;
172
+ for (;;) { fn(x0, y0); if (x0 === x1 && y0 === y1) break; const e2 = 2 * e; if (e2 >= dy) { e += dy; x0 += sx; } if (e2 <= dx) { e += dx; y0 += sy; } } }
173
+ function heroSprite(p, tailMode, blink) {
174
+ const w = 26, h = 28, OX = 5, OY = 6, d = new Uint8Array(w * h);
175
+ const P = (x, y, c) => { x += OX; y += OY; if (x >= 0 && x < w && y >= 0 && y < h) d[y * w + x] = c; };
176
+ const b = p.bob, lean = p.lean || 0;
177
+ // hood tail
178
+ const tm = p.tail || tailMode;
179
+ if (tm === 'hang') { const pts = [[3, 5], [2, 6], [2, 7], [1, 8], [1, 9]]; pts.forEach(([x, y], i) => { P(x + lean, y + b, M(27)); if (i < 4) P(x - 1 + lean, y + b, M(26)); }); }
180
+ else { const dy = TAILS[tm]; dy.forEach((v, i) => { const x = 3 - i + lean, y = 4 + b + v; P(x, y, M(27)); if (i < 5) P(x, y + 1, M(26)); }); }
181
+ // back arm
182
+ const arm = (sx, rx, e, hnd, c, cs, hc, hs, side) => { const y0 = 12 + b;
183
+ const seg = (x0, y_0, x1, y1) => { const vert = Math.abs(y1 - y_0) >= Math.abs(x1 - x0); line(x0, y_0, x1, y1, (x, y) => { P(x, y, c); if (vert) P(x + side, y, cs); else P(x, y + 1, cs); }); };
184
+ const hx_ = rx + hnd[0], hy = y0 + hnd[1]; if (e) { seg(sx, y0, rx + e[0], y0 + e[1]); seg(rx + e[0], y0 + e[1], hx_, hy); } else seg(sx, y0, hx_, hy);
185
+ if (!p.noHands) { P(hx_, hy, hc); P(hx_ + side, hy, hc); P(hx_, hy + 1, hs); P(hx_ + side, hy + 1, hs); } };
186
+ const L = p.lit; // item-get: sleeves/shoulders/hair crest lit by the crystal overhead
187
+ arm(5, 6, p.be, p.bh, L ? M(5) : M(3), L ? M(4) : M(2), M(25), M(24), -1);
188
+ // back leg
189
+ const bfx = 6 + p.bf[0], bfy = 15 + p.bf[1];
190
+ line(6, 15 + b, bfx, bfy - 1, (x, y) => { P(x, y, M(22)); P(x + 1, y, M(22)); });
191
+ P(bfx, bfy, M(21)); P(bfx + 1, bfy, M(21)); P(bfx + 2, bfy, M(21)); P(bfx, bfy - 1, M(21)); P(bfx + 1, bfy - 1, M(21));
192
+ // torso
193
+ const T = ['bbbbbb', 'Bbbbbb', 'yyyuyy', 'BbbbbB'];
194
+ T.forEach((r, j) => { for (let i = 0; i < 6; i++) P(5 + i, 11 + b + j, LEG[r[i]]); });
195
+ if (L) for (let i = 1; i < 5; i++) P(5 + i, 11 + b, M(5));
196
+ // front leg
197
+ const ffx = 8 + p.ff[0], ffy = 15 + p.ff[1];
198
+ line(8, 15 + b, ffx, ffy - 1, (x, y) => { P(x, y, M(23)); P(x + 1, y, M(23)); });
199
+ P(ffx, ffy, M(22)); P(ffx + 1, ffy, M(22)); P(ffx + 2, ffy, M(22)); P(ffx, ffy - 1, M(22)); P(ffx + 1, ffy - 1, M(22));
200
+ // head
201
+ const hd = blink ? HEAD_BLINK : HEAD;
202
+ const hdx = p.hdx || 0, hdy = p.hdy || 0;
203
+ for (let j = 0; j < hd.h; j++) for (let i = 0; i < hd.w; i++) { const c = hd.d[j * hd.w + i]; if (c) P(3 + i + lean + hdx, 2 + b + j + hdy, c); }
204
+ if (L) for (let i = 0; i < hd.w; i++) { const c = hd.d[i]; if (c === M(27)) P(3 + i + lean + hdx, 2 + b + hdy, i === 5 ? M(28) : M(12)); }
205
+ // front arm
206
+ arm(10, 9, p.fe, p.fh, L ? M(6) : M(5), L ? M(5) : M(4), M(28), M(25), 1);
207
+ return outline({ w, h, d }); // sprite origin = box(0,0) at (-OX-1, -OY-1)
208
+ }
209
+ const HERO_OX = 6, HERO_OY = 7; // box (0,0) offset inside outlined sprite
210
+ const heroCache = new Map();
211
+ function hero(key, p, tail, blink) { const k = key + '|' + tail + '|' + (blink ? 1 : 0); if (!heroCache.has(k)) heroCache.set(k, heroSprite(p, tail, blink)); return heroCache.get(k); }
212
+
213
+ const COIN = [
214
+ O(['.yyyy.', 'uyyyyY', 'uyYYyY', 'uyYYyY', 'uyYYyY', 'uyYYyY', 'uyyyYY', '.YYYY.']),
215
+ O(['.yy.', 'uyyY', 'uyYY', 'uyYY', 'uyYY', 'uyYY', 'uyyY', '.YY.']),
216
+ O(['uY', 'uY', 'uY', 'uY', 'uY', 'uY', 'uY', 'uY']),
217
+ O(['.yy.', 'Yyyu', 'YYyu', 'YYyu', 'YYyu', 'YYyu', 'Yyyu', '.YY.']),
218
+ ];
219
+ const SLIME_A = O([
220
+ '....pppp....',
221
+ '..pppppppp..',
222
+ '.pwwpppppppP',
223
+ '.pwppkppkppP',
224
+ 'pppppkppkpPP',
225
+ 'ppppppppppPP',
226
+ 'PpppppppppPP',
227
+ '.PPPPPPPPPP.',
228
+ ]);
229
+ const SLIME_B = O([
230
+ '...pppppp...',
231
+ '.pwwppppppP.',
232
+ 'pwpppkppkppP',
233
+ 'pppppkppkpPP',
234
+ 'PpppppppppPP',
235
+ '.PPPPPPPPPP.',
236
+ ]);
237
+ const SLIME_F = O(['..pppppppp..', 'ppkkpppkkppP', '.PPPPPPPPPP.']);
238
+ const CH_LID = ['.hIuuttttttttttIh.', 'ihuttttttttttttlhi', 'iItttttttttttllLIi', 'iILlllllllllllLLIi', 'iiiiiiiuyyYiiiiiii'];
239
+ const CH_BODY = ['iItttttyykYtttttIi', 'ihlllllYYkYlllllhi', 'iIllllllllllllllIi', 'iILLLLLLLLLLLLLLIi', 'iItlllllllllllllIi',
240
+ 'ihllllllllllllllhi', 'iIlllllllllllllLIi', 'iILLLLLLLLLLLLLLIi', 'iieeeeeeeeeeeeeeii'];
241
+ const CHEST = O([...CH_LID, ...CH_BODY]);
242
+ const CHEST_TALK = O([...CH_LID, 'ekkkkwkkkkkkwkkkke', ...CH_BODY]);
243
+ const GLOW = ['.uwwwwwwwwwwwwwwu.'];
244
+ const CHEST_CRACK = O([...CH_LID, ...GLOW, ...CH_BODY]);
245
+ const CHEST_CRACK2 = O([...CH_LID, '.uuwwwwwwwwwwwwuu.', ...GLOW, ...CH_BODY]);
246
+ const CHEST_HALF = O(['..iIttttttttttIi..', '.iILLLLLLLLLLLLIi.', '.iieeeeeeeeeeeeii.', '..uuuwwwwwwwwuuu..', ...GLOW, ...CH_BODY]);
247
+ // open: lid swung back (inner face, 2 px narrower, one ramp darker) + back rim + glowing interior + front body
248
+ const CHEST_OPEN = O(['...iILLLLLLLLIi...', '..iIeLLLLLLLLeIi..', '..ihLLLLLLLLLLhi..', '.iIeeeeeeeeeeeeIi.',
249
+ 'iIuwwwwwwwwwwwwuIi', 'iIyuywyyuyywyuyyIi', 'iIYyYYyYYyYYyYyYIi', ...CH_BODY]);
250
+ const TAIL = outline(outline(outline(spr(['nnnnnnn', '.nnnnnn', '..nnnnn', '...nnnn', '....nnn', '.....nn', '......n']), M(3)), M(8)), M(0));
251
+ const BUBBLE_Q = O(['..wwwww..', '.wwBBBww.', 'wwBwwwBww', 'wwwwwwBww', 'wwwwBBwww', 'wwwwBwwww', 'wwwwwwwww', '.wwwBwww.', '..wwww...', '..ww.....', '..w......']);
252
+ const GEM = O(['...w...', '..zwv..', '.zzvvc.', 'zzzvvcC', '.zvvcC.', '..vcC..', '...C...']);
253
+ // v5: the crystal is a round ORB (9 px disc + 1 px outline = 11 logical px), lit from the top left
254
+ const ORB = O(['..zzvvc..', '.zwwzvvc.', 'zwwwzvvcc', 'zwwzvvvcc', 'zzvvvvccb', 'vvvvvccbb', 'vvvvccbbC', '.vvccbCC.', '..cbbCC..']);
255
+ const SPARK = [
256
+ spr(['.u.', 'uwu', '.u.']),
257
+ spr(['..w..', '..u..', 'wuwuw', '..u..', '..w..']),
258
+ spr(['...w...', '...u...', '...u...', 'wuuwuuw', '...u...', '...u...', '...w...']),
259
+ ];
260
+ const PUFF = [spr(['.ww.', 'wwww', 'wwww', '.ww.']), spr(['..ww..', '.wwwww', 'ww..ww', '.wwww.']), spr(['.w..w.', 'w....w', '.w..w.'])];
261
+ const BUBBLE = O(['..wwwww..', '.wwwrwww.', 'wwwwrwwww', 'wwwwrwwww', 'wwwwrwwww', 'wwwwwwwww', 'wwwwrwwww', '.wwwwwww.', '..wwww...', '..ww.....', '..w......']);
262
+ const BUBBLE_S = O(['.www.', 'wwrww', 'wwrww', 'wwwww', 'wwrww', '.www.', '.w...']);
263
+ const DIG = { 0: ['111', '101', '101', '101', '111'], 1: ['010', '110', '010', '010', '111'], 5: ['111', '100', '111', '001', '111'] };
264
+ function tinyNum(str, x, y, c, sh) { let cx = x; for (const ch of str) { const g = DIG[ch]; for (let j = 0; j < 5; j++) for (let i = 0; i < 3; i++) if (g[j][i] === '1') { if (sh) px(cx + i + 1, y + j + 1, sh); } for (let j = 0; j < 5; j++) for (let i = 0; i < 3; i++) if (g[j][i] === '1') px(cx + i, y + j, c); cx += 4; } }
265
+
266
+ // ------------------------------------------------------------------ fonts -> 1-bit masks
267
+ const gcache = new Map();
268
+ function glyph(ch, fam, size) {
269
+ const key = fam + size + ch; if (gcache.has(key)) return gcache.get(key);
270
+ const c = document.createElement('canvas'); c.width = size * 2; c.height = size * 2; const x = c.getContext('2d');
271
+ x.font = `${size}px "${fam}"`; x.fillStyle = '#fff'; x.textBaseline = 'alphabetic'; x.fillText(ch, 0, size);
272
+ const id = x.getImageData(0, 0, c.width, c.height).data; const m = new Uint8Array(c.width * c.height);
273
+ for (let i = 0; i < m.length; i++) m[i] = id[i * 4 + 3] > 110 ? 1 : 0;
274
+ const g = { w: c.width, h: c.height, m, adv: Math.round(x.measureText(ch).width), size }; gcache.set(key, g); return g;
275
+ }
276
+ function text(str, x, base, fam, size, col, sh, spacing = 0, n = Infinity) {
277
+ let cx = x, i = 0;
278
+ for (const ch of str) { if (i++ >= n) break; const g = glyph(ch, fam, size);
279
+ for (let pass = 0; pass < 2; pass++) { const c = pass ? col : sh; if (!c) continue; const ox = pass ? 0 : sh === col ? 0 : 1, oy = pass ? 0 : 1;
280
+ for (let j = 0; j < g.h; j++) for (let k = 0; k < g.w; k++) if (g.m[j * g.w + k]) px(cx + k + ox, base - size + j + oy, c); }
281
+ cx += g.adv + spacing; }
282
+ return cx - x;
283
+ }
284
+ const textW = (str, fam, size, spacing = 0) => { let w = 0; for (const ch of str) w += glyph(ch, fam, size).adv + spacing; return w - spacing; };
285
+
286
+ // ------------------------------------------------------------------ logo (custom 6x7-cell block glyphs, 4-px cells)
287
+ const LG = {
288
+ P: ['11111.', '11..11', '11..11', '11111.', '11....', '11....', '11....'],
289
+ I: ['111111', '..11..', '..11..', '..11..', '..11..', '..11..', '111111'],
290
+ X: ['11..11', '11..11', '.1111.', '..11..', '.1111.', '11..11', '11..11'],
291
+ E: ['111111', '11....', '11....', '11111.', '11....', '11....', '111111'],
292
+ L: ['11....', '11....', '11....', '11....', '11....', '11....', '111111'],
293
+ Q: ['.1111.', '11..11', '11..11', '11..11', '11.111', '.1111.', '....11'],
294
+ U: ['11..11', '11..11', '11..11', '11..11', '11..11', '11..11', '.1111.'],
295
+ S: ['.11111', '11....', '11....', '.1111.', '....11', '....11', '11111.'],
296
+ T: ['111111', '..11..', '..11..', '..11..', '..11..', '..11..', '..11..'],
297
+ };
298
+ const CELL = 4, LW_ = 5 * 6 * CELL + 4 * CELL, LH_ = 7 * CELL, EXT = 4, GAP = 5;
299
+ function buildLogo() {
300
+ const w = LW_ + 2, h = (LH_ + EXT) * 2 + GAP + 2, mask = new Uint8Array(w * h), fill = new Uint8Array(w * h), row = new Int16Array(w * h).fill(-1);
301
+ ['PIXEL', 'QUEST'].forEach((word, li) => {
302
+ const oy = 1 + li * (LH_ + EXT + GAP);
303
+ [...word].forEach((ch, ci) => { const g = LG[ch]; const ox = 1 + ci * 7 * CELL;
304
+ for (let j = 0; j < 7; j++) for (let i = 0; i < 6; i++) if (g[j][i] === '1')
305
+ for (let yy = 0; yy < CELL; yy++) for (let xx = 0; xx < CELL; xx++) { const p = (oy + j * CELL + yy) * w + ox + i * CELL + xx; mask[p] = 1; row[p] = j * CELL + yy; } });
306
+ });
307
+ const out = new Uint8Array(w * h);
308
+ const at = (x, y) => x >= 0 && y >= 0 && x < w && y < h && mask[y * w + x];
309
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) { if (!mask[y * w + x]) continue; const r = row[y * w + x]; let c;
310
+ if (r < 2) c = M(15); else if (r < 10) c = M(14); else if (r < 14) c = ((x + y) & 1) ? M(14) : M(13); else if (r < 21) c = M(13); else if (r < 24) c = ((x + y) & 1) ? M(13) : M(12); else c = M(12);
311
+ if (!at(x, y - 1)) c = M(15); else if (!at(x - 1, y) && r < 16) c = M(15);
312
+ out[y * w + x] = c; fill[y * w + x] = 1; }
313
+ for (let y = h - 1; y >= 0; y--) for (let x = 0; x < w; x++) if (mask[y * w + x]) for (let d = 1; d <= EXT; d++) { const yy = y + d; if (yy < h && !mask[yy * w + x] && !out[yy * w + x]) out[yy * w + x] = d < EXT ? M(26) : M(9); }
314
+ const o2 = out.slice();
315
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (!out[y * w + x]) { const n = (xx, yy) => xx >= 0 && yy >= 0 && xx < w && yy < h && out[yy * w + xx];
316
+ if (n(x - 1, y) || n(x + 1, y) || n(x, y - 1) || n(x, y + 1)) o2[y * w + x] = M(0); }
317
+ return { w, h, d: o2, fill };
318
+ }
319
+ const LOGO0 = buildLogo(), LOGO = LOGO0;
320
+ // v5 title 「像素风」: Fusion Pixel 12 glyph bitmaps, 1 font px = C5×C5 logical px, same fill ramp / top bevel / drop / outline
321
+ const C5 = 4, EXT5 = 2, SP5 = 1, Y5 = 18, RIB5 = 72;
322
+ function buildLogo5(str) {
323
+ const gs = [...str].map(ch => glyph(ch, 'Fusion Pixel 12', 12)), cols = []; let t = 99, b = -1;
324
+ for (const g of gs) { let l = 99, r = -1; for (let y = 0; y < g.h; y++) for (let x = 0; x < g.w; x++) if (g.m[y * g.w + x]) { t = Math.min(t, y); b = Math.max(b, y); l = Math.min(l, x); r = Math.max(r, x); } cols.push([l, r]); }
325
+ const LH = (b - t + 1) * C5, gw = cols.map(([l, r]) => (r - l + 1) * C5);
326
+ const w = gw.reduce((a, c) => a + c, 0) + (gs.length - 1) * (1 + SP5) * C5 + 2, h = LH + EXT5 + 2;
327
+ const mask = new Uint8Array(w * h), fill = new Uint8Array(w * h), row = new Int16Array(w * h).fill(-1);
328
+ let ox = 1;
329
+ gs.forEach((g, gi) => { const [l, r] = cols[gi];
330
+ for (let j = t; j <= b; j++) for (let i = l; i <= r; i++) if (g.m[j * g.w + i])
331
+ for (let yy = 0; yy < C5; yy++) for (let xx = 0; xx < C5; xx++) { const p = (1 + (j - t) * C5 + yy) * w + ox + (i - l) * C5 + xx; mask[p] = 1; row[p] = (j - t) * C5 + yy; }
332
+ ox += gw[gi] + (1 + SP5) * C5; });
333
+ const out = new Uint8Array(w * h), at = (x, y) => x >= 0 && y >= 0 && x < w && y < h && mask[y * w + x], sc = 28 / LH;
334
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) { if (!mask[y * w + x]) continue; const r = row[y * w + x] * sc; let c;
335
+ if (r < 2) c = M(15); else if (r < 10) c = M(14); else if (r < 14) c = ((x + y) & 1) ? M(14) : M(13); else if (r < 21) c = M(13); else if (r < 24) c = ((x + y) & 1) ? M(13) : M(12); else c = M(12);
336
+ if (!at(x, y - 1)) c = M(15); else if (!at(x - 1, y) && r < 16) c = M(15);
337
+ out[y * w + x] = c; fill[y * w + x] = 1; }
338
+ for (let y = h - 1; y >= 0; y--) for (let x = 0; x < w; x++) if (mask[y * w + x]) for (let d = 1; d <= EXT5; d++) { const yy = y + d; if (yy < h && !mask[yy * w + x] && !out[yy * w + x]) out[yy * w + x] = d < EXT5 ? M(26) : M(9); }
339
+ const o2 = out.slice();
340
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (!out[y * w + x]) { const n = (xx, yy) => xx >= 0 && yy >= 0 && xx < w && yy < h && out[yy * w + xx];
341
+ if (n(x - 1, y) || n(x + 1, y) || n(x, y - 1) || n(x, y + 1)) o2[y * w + x] = M(0); }
342
+ const cn = []; for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) if (mask[y * w + x] && !at(x, y - 1) && !at(x - 1, y)) cn.push([x, y]); // top-left stroke corners → glint spots
343
+ cn.sort((a, b2) => a[0] - b2[0]); const glint = [0.04, 0.78, 0.36, 0.96, 0.58].map(u => cn[Math.min(cn.length - 1, Math.floor(u * cn.length))]);
344
+ return { w, h, d: o2, fill, glint, gh: LH };
345
+ }
346
+ const LOGO5 = V5 ? buildLogo5('像素风') : null;
347
+
348
+ // ------------------------------------------------------------------ background layers (precomputed slot bitmaps)
349
+ function layer(w, fn) { const d = new Uint8Array(w * H); for (let y = 0; y < H; y++) for (let x = 0; x < w; x++) d[y * w + x] = fn(x, y) || 0; return { w, d }; }
350
+ function blitLayer(L, off, y0 = 0, y1 = H) { for (let y = y0; y < y1; y++) { const r = y * L.w + off, o = y * W; for (let x = 0; x < W; x++) { const s = L.d[r + x]; if (s) IB[o + x] = s; } } }
351
+ const CAM_END = 260, X0 = 150, GROUND = 150;
352
+ // far mountains (x0.125)
353
+ const MT = (() => {
354
+ const w = 380, R = rng(7), top = new Float32Array(w), peaks = [];
355
+ let x = -10, hi = true; const pts = [];
356
+ while (x < w + 40) { const y = hi ? 64 + R() * 18 : 94 + R() * 10; pts.push([x, y]); if (hi) peaks.push([x, y]); x += 16 + R() * 22; hi = !hi; }
357
+ for (let i = 0; i < pts.length - 1; i++) { const [ax, ay] = pts[i], [bx, by] = pts[i + 1];
358
+ for (let xx = Math.ceil(ax); xx < bx; xx++) if (xx >= 0 && xx < w) top[xx] = Math.round(ay + (by - ay) * (xx - ax) / (bx - ax) + (hash(xx, 3) < 0.25 ? 1 : 0)); }
359
+ return layer(w, (x, y) => {
360
+ const t = top[x]; if (y < t) return 0;
361
+ let pk = peaks[0]; for (const p of peaks) if (Math.abs(p[0] - x) < Math.abs(pk[0] - x)) pk = p;
362
+ const lit = x >= pk[0] - Math.floor((y - pk[1]) * 0.35);
363
+ if (y > 112) { const m = (y - 112) / 14; if (m > bay(x, y)) return E.mist; }
364
+ if (pk[1] < 76 && y < pk[1] + 6 - Math.abs(x - pk[0]) * 0.3 + (hash(x, 9) < 0.3 ? 1 : 0)) return E.mS;
365
+ return lit ? E.mL : E.mD;
366
+ });
367
+ })();
368
+ // mid layer: mesas, pines, waterfall (x0.3)
369
+ const WF_X = 137, WF_W = 9;
370
+ const MID = (() => {
371
+ const w = 420, R = rng(11);
372
+ const ridge = x => Math.round(111 + 4 * Math.sin(x * 0.043) + 3 * Math.sin(x * 0.11 + 1.3));
373
+ const CL0 = 118, CL1 = 166, CLT = 96; // rock cliff carrying the waterfall
374
+ // canopy silhouette: a row of pine tips of random width/height
375
+ const canopy = new Int16Array(w), tipc = new Int16Array(w), tiph = new Int8Array(w);
376
+ for (let x = 0; x < w;) { const tw = 4 + Math.floor(R() * 4), th = 3 + Math.floor(R() * 5); for (let i = 0; i < tw && x + i < w; i++) { const u = Math.abs(i - (tw - 1) / 2) / ((tw + 1) / 2); canopy[x + i] = Math.round(th * (1 - u)); tipc[x + i] = x + (tw >> 1); tiph[x + i] = th; } x += tw; }
377
+ const jit = x => Math.floor(hash(x, 21) * 4);
378
+ const bush = new Int16Array(w).fill(999), bushC = new Int16Array(w), R2 = rng(12); // 2nd staggered bush-silhouette row
379
+ for (let bx = -3, k = 0; bx < w + 6; k++) { const r = 3 + Math.floor(R2() * 3), cy = 127 + ((k & 1) ? 3 : 0) + Math.floor(R2() * 2);
380
+ for (let i = -r; i <= r; i++) { const xx = bx + i; if (xx < 0 || xx >= w) continue; const t = Math.round(cy - Math.sqrt(Math.max(0, (r + 0.4) ** 2 - i * i)) * 0.85);
381
+ if (t < bush[xx]) { bush[xx] = t; bushC[xx] = bx; } } bx += r + 1 + Math.floor(R2() * 3); }
382
+ const L = layer(w, (x, y) => {
383
+ if (x >= CL0 && x < CL1) { const t = CLT + (x < CL0 + 4 ? CL0 + 4 - x : 0) + (x >= CL1 - 5 ? x - (CL1 - 5) : 0);
384
+ if (y >= t) {
385
+ const FY = 124; // foam band where the fall hits the plunge pool
386
+ if (y >= FY && y < FY + 3 && x >= WF_X - 2 && x < WF_X + WF_W + 2) { const k = y - FY; return (k === 0 || (k === 1 && ((x + y) & 1)) || (k === 2 && bay(x, y) < 0.25)) ? E.w3 : WS[(x + y) & 3]; }
387
+ if (y >= FY + 3 && y < FY + 6 && x >= WF_X - 3 && x < WF_X + WF_W + 3) return y === FY + 5 ? E.w0 : WS[(Math.floor((x + (y & 1) * 2) / 3)) & 3];
388
+ if (x >= WF_X && x < WF_X + WF_W && y >= CLT + 1 && y < FY) { if (y < CLT + 3) return E.w3; const g = (x === WF_X || x === WF_X + WF_W - 1) ? 1 : hash(x, 41) < 0.5 ? 0 : 1; return WFG[g][(y + Math.floor(hash(x, 43) * 8)) & 7]; }
389
+ if (y < t + 2) return y === t ? E.fL : E.fM;
390
+ if (x >= CL1 - 7 || (x > WF_X + WF_W && x < WF_X + WF_W + 2)) return E.rL; if (x < CL0 + 6 || (x >= WF_X - 2 && x < WF_X)) return E.rD;
391
+ if ((y - t) % 6 === 3 && hash(x >> 2, y) < 0.65) return E.rD;
392
+ if (y > 126 && (y - 126) / 18 > bay(x, y)) return E.rD;
393
+ return hash(x, y) < 0.07 ? E.rL : E.rM; } }
394
+ const top = ridge(x) - canopy[x]; if (y < top) return 0;
395
+ const lit = x > tipc[x] && y < ridge(x) + 2;
396
+ if (y < ridge(x) + 1) return lit ? E.fL : x < tipc[x] ? E.fD : E.fM;
397
+ if (y >= bush[x]) { const lit = x > bushC[x]; if (y === bush[x]) return lit && hash(x, 51) < 0.7 ? E.fL : E.fM;
398
+ if (y === bush[x] + 1 && lit && hash(x, 52) < 0.35) return E.fL; return (y - bush[x] - 4) / 6 > bay(x, y) ? E.fD : E.fM; }
399
+ if (y > 120 && (y - 120) / 12 > bay(x, y)) return E.fD;
400
+ const cg = Math.floor((x + 60) / 6), dy = y - ridge(x) - 1 + Math.floor(hash(cg, 31) * 5), row = Math.floor(dy / 5), r = dy % 5, q = (x + 60) % 6;
401
+ if (r === 0 && q === 3 && hash(cg, row) < 0.7) return E.fL; if (r === 1 && (q === 2 || q === 4)) return E.fM; if (r === 4 && q !== 3) return E.fD;
402
+ return E.fM;
403
+ });
404
+ // taller lone pines on the ridge and on the cliff top
405
+ for (let x = 6; x < w; x += 12 + Math.floor(R() * 22)) { if (x > WF_X - 4 && x < WF_X + WF_W + 3) continue; const onCliff = x >= CL0 + 4 && x < CL1 - 5; const base = onCliff ? CLT + 1 : ridge(x) - 1, h = (onCliff ? 12 : 9) + Math.floor(R() * 10);
406
+ const tier = Math.ceil(h / 3);
407
+ for (let r = 0; r < h; r++) { const k = Math.floor(r / tier), within = r % tier, half = Math.floor((within + 1) * (0.7 + k * 0.35));
408
+ const yy = base - h + r; for (let dx = -half; dx <= half; dx++) { const xx = x + dx; if (xx < 0 || xx >= w || yy < 0) continue; L.d[yy * w + xx] = dx === half && dx > 0 ? E.fL : dx >= 0 ? E.fM : E.fD; } }
409
+ if (base >= 0) L.d[base * w + x] = E.rD;
410
+ }
411
+ return L;
412
+ })();
413
+ // near hills (x0.55)
414
+ const HILLS = (() => {
415
+ const w = 480, R = rng(23), bumps = [];
416
+ for (let x = -10; x < w + 20; x += 20 + R() * 20) bumps.push([x, 12 + R() * 11]);
417
+ const L = layer(w, (x, y) => {
418
+ let best = null;
419
+ for (const [cx, r] of bumps) { const cy = GROUND + r * 0.3; const d = Math.hypot(x - cx, (y - cy) * 1.15); if (d <= r && (!best || d - r < best.e)) best = { cx, cy, r, d, e: d - r }; }
420
+ if (!best) return 0;
421
+ const rim = best.r - best.d;
422
+ if (rim < 1.5 && x > best.cx - best.r * 0.3) return E.hL;
423
+ if (rim < 2.5 && (x + y) % 3 === 0 && x > best.cx) return E.hL;
424
+ if (y > 138 && (y - 138) / 14 > bay(x, y)) return E.hD;
425
+ if (hash(x >> 1, y >> 1) < 0.08) return E.hL;
426
+ return E.hM;
427
+ });
428
+ return L;
429
+ })();
430
+
431
+ // ------------------------------------------------------------------ world state (closed-form)
432
+ const SKID = [0, 2, 3, 4, 5, 6, 7, 7, 8, 8, 8, 8, 8];
433
+ const fStop = F(cues.stop), fSkid = F(cues.skid);
434
+ const hx = f => f <= fSkid ? X0 + 2 * f : X0 + 2 * fSkid + SKID[Math.min(f - fSkid, 12)];
435
+ const cam = f => { const r = hx(f) - 96; return r < CAM_END - 6 ? r : Math.min(CAM_END, CAM_END - 6 + Math.max(0, f - (CAM_END - 6 - (hx(0) - 96)) / 2)); };
436
+ const J1 = F(cues.jumps[0]), J2 = F(cues.jumps[1]), STOMP = F(cues.stomp), L2 = F(cues.lands[1]);
437
+ const BR0 = 316, BR1 = 380;
438
+ const bridgeSag = wx => (wx >= BR0 && wx < BR1) ? Math.round(2 * Math.sin(Math.PI * (wx - BR0) / (BR1 - BR0))) : 0;
439
+ function heroH(f) {
440
+ if (f >= J1 && f < J1 + 18) { const s = (f - J1) / 18; return Math.round(112 * s * (1 - s)); }
441
+ if (f >= J2 && f <= STOMP) { const s = (f - J2) / 13.21; return Math.round(96 * s * (1 - s)); }
442
+ if (f > STOMP && f < L2) { const u = (f - STOMP) / (L2 - STOMP); return Math.round(8 * (1 - u) + 72 * u * (1 - u)); }
443
+ return 0;
444
+ }
445
+ function heroPose(f) {
446
+ const h = heroH(f), hp = heroH(f - 1);
447
+ if (f >= FGET) { const k = f < FGET + 24 ? Math.floor((f - FGET) / 6) : Math.floor((f - FGET) / 12); return (k & 1) ? ['get1', POSE.get1, 'hang'] : ['get0', POSE.get0, 'hang']; }
448
+ if (f >= FCHEER) return ['cheer', POSE.cheer, 'hang'];
449
+ if (f >= FLASH) return ['land', POSE.land, 'hang'];
450
+ if (f >= FLINCH) return f < FLINCH + 1 ? ['flinch0', POSE.flinch0, 'hang'] : ['flinch1', POSE.flinch1, 'hang'];
451
+ if (f >= fStop) return ((Math.floor(f / 12) & 1) ? ['idle1', POSE.idle1, 'hang'] : ['idle0', POSE.idle0, 'hang']);
452
+ if (f >= fSkid) return ['skid', POSE.skid, 'run1'];
453
+ if (h > 0) return h >= hp && f !== STOMP ? ['up', POSE.up, 'up'] : ['down', POSE.down, 'down'];
454
+ if ((f >= J1 - 2 && f < J1) || (f >= J2 - 2 && f < J2)) return ['land', POSE.land, 'run1']; // anticipation crouch
455
+ if ((f >= J1 + 18 && f < J1 + 20) || (f >= L2 && f < L2 + 2)) return ['land', POSE.land, 'hang'];
456
+ const ph = Math.floor(f / 3) % 6; return ['run' + ph, RUN[ph], 'run' + (Math.floor(f / 3) % 3)];
457
+ }
458
+ const COINS = cues.coins.map(t => { const f = F(t), x = hx(f) + 8, y = GROUND - heroH(f) - 10 + bridgeSag(x); return { f, x, y }; });
459
+ const SLIME_X = hx(STOMP) + 8;
460
+ const CHEST_X = 422, FGET = F(cues.item_get), FCHEER = F(cues.cheer), FLINCH = F(cues.flinch);
461
+ const LH1 = F(cues.lid_hops[0]), LH2 = F(cues.lid_hops[1]), fShk = F(cues.chest_shake[0]);
462
+ const fType = F(cues.type[0]), fBoxO = F(cues.box_open), fBoxC = F(cues.box_close), fLogo = F(cues.logo), fShine = F(cues.shine), fSub = F(cues.subtitle[0]), fPress = F(cues.press_start);
463
+ function scoreAt(f) { let s = 0, c = 0; for (const k of COINS) if (f >= k.f) { s += 100; c++; } if (f >= STOMP) s += 500; return [s, c]; }
464
+ // chest fountain (precomputed ballistic, integer positions)
465
+ const FOUNT = (() => { const R = rng(99), a = [];
466
+ const VX = [-3.2, 2.8, -2.4, 2.0, -1.6, 1.6, -2.0, 2.4, -2.8, 3.2];
467
+ VX.forEach((vx, i) => a.push({ kind: 'coin', vx: vx + (R() - 0.5) * 0.2, vy: -6 - R() * 3, g: 0.5, ph: i, t0: 1 + i, sx: vx < 0 ? -7 : 7 }));
468
+ for (let i = 0; i < 14; i++) a.push({ kind: 'spark', vx: (R() - 0.5) * 3.2, vy: -2.5 - R() * 3, g: 0.1, life: 12 + Math.floor(R() * 10), d: Math.floor(R() * 4) });
469
+ return a; })();
470
+ function fountPos(p, tau, x0, y0) { let x = x0 + p.vx * tau, y = y0 + p.vy * tau + 0.5 * p.g * tau * tau, life = 1;
471
+ if (p.kind === 'coin') { const yg = GROUND - 6, tl = (-p.vy + Math.sqrt(p.vy * p.vy + 2 * p.g * (yg - y0))) / p.g;
472
+ if (tau > tl) { const t2 = tau - tl, v2 = -(p.vy + p.g * tl) * 0.4, t3 = 2 * v2 / p.g; x = x0 + p.vx * (tl + Math.min(t2, t3) * 0.7);
473
+ y = t2 < t3 ? yg - (v2 * t2 - 0.5 * p.g * t2 * t2) : yg; life = t2 < t3 ? 1 : t2 - t3 < 10 ? 2 : 0; } }
474
+ return [Math.round(x), Math.round(y), life]; }
475
+ const STARS = (() => { const R = rng(5), a = []; for (let i = 0; i < 80; i++) a.push({ x: Math.floor(R() * W), y: 3 + Math.floor(R() * 96), k: Math.floor(R() * 4), big: R() < 0.08, early: R() < 0.4 }); return a; })();
476
+ const CLOUDS = (() => { const R = rng(3), a = []; const ys = [34, 52, 28, 60, 44, 38];
477
+ for (let i = 0; i < 6; i++) { const w = 30 + Math.floor(R() * 28); const n = 3 + Math.floor(R() * 2), bumps = [];
478
+ for (let b = 0; b < n; b++) { const u = (b + 0.5) / n; bumps.push([w * (0.12 + 0.76 * u), (0.16 + 0.16 * Math.sin(Math.PI * u) + R() * 0.06) * w]); }
479
+ const h = Math.ceil(Math.max(...bumps.map(b => b[1]))) + 2;
480
+ a.push({ x: i * 68 + Math.floor(R() * 24), y: ys[i] - h, w, h, bumps }); } return a; })();
481
+ const FG = [[20, 9], [70, 12], [150, 8], [236, 11], [330, 10], [372, 13], [420, 9], [520, 12], [610, 10], [646, 13], [700, 8]];
482
+ const FIREFLY = (() => { const R = rng(17), a = []; for (let i = 0; i < 7; i++) a.push({ x: 20 + Math.floor(R() * 280), y: 112 + Math.floor(R() * 30), p: R() * 6.28, q: R() * 6.28, o: Math.floor(R() * 20) }); return a; })();
483
+
484
+ // ------------------------------------------------------------------ frame
485
+ function drawSky(f) {
486
+ const sk = [E.s0, E.s1, E.s2, E.s3, E.s4];
487
+ for (let y = 0; y < H; y++) { const v = clamp((y - 4) / 116 * 4, 0, 4), b = Math.floor(v), fr = v - b, fr2 = clamp((fr - 0.5) * 2.2 + 0.5, 0, 1);
488
+ for (let x = 0; x < W; x++) IB[y * W + x] = sk[Math.min(4, b + (fr2 > bay(x, y) ? 1 : 0))]; }
489
+ // stars
490
+ const wr = waveR(f), lp = lightAt(f);
491
+ if (f >= FLASH + 2) for (const s of STARS) { if (!s.early && (wr === null || Math.hypot(s.x - lp[0], s.y - lp[1]) < wr + 3)) continue;
492
+ if (lp && Math.hypot(s.x - lp[0], s.y - lp[1]) < lp[2] + 3) continue; const c = STS[s.k]; px(s.x, s.y, c);
493
+ if (s.big) { px(s.x - 1, s.y, M(3)); px(s.x + 1, s.y, M(3)); px(s.x, s.y - 1, M(3)); px(s.x, s.y + 1, M(3)); } }
494
+ // sun
495
+ const sy = Math.round(44 + f * 0.41), sx = 236;
496
+ if (sy < 140 && !hidden('sun')) for (let y = sy - 22; y <= sy + 22; y++) for (let x = sx - 22; x <= sx + 22; x++) { const d = Math.hypot(x - sx, y - sy), b = bay(x, y);
497
+ if (d <= 10) px(x, y, E.sun); else if (d <= 16 && b < 0.5 - (d - 10) * 0.02) px(x, y, E.sunG); else if (d <= 22 && b < 0.25 - (d - 16) * 0.025) px(x, y, E.sunG); }
498
+ // clouds
499
+ const off = Math.floor(cam(f) * 0.06) + Math.floor(f / 4);
500
+ for (const c of CLOUDS) { const x0 = ((c.x - off) % 420 + 420) % 420 - 50;
501
+ for (let j = 0; j < c.h; j++) for (let i = 0; i < c.w; i++) { const by = c.h - 1; let inn = false, rim = 0;
502
+ for (const [bx, r] of c.bumps) { const d = Math.hypot(i - bx, (j - by) * 1.25); if (d <= r) { inn = true; rim = Math.max(rim, r - d); } }
503
+ if (!inn) continue; const shade = j >= by - 1 || (j >= by - 3 && bay(x0 + i, j) < 0.5); px(x0 + i, c.y + j, shade ? E.clD : E.clL); } }
504
+ }
505
+ function drawGround(f, cx) {
506
+ for (let y = 140; y < H; y++) for (let x = 0; x < W; x++) { const wx = x + cx; let s = 0;
507
+ if (wx >= BR0 && wx < BR1) {
508
+ const sag = bridgeSag(wx), py = y - sag;
509
+ if (py >= 150 && py <= 153) s = (wx - BR0) % 4 === 3 ? E.dD : py === 150 ? E.dL : py === 153 ? E.dD : E.dM;
510
+ else if (y >= 154 + sag) { const deep = y - 156, dash = ((y * 7 + (wx >> 2)) & 3) === 0;
511
+ if (dash && hash(wx >> 2, y) < 0.17 && ((Math.floor(wx / 3) + (f >> 2)) & 3) === 0) s = E.rvG;
512
+ else if (dash || (y === 154 + sag && (wx & 3) < 2)) s = WS[(Math.floor((wx + (y & 1) * 2) / 3)) & 3]; else s = deep > 10 ? E.rvD : E.rvB; }
513
+ const rope = 143 + Math.round(4 * Math.sin(Math.PI * (wx - BR0) / (BR1 - BR0)));
514
+ if (y === rope) s = E.dD; if ((wx - BR0) % 8 === 4 && y > rope && py < 150) s = E.dD;
515
+ if ((wx - BR0 < 2 || BR1 - wx <= 2) && y >= 141 && y < 154) s = (wx - BR0 === 0 || BR1 - wx === 1) ? E.dL : E.dD;
516
+ } else if (y < 150) {
517
+ if (y === 149 && hash(wx, 1) < 0.35) s = E.gM; else if (y === 148 && hash(wx, 1) < 0.1) s = E.gL;
518
+ if (hash(wx >> 0, 77) < 0.035) { if (y === 147) s = E.flw; else if (y >= 148) s = E.gD; }
519
+ } else {
520
+ const bank = wx === BR0 - 1 || wx === BR1;
521
+ if (y === 150) s = hash(wx, 2) < 0.15 ? E.gM : E.gL; else if (y <= 152) s = hash(wx, y) < 0.08 ? E.gL : E.gM; else if (y === 153) s = E.gD;
522
+ else { const drip = Math.floor(hash(wx >> 1, 5) * 3); if (y < 154 + drip && y <= 155) s = E.gD;
523
+ else { const ty = y - 156, row = Math.floor(ty / 12), tx = (wx + (row & 1) * 8) & 15;
524
+ if (tx === 0 || ty % 12 === 11 || ty < 0) s = E.dD; else if (tx === 1 || ty % 12 === 0) s = E.dL; else s = hash(wx, y) < 0.05 ? E.dL : hash(wx + 1, y - 1) < 0.05 ? E.dD : E.dM; } }
525
+ if (bank && y > 150) s = E.dD;
526
+ }
527
+ if (s) IB[y * W + x] = s; }
528
+ }
529
+ const FRAYS = F(cues.rays_off);
530
+ const raysStep = f => f < FRAYS ? -1 : Math.floor((f - FRAYS) / 3); // -1 full, 0 R34, 1 R18, 2+ gone
531
+ const RIM_DIM = [{ [M(8)]: M(15), [M(15)]: M(14) }, { [M(8)]: M(14), [M(15)]: M(13) }, { [M(8)]: M(13), [M(15)]: M(24) }];
532
+ function drawRays(f) {
533
+ const tau = f - FLASH; if (tau < 0) return; const rs = raysStep(f); if (rs >= 2) return;
534
+ const cx = CHEST_X - CAM_END + 10, cy = GROUND - 11;
535
+ const R = rs === 0 ? 34 : rs === 1 ? 18 : tau < 6 ? 30 + tau * 26 : Math.max(50, 190 - (tau - 6) * 7) + ((Math.floor(f / 6) & 1) ? 2 : 0), core = rs === 0 ? 8 : rs === 1 ? 0 : 14;
536
+ const rot = Math.floor(f / 3) * 0.035;
537
+ for (let y = Math.max(0, cy - R); y < GROUND; y++) for (let x = Math.max(0, cx - R); x < Math.min(W, cx + R); x++) {
538
+ const dx = x - cx, dy = y - cy, d = Math.hypot(dx, dy); if (d > R) continue;
539
+ const a = Math.atan2(dy, dx) + rot, band = Math.floor((a / (2 * Math.PI) + 1) * 18) & 1; const fall = 1 - d / R;
540
+ if (d < core) continue;
541
+ if (band && fall * 1.3 > bay(x, y)) px(x, y, d < R * 0.4 ? M(15) : M(14));
542
+ }
543
+ }
544
+ function drawHero(f, cx) {
545
+ const [key, p, tail] = heroPose(f); let h = heroH(f);
546
+ if (key === 'cheer') h = 2;
547
+ const x = hx(f) - cx, bx = hx(f) + 8, sag = bridgeSag(bx);
548
+ const blink = (f >= 150 && f < 152) || (f >= 276 && f < 278) || key.startsWith('flinch');
549
+ const s = hero(key, p, tail, blink); blit(s, x - HERO_OX, GROUND - 20 - h + sag - HERO_OY + 1);
550
+ }
551
+ function drawEntities(f, cx) {
552
+ // coins
553
+ for (const c of COINS) { if (f < c.f) blit(COIN[(Math.floor(f / 3) + c.x) & 3], c.x - cx - 4, c.y - 5);
554
+ else if (f < c.f + 9) { const a = f - c.f; const s = SPARK[a < 3 ? 1 : a < 6 ? 2 : 0]; blit(s, c.x - cx - (s.w >> 1), c.y - (s.h >> 1)); } }
555
+ // slime
556
+ const sx = SLIME_X - cx;
557
+ if (f < STOMP) { const A = (Math.floor(f / 5) & 1) === 0; blit(A ? SLIME_A : SLIME_B, sx - 7, GROUND - (A ? 9 : 7)); }
558
+ else if (f < STOMP + 7) blit(SLIME_F, sx - 7, GROUND - 4);
559
+ else if (f < STOMP + 16) { const a = f - STOMP - 7, s = PUFF[Math.min(2, Math.floor(a / 3))]; blit(s, sx - (s.w >> 1), GROUND - 6 - Math.floor(a / 3)); }
560
+ // dust puffs
561
+ const puffs = [[J1, hx(J1) + 4], [J1 + 18, hx(J1 + 18) + 6], [J2, hx(J2) + 4], [L2, hx(L2) + 6]];
562
+ for (let k = fSkid; k < fStop; k += 3) puffs.push([k, hx(k) + 12]);
563
+ for (const [f0, wx] of puffs) { const a = f - f0; if (a < 0 || a >= 9) continue; const s = PUFF[Math.floor(a / 3)]; blit(s, wx - cx - (s.w >> 1) - Math.floor(a / 3), GROUND - s.h - Math.floor(a / 4)); }
564
+ // chest
565
+ const chx = CHEST_X - cx; let shake = 0;
566
+ if (f >= fShk && f < FLASH) shake = [1, -1, 0, 1, -1, 1, 0, -1, 1, -1, 1, 0][(f - fShk) % 12];
567
+ if (f >= FLASH) {
568
+ const tau = f - FLASH, gp = gemPos(f); let nsp = 0;
569
+ for (const p of FOUNT) {
570
+ if (p.kind === 'coin') { const tt = tau - p.t0; if (tt < 0) continue; const [x, y, life] = fountPos(p, tt, chx + 10 + p.sx, GROUND - 12);
571
+ if (!life || (life === 2 && (f & 1))) continue; blit(COIN[(Math.floor(f / 2) + p.ph) & 3], x - 3, y - 4); }
572
+ else { const [x, y] = fountPos(p, tau, chx + 10, GROUND - 12); if (tau > p.life) continue; if (gp && Math.hypot(x - gp[0], y - gp[1]) < 20 && ++nsp > 6) continue; const s = SPARK[tau < p.life * 0.3 ? 2 : tau < p.life * 0.7 ? 1 : 0]; blit(s, x - (s.w >> 1), y - (s.h >> 1)); } }
573
+ const cs = f === FLASH ? CHEST_HALF : CHEST_OPEN, rs = raysStep(f); blit(cs, chx, GROUND - cs.h + 1, false, rs < 0 ? null : RIM_DIM[Math.min(2, rs)]);
574
+ } else {
575
+ const talking = f >= fType && f < fType + 27 && (Math.floor((f - fType) / 3) % 2 === 0);
576
+ const hop = talking ? 1 : 0;
577
+ let cs = talking ? CHEST_TALK : CHEST;
578
+ let L = 0;
579
+ if (f >= LH1 && f < LH1 + 2) { cs = CHEST_CRACK; shake = 0; L = 4; } else if (f === LH1 + 2) shake = -1;
580
+ else if (f >= LH2 && f < FLASH) { cs = CHEST_CRACK2; shake = (f & 1) ? 1 : 0; L = 8 + (f - LH2) * 7; }
581
+ if (L) { const sy = GROUND - 10; // light leaking from the seam
582
+ for (const [x0, ddx, ddy] of [[1, -1, -0.55], [5, -0.35, -1], [10, 0, -1], [14, 0.35, -1], [18, 1, -0.55]]) {
583
+ line(chx + x0, sy, chx + x0 + Math.round(ddx * L), sy + Math.round(ddy * L), (x, y) => { const d = Math.hypot(x - chx - x0, y - sy); if (d < L * 0.55 || bay(x, y) < 0.5) px(x, y, d < L * 0.55 ? M(15) : M(14)); }); } }
584
+ blit(cs, chx + shake, GROUND - cs.h + 1);
585
+ if (f >= fShk) for (const k of [fShk, fShk + 3, LH1, LH2]) { const a = f - k; if (a < 0 || a > 4) continue; // dust flecks
586
+ for (const sd of [-1, 1]) px(chx + (sd < 0 ? 0 : 19) + sd * (1 + a + (k & 1)), GROUND - [1, 2, 2, 1, 0][a], E.dL); }
587
+ }
588
+ drawStar(f);
589
+ drawHero(f, cx);
590
+ drawGem(f);
591
+ // alert bubble
592
+ if (f >= F(cues.question) && f < FLASH) { const ex = f >= LH2, b = ex ? BUBBLE : BUBBLE_Q, pop = f < (ex ? LH2 : F(cues.question)) + 2 ? 1 : 0; blit(b, hx(f) - cx + 9 + (ex ? 1 : 0), GROUND - 20 - b.h - 1 - pop); }
593
+ if (f >= fStop && f < fStop + 16) { const hxs = hx(f) - cx; const b = f < fStop + 2 ? BUBBLE_S : BUBBLE; blit(b, hxs + 9, GROUND - 20 - b.h - 1); }
594
+ }
595
+ const HERO_SX = hx(fStop) - CAM_END; // hero screen x once the camera has stopped
596
+ function gemPos(f) {
597
+ const tau = f - FLASH; if (tau < 1) return null;
598
+ const x0 = CHEST_X - CAM_END + 10, y0 = GROUND - 12, top = y0 - 34;
599
+ if (tau <= 6) { const u = tau / 6; return [x0, Math.round(y0 - 34 * (1 - (1 - u) * (1 - u)))]; }
600
+ if (f < FCHEER) return [x0, top];
601
+ const hold = [HERO_SX + 8, GROUND - 19 - 4 - (V5 ? 1 : 0)]; // v5: the bigger orb sits 1 px higher
602
+ if (f < FGET) { const u = (f - FCHEER + 1) / (FGET - FCHEER + 1); return [Math.round(x0 + (hold[0] - x0) * u), Math.round(top + (hold[1] - 2 - top) * u - 12 * u * (1 - u))]; }
603
+ const [, p] = heroPose(f); return [hold[0], hold[1] + p.bob];
604
+ }
605
+ function drawGem(f) {
606
+ const g = gemPos(f); if (!g) return; const [gx, gy] = g;
607
+ if (V5) { if (!hidden('dot')) blit(ORB, gx - 5, gy - 5);
608
+ if (f >= FGET) for (const sd of [-1, 1]) { const x0 = sd < 0 ? gx - 5 : gx + 4, xo = sd < 0 ? x0 - 1 : x0 + 2; // 2x2 hands on the orb's lower sides (kept with nolink=dot)
609
+ px(x0, gy + 2, M(28)); px(x0 + 1, gy + 2, M(28)); px(x0, gy + 3, M(25)); px(x0 + 1, gy + 3, M(25)); px(xo, gy + 2, M(0)); px(xo, gy + 3, M(0)); px(x0, gy + 1, M(0)); px(x0 + 1, gy + 1, M(0)); }
610
+ return; }
611
+ blit(GEM, gx - 4, gy - 4);
612
+ if (f >= FGET) for (const sd of [-1, 1]) { const x0 = sd < 0 ? gx - 4 : gx + 3; // 2x2 hands wrapped on the lower facets
613
+ px(x0, gy, M(28)); px(x0 + 1, gy, M(28)); px(x0, gy + 1, M(25)); px(x0 + 1, gy + 1, M(25)); px(sd < 0 ? x0 - 1 : x0 + 2, gy, M(0)); px(sd < 0 ? x0 - 1 : x0 + 2, gy + 1, M(0)); px(x0, gy - 1, M(0)); px(x0 + 1, gy - 1, M(0)); }
614
+ }
615
+ function drawStar(f) {
616
+ if (hidden('dot')) return; // the orb's star / sparkles leave with it
617
+ const g = gemPos(f); if (!g) return; const [gx, gy] = g;
618
+ const sl = starLen(f); if (sl) { const a0 = (Math.floor(f / 4) & 3) * Math.PI / 8;
619
+ for (let q = 0; q < 4; q++) { const ca = Math.cos(a0 + q * Math.PI / 2), sa = Math.sin(a0 + q * Math.PI / 2);
620
+ for (let r = 5; r <= sl; r++) { const x = gx + Math.round(ca * r), y = gy + Math.round(sa * r), u = (r - 4) / (sl - 4);
621
+ if (u < 0.4) px(x, y, M(8)); else if (u < 0.72) px(x, y, M(7)); else if (bay(x, y) < 1.5 - u) px(x, y, M(6)); } } }
622
+ if (f >= FGET && f < FGET + 6) { const a = f - FGET, s = SPARK[a < 2 ? 2 : a < 4 ? 1 : 0]; // catch burst
623
+ blit(s, gx - (s.w >> 1), gy - (s.h >> 1)); for (const [vx, vy] of [[-1, -1], [1, -1], [-1, 1], [1, 1]]) px(gx + vx * (5 + a * 2), gy + vy * (5 + a * 2), M(15)); }
624
+ else if (f >= FGET + 6 && f < FRAYS) { const a = (f - FGET) % 8; if (a < 4) { const s = SPARK[a === 0 || a === 3 ? 0 : 1]; blit(s, gx + 4 - (s.w >> 1), gy - 4 - (s.h >> 1)); } }
625
+ else if (f - FLASH > 6 && ((f >> 1) & 1)) px(gx + 3, gy - 3, M(8));
626
+ }
627
+ function starLen(f) { const rs = raysStep(f); if (rs < 0) return 0; if (rs < 3) return [7, 11, 15][rs]; return 12 + [6, 4, 2, 1][(f - FGET) % 12] || 12; }
628
+ function lightAt(f) { // crystal light pool (cool palette re-light), [x, y, r, r1]
629
+ if (f < FGET) return null; const g = gemPos(f), k = Math.floor((f - FGET) / 2);
630
+ const r = [10, 20, 30, 37, 38][Math.min(4, k)], r1 = Math.min(22, Math.round(r * 0.58)) + (k >= 4 && ((f - FGET) % 12) < 2 ? 2 : k >= 4 && ((f - FGET) % 12) >= 6 && ((f - FGET) % 12) < 8 ? -2 : 0);
631
+ return [g[0], g[1], r, r1];
632
+ }
633
+ const NWR = cues.night_wave_r, FNW = cues.night_wave.map(F);
634
+ const waveR = f => { if (f < FNW[0]) return null; let r = NWR[0]; for (let k = 0; k < FNW.length; k++) if (f >= FNW[k]) r = NWR[k]; return r; };
635
+ function drawPopups(f, cx) {
636
+ if (f >= STOMP && f < STOMP + 30 && (f < STOMP + 20 || (f & 1))) tinyNum('500', SLIME_X - 6 - cx, GROUND - 19 - heroH(STOMP) - 11 - Math.floor((f - STOMP) / 2), M(8), M(0));
637
+ }
638
+ function drawFG(f, cx) {
639
+ const off = Math.floor(cx * 1.4);
640
+ for (const [fx, hgt] of FG) { const x0 = fx - off + 40; if (x0 < -12 || x0 > W + 12) continue;
641
+ for (let b = 0; b < 5; b++) { const bx = x0 + b * 2 - 4, bh = hgt - Math.abs(b - 2) * 3 + (b & 1); for (let j = 0; j < bh; j++) { const lean = Math.floor(j * (b - 2) / 6); px(bx + lean, H - 1 - j, j > bh - 3 ? E.hM : E.hD); px(bx + lean + 1, H - 1 - j, E.hD); } } }
642
+ }
643
+ function drawFireflies(f) {
644
+ if (f < fLogo + 6) return;
645
+ for (const q of FIREFLY) { if (((f + q.o) % 22) >= 15) continue; const fs = Math.floor(f / 2) * 2; const x = q.x + Math.round(7 * Math.sin(fs * 0.045 + q.p)), y = q.y + Math.round(4 * Math.sin(fs * 0.07 + q.q));
646
+ px(x, y, M(15)); if (((f + q.o) % 22) < 9) { px(x - 1, y, M(13)); px(x + 1, y, M(13)); px(x, y - 1, M(13)); px(x, y + 1, M(13)); } }
647
+ }
648
+ function drawHUD(f) {
649
+ if (f >= fBoxO) return;
650
+ const [s, c] = scoreAt(f);
651
+ text('SCORE', 16, 17, 'Press Start 2P', 8, M(8), M(0));
652
+ text(String(s).padStart(6, '0'), 16, 27, 'Press Start 2P', 8, M(14), M(0));
653
+ blit(COIN[Math.floor(f / 3) & 3], 262, 8);
654
+ text('x' + String(c).padStart(2, '0'), 272, 17, 'Press Start 2P', 8, M(8), M(0));
655
+ }
656
+ function drawBox(f) {
657
+ let h = 0; const full = 30;
658
+ if (f >= fBoxO && f < fBoxC) h = f < fBoxO + 2 ? 10 : f < fBoxO + 4 ? 20 : full;
659
+ else if (f >= fBoxC && f < fBoxC + 3) h = [20, 10, 4][f - fBoxC];
660
+ if (!h) return;
661
+ const x0 = 72, w = 176, y0 = 99 - (h >> 1);
662
+ rect(x0, y0, w, h, M(0)); rect(x0 + 1, y0 + 1, w - 2, h - 2, M(8)); rect(x0 + 2, y0 + 2, w - 4, h - 4, M(3)); rect(x0 + 3, y0 + 3, w - 6, h - 6, M(2));
663
+ for (let y = y0 + 3; y < y0 + Math.min(h - 3, 9); y++) for (let x = x0 + 3; x < x0 + w - 3; x++) if (bay(x, y) < 0.5 - (y - y0 - 3) * 0.08) px(x, y, M(3));
664
+ if (h < full) return;
665
+ const tx = 165; blit(TAIL, tx - 3, y0 + h - 6); rect(tx - 3, y0 + h - 6, 13, 3, M(2)); rect(tx, y0 + h - 3, 7, 3, M(2)); // speech tail -> chest
666
+ // name tag
667
+ const ty = y0 - 8; { const tx = 82; rect(tx, ty, 56, 13, M(0)); rect(tx + 1, ty + 1, 54, 11, M(26)); rect(tx + 1, ty + 1, 54, 1, M(27));
668
+ text('神秘宝箱', tx + 4, ty + 11, 'Fusion Pixel 12', 12, M(14), M(0)); }
669
+ const n = f < fType ? 0 : Math.min(9, Math.floor((f - fType) / 3) + 1);
670
+ const talk = f >= fType && f < fType + 27 && (Math.floor((f - fType) / 3) % 2 === 0);
671
+ rect(x0 + 5, y0 + 5, 24, 20, M(1)); blit(talk ? CHEST_TALK : CHEST, x0 + 7, y0 + (talk ? 6 : 7));
672
+ text('欢迎来到像素世界!', x0 + 34, y0 + 22, 'Fusion Pixel 12', 12, M(8), M(1), 1, n);
673
+ if (f >= fType + 27 && ((f >> 2) & 1) === 0) { const ax = x0 + w - 14, ay = y0 + h - 9; for (let j = 0; j < 3; j++) for (let i = j; i < 5 - j; i++) px(ax + i, ay + j, M(14)); }
674
+ }
675
+ function drawLogo(f) {
676
+ if (f < fLogo - 12) return 0;
677
+ const LOGO = V5 ? LOGO5 : LOGO0;
678
+ const X = (W - LOGO.w) >> 1, Y = V5 ? Y5 : 16;
679
+ const BOUNCE = { 0: 0, 1: -5, 2: -2, 3: 0 };
680
+ const i = f - (fLogo - 12), dy = f < fLogo ? Math.round(-128 * (1 - (i / 12) ** 2)) : (BOUNCE[Math.floor((f - fLogo) / 2)] ?? 0); // gravity: spacing grows every frame
681
+ const flash = f === fLogo ? M(8) : f === fLogo + 1 ? M(15) : 0;
682
+ const shineS = f >= fShine && f < fShine + 12 ? -30 + (f - fShine) * (V5 ? 20 : 18) : -999;
683
+ const SQT = { 0: [6, 6], 1: [3, 3], 2: [-4, -2], 3: [-4, -2] }, [SQ, SW] = SQT[f - fLogo] || [0, 0]; // squash 6/12 px, stretch +4 on the rebound
684
+ const LHs = LOGO.h - SQ, LWs = LOGO.w + 2 * SW;
685
+ for (let jj = 0; jj < LHs; jj++) { const j = Math.floor(jj * LOGO.h / LHs), y = Y + dy + SQ + jj; if (y < 0 || y >= H) continue;
686
+ for (let ii = 0; ii < LWs; ii++) { const i = Math.floor(ii * LOGO.w / LWs); let c = LOGO.d[j * LOGO.w + i]; if (!c) continue;
687
+ if (LOGO.fill[j * LOGO.w + i]) { if (flash) c = flash; const u = i + j * 0.7; if (u >= shineS && u < shineS + 6) c = M(8); else if (u >= shineS + 9 && u < shineS + 11) c = M(15); }
688
+ px(X - SW + ii, y, c); } }
689
+ if (f >= fLogo && f < fLogo + 10) { const a = f - fLogo, yb = Y + LOGO.h - 2; // dithered dust puffs from the bottom corners
690
+ for (const [ox, sd] of [[X + 6, -1], [X + LOGO.w - 6, 1]]) { const pcx = ox + sd * (3 + a * 2), r = 3 + a * 0.7, dens = 0.9 - a * 0.085;
691
+ for (let y = Math.floor(yb - r); y <= yb + r; y++) for (let x = Math.floor(pcx - r * 1.4); x <= pcx + r * 1.4; x++) { const d = Math.hypot((x - pcx) / 1.4, y - yb);
692
+ if (d <= r && bay(x, y) < dens * (1 - d / (r + 1) * 0.5)) px(x, y, d < r * 0.5 && a < 5 ? M(15) : M(31)); } } }
693
+ // glints in the hold
694
+ if (f >= fSub + 6) { const G = V5 ? LOGO.glint.map(([gx, gy]) => [X + gx, Y + gy]) : [[X + 3, Y + 2], [X + LOGO.w - 26, Y + 38], [X + 58, Y + 1], [X + LOGO.w - 4, Y + 4], [X + 30, Y + 37]];
695
+ const g = Math.floor((f - fSub) / 9) % G.length, a = (f - fSub) % 9; if (a < 6) { const s = SPARK[a < 2 ? 0 : a < 4 ? 2 : 1]; blit(s, G[g][0] - (s.w >> 1), G[g][1] - (s.h >> 1)); } }
696
+ if (f >= fLogo && f < fLogo + 9) { const a = f - fLogo, Yl = Y + dy;
697
+ const y1 = V5 ? LOGO.h - 4 : 64, y2 = V5 ? Math.round(LOGO.h * 0.25) : 18, y3 = V5 ? Math.round(LOGO.h * 0.65) : 46;
698
+ const EM = [[X + 12, Yl + y1, -2, 1], [X + LOGO.w - 12, Yl + y1, 2, 1], [X - 2, Yl + y2, -3, -1], [X + LOGO.w + 2, Yl + y2, 3, -1], [X + 44, Yl - 2, -1, -2], [X + LOGO.w - 44, Yl - 2, 1, -2], [X - 2, Yl + y3, -3, 1], [X + LOGO.w + 2, Yl + y3, 3, 1]];
699
+ for (const [ex, ey, vx, vy] of EM) { const s = SPARK[a < 3 ? 2 : a < 6 ? 1 : 0]; blit(s, ex + vx * a - (s.w >> 1), ey + vy * a - (s.h >> 1)); } }
700
+ return logoShake(f);
701
+ }
702
+ const logoShake = f => f >= fLogo && f < fLogo + 6 ? [3, -3, 2, -1, 1, 0][f - fLogo] : 0;
703
+ const shakeAt = f => logoShake(f) || (f >= FLASH && f < FLASH + 4 ? [2, -2, 1, -1][f - FLASH] : 0) || ({ [LH1]: 0.5, [LH1 + 1]: -0.5, [LH2]: 1, [LH2 + 1]: -0.5 }[f] ?? 0);
704
+ function drawRibbon(f) {
705
+ if (f < fSub) return;
706
+ const k = f - fSub, full = 88, w = k < 1 ? 16 : k < 2 ? 40 : k < 3 ? 64 : full, cx = 160, y0 = V5 ? RIB5 : 88, h = 15, x0 = cx - (w >> 1);
707
+ if (w === full) { for (const side of [-1, 1]) { const ex = side < 0 ? x0 - 10 : x0 + w; rect(ex, y0 + 4, 10, h, M(0)); rect(ex + 1, y0 + 5, 8, h - 2, M(26));
708
+ for (let j = 0; j < 5; j++) for (let i = 0; i < 5 - j; i++) px(side < 0 ? ex + i : ex + 9 - i, y0 + 6 + j + 3, M(0)); } }
709
+ rect(x0, y0, w, h, M(0)); rect(x0 + 1, y0 + 1, w - 2, h - 2, M(27)); rect(x0 + 1, y0 + 1, w - 2, 1, M(12)); rect(x0 + 1, y0 + h - 2, w - 2, 1, M(26));
710
+ if (V5) { const str = 'PIXEL ART', n = [2, 5, 7, 9][Math.min(3, Math.floor(k / 3))], tw = textW(str, 'Press Start 2P', 8); // v5: small English line, the logo is the name
711
+ if (w === full) text(str, cx - (tw >> 1), y0 + 11, 'Press Start 2P', 8, M(8), M(26), 0, n); return; }
712
+ const str = '像素冒险', n = Math.min(4, Math.floor(k / 3) + 1), tw = textW(str, 'Fusion Pixel 12', 12, 5);
713
+ if (w === full) text(str, cx - (tw >> 1), y0 + 12, 'Fusion Pixel 12', 12, M(8), M(26), 5, n);
714
+ }
715
+ function drawPress(f) {
716
+ if (f < fPress || ((f - fPress) % 24) >= 12) return;
717
+ const s = 'PRESS START', w = textW(s, 'Press Start 2P', 8), x = 160 - (w >> 1), b = 168;
718
+ rect(x - 4, b - 11, w + 8, 14, M(0)); rect(x - 3, b - 10, w + 6, 12, M(1));
719
+ for (const [ox, oy] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) text(s, x + ox, b + oy, 'Press Start 2P', 8, M(0), 0);
720
+ text(s, x, b, 'Press Start 2P', 8, M(8), 0);
721
+ }
722
+ let shakeY = 0;
723
+ const F_CAMSTOP = (() => { let f = 0; while (cam(f) < CAM_END) f++; return f; })();
724
+ function drawFrame(f) {
725
+ const cx = cam(f);
726
+ drawSky(f);
727
+ blitLayer(MT, Math.floor(cx * 0.125) + Math.floor(Math.max(0, f - F_CAMSTOP) / 16), 60, 150);
728
+ blitLayer(MID, Math.floor(cx * 0.3), 80, 150);
729
+ blitLayer(HILLS, Math.floor(cx * 0.55), 110, 150);
730
+ drawRays(f);
731
+ drawGround(f, cx);
732
+ drawEntities(f, cx);
733
+ drawFG(f, cx);
734
+ drawPopups(f, cx);
735
+ drawFireflies(f);
736
+ drawHUD(f);
737
+ drawBox(f);
738
+ if (V5) { if (!hidden('title')) drawLogo(f); shakeY = shakeAt(f); } else
739
+ shakeY = drawLogo(f) || (f >= FLASH && f < FLASH + 4 ? [2, -2, 1, -1][f - FLASH] : 0) || ({ [LH1]: 0.5, [LH1 + 1]: -0.5, [LH2]: 1, [LH2 + 1]: -0.5 }[f] ?? 0);
740
+ drawRibbon(f);
741
+ drawPress(f);
742
+ }
743
+ function drawSheet() {
744
+ IB.fill(M(7)); let x = 4, y = 4;
745
+ const put = s => { if (x + s.w > W - 4) { x = 4; y += 30; } blit(s, x, y); x += s.w + 3; };
746
+ RUN.forEach((p, i) => put(hero('run' + i, p, 'run' + (i % 3), false)));
747
+ for (const k of ['up', 'down', 'land', 'skid', 'idle0', 'idle1', 'cheer', 'get0', 'get1', 'flinch0', 'flinch1']) put(hero(k, POSE[k], POSE[k].tail || 'hang', false));
748
+ put(hero('idle0', POSE.idle0, 'hang', true));
749
+ x = 4; y += 30; COIN.forEach(put); [SLIME_A, SLIME_B, SLIME_F, CHEST, CHEST_TALK, CHEST_CRACK, CHEST_CRACK2, CHEST_HALF, CHEST_OPEN, GEM, BUBBLE, BUBBLE_Q, BUBBLE_S, ...SPARK, ...PUFF].forEach(put);
750
+ x = 4; y += 26; blit(LOGO, x, y);
751
+ text('欢迎来到像素世界!', 150, y + 14, 'Fusion Pixel 12', 12, M(0), 0, 1); text('神秘宝箱 像素冒险', 150, y + 30, 'Fusion Pixel 12', 12, M(26), 0, 5);
752
+ text('SCORE 001100', 150, y + 44, 'Press Start 2P', 8, M(0)); text('PRESS START', 150, y + 56, 'Press Start 2P', 8, M(3));
753
+ }
754
+
755
+ // ------------------------------------------------------------------ WebGL2 CRT
756
+ const cv = document.getElementById('c');
757
+ const gl = cv.getContext('webgl2', { preserveDrawingBuffer: true, antialias: false, alpha: false });
758
+ const vs = `#version 300 es
759
+ in vec2 p; void main(){ gl_Position = vec4(p,0.,1.); }`;
760
+ const fs = `#version 300 es
761
+ precision highp float;
762
+ uniform sampler2D uTex; uniform vec2 uSrc, uOut, uShake; uniform float uOn, uOnX, uCrt, uWhite, uGain, uRoll, uWob;
763
+ out vec4 o;
764
+ vec3 sharp(vec2 uv){ vec2 t = uv*uSrc; vec2 fl = floor(t); vec2 s = t - fl; float sc = uOut.y/uSrc.y; float reg = 0.5 - 0.5/sc;
765
+ vec2 cd = s - 0.5; vec2 f = (cd - clamp(cd, -reg, reg))*sc + 0.5; return texture(uTex, (fl + f)/uSrc).rgb; }
766
+ void main(){
767
+ vec2 uv = gl_FragCoord.xy/uOut; uv.y = 1.0 - uv.y;
768
+ if (uCrt < 0.5) { o = vec4(sharp(uv), 1.0); return; }
769
+ vec2 c = uv*2.0 - 1.0; float k = 0.03;
770
+ vec2 cc = c*(1.0 + k*dot(c,c))/(1.0 + k);
771
+ vec2 u = cc*0.5 + 0.5;
772
+ vec2 e = min(u, 1.0 - u); float bez = smoothstep(0.0, 0.0022, e.x)*smoothstep(0.0, 0.004, e.y);
773
+ float dy = u.y - 0.5, dx0 = u.x - 0.5; float on = smoothstep(uOn*0.5 + 0.0015, uOn*0.5 - 0.0015, abs(dy))*smoothstep(uOnX*0.5 + 0.002, uOnX*0.5 - 0.002, abs(dx0));
774
+ u.y = 0.5 + dy/max(uOn, 0.004);
775
+ float blank = 1.0;
776
+ if (uRoll > 0.0) { u.y = fract(u.y + uRoll); blank = smoothstep(0.0, 0.012, u.y)*smoothstep(0.0, 0.012, 1.0 - u.y); }
777
+ if (uWob != 0.0) u.x += (mod(floor(u.y*uSrc.y), 2.0) < 1.0 ? 1.0 : -1.0)*uWob/uSrc.x;
778
+ u += uShake/uSrc;
779
+ vec2 uc = clamp(u, 0.0, 1.0);
780
+ vec3 col = sharp(uc);
781
+ float lum = dot(col, vec3(0.3, 0.59, 0.11));
782
+ float sy = fract(uc.y*uSrc.y) - 0.5;
783
+ float bw = mix(0.30, 0.47, lum);
784
+ float scan = mix(0.64, 1.0, smoothstep(bw + 0.13, bw - 0.05, abs(sy)));
785
+ vec3 g = vec3(0.0); vec2 ts = 1.0/uSrc;
786
+ for (int i = 0; i < 8; i++) { float a = float(i)*0.785398; vec2 d = vec2(cos(a), sin(a));
787
+ g += texture(uTex, uc + d*ts*1.6).rgb + texture(uTex, uc + d*ts*3.4).rgb*0.6; }
788
+ g /= 12.8;
789
+ vec3 glow = max(g - 0.30, 0.0)*0.55;
790
+ col = col*scan*1.10 + glow;
791
+ float r = length(c)/1.4142; col *= 1.0 - 0.24*pow(r, 2.6);
792
+ col = mix(col, vec3(1.0), uWhite)*uGain*blank;
793
+ if (uWob != 0.0) col *= vec3(1.0 + 0.05*uWob, 1.0, 1.0 - 0.05*uWob);
794
+ vec3 ph = vec3(0.10, 0.12, 0.17)*uWhite*exp(-abs(dy)*6.0)*mix(exp(-abs(dx0)*9.0), 1.0, min(uOnX, 1.0));
795
+ o = vec4((col*on + ph*(1.0 - on))*bez, 1.0);
796
+ }`;
797
+ function sh(type, src) { const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s)); return s; }
798
+ const prog = gl.createProgram(); gl.attachShader(prog, sh(gl.VERTEX_SHADER, vs)); gl.attachShader(prog, sh(gl.FRAGMENT_SHADER, fs)); gl.linkProgram(prog); gl.useProgram(prog);
799
+ const vb = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vb); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
800
+ const loc = gl.getAttribLocation(prog, 'p'); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
801
+ const tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex);
802
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
803
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
804
+ const U = n => gl.getUniformLocation(prog, n);
805
+ gl.uniform2f(U('uSrc'), W, H); gl.uniform2f(U('uOut'), cv.width, cv.height); gl.uniform1i(U('uTex'), 0);
806
+ const RGBA = new Uint8Array(W * H * 4);
807
+ // dot line line 6 ln 60 ln 180 roll wobble x4
808
+ const PWR = { on: [0.012, 0.007, 0.006, 0.034, 0.334, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
809
+ onx: [0.018, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
810
+ wh: [1.0, 1.0, 0.85, 0.6, 0.35, 0.16, 0.05, 0, 0, 0, 0],
811
+ gain:[1.0, 1.45, 1.2, 1.1, 1.05, 1.02, 1.35, 1.1, 1.05, 1.02, 1.0],
812
+ roll:[0, 0, 0, 0, 0, 0, 8 / 180, 0, 0, 0, 0],
813
+ wob: [0, 0, 0, 0, 0, 0, 0, 1, -0.75, 0.5, -0.25] };
814
+ const ZOOM = cues.zoom.map((t, k) => [F(t), cues.zoom_scale[k]]);
815
+ const zoomAt = f => { if (f >= FLASH) return 6; let z = 6; for (const [f0, v] of ZOOM) if (f >= f0) z = v; return z; };
816
+ const LUT_L1 = new Uint8Array(NSLOT), LUT_L2 = new Uint8Array(NSLOT), LUT_M = new Uint8Array(NSLOT), LUT_N = new Uint8Array(NSLOT);
817
+ // crystal light = cool blue-white: surfaces re-lit by material (day colour), sky/far layers lifted 1-2 steps on the blue ramp
818
+ const lum = c => 0.3 * MP[c][0] + 0.59 * MP[c][1] + 0.11 * MP[c][2];
819
+ const BLUE = [0, 1, 2, 3, 4, 5, 6, 7], bRank = c => { let bi = 0; for (let k = 0; k < 8; k++) if (Math.abs(lum(BLUE[k]) - lum(c)) < Math.abs(lum(BLUE[bi]) - lum(c))) bi = k; return bi; };
820
+ const MAT = { fD: [3, 2], fM: [4, 3], fL: [5, 3], hD: [3, 2], hM: [4, 3], hL: [6, 3], gD: [3, 2], gM: [4, 3], gL: [18, 3],
821
+ dD: [2, 1], dM: [29, 2], dL: [30, 3], rD: [3, 2], rM: [29, 3], rL: [30, 29], flw: [7, 5] };
822
+ const SKYN = ['s0', 's1', 's2', 's3', 's4', 'sun', 'sunG', 'clL', 'clD', 'mD', 'mL', 'mS', 'mist', 'st0', 'st1', 'st2', 'st3'];
823
+ const PL1 = new Int16Array(64).fill(-1), PL2 = new Int16Array(64).fill(-1), SKYF = new Uint8Array(NSLOT);
824
+ for (const [n, v] of Object.entries(MAT)) { PL1[E[n]] = v[0]; PL2[E[n]] = v[1]; }
825
+ for (const n of SKYN) { const r = bRank(SM[E[n]][5]); PL1[E[n]] = Math.min(3, r + 1); PL2[E[n]] = Math.min(3, r + 1); SKYF[E[n]] = 1; }
826
+ const CB1 = c => { const l = lum(c); return l < 45 ? 3 : l < 90 ? 4 : l < 150 ? 5 : 6; }, CB2 = c => lum(c) < 60 ? 2 : lum(c) < 120 ? 3 : 4; // water: by current colour
827
+
828
+ window.DEMO = { width: 1920, height: 1080, fps: 30, duration: 10, motionBlur: { samples: 1, shutter: 0.5 } };
829
+ window.renderAt = async (t) => {
830
+ const f = Math.min(299, Math.max(0, Math.round(t * FPS)));
831
+ let lut;
832
+ if (SHEET) { drawSheet(); lut = makeLUT(0); } else { drawFrame(f); lut = makeLUT(f); }
833
+ const lw = SHEET || Q.has('nolight') ? null : lightAt(f), lt = V5 && hidden('pool') ? null : lw; // lw: light centre (night front), lt: pool
834
+ const wv = SHEET ? null : waveR(f); // night front rolling in toward the crystal
835
+ if (wv !== null) { makeLUT(f, [4, 5, 0.5], LUT_M); makeLUT(f, [5, 5, 0], LUT_N); }
836
+ if (lt) for (let sl = 0; sl < NSLOT; sl++) { if (sl >= 64) { LUT_L1[sl] = LUT_L2[sl] = lut[sl]; continue; }
837
+ const c = wv !== null ? LUT_N[sl] : lut[sl]; LUT_L1[sl] = PL1[sl] >= 0 ? PL1[sl] : CB1(c); LUT_L2[sl] = PL2[sl] >= 0 ? PL2[sl] : CB2(c); }
838
+ const z = SHEET ? 6 : zoomAt(f), cw = 1920 / z, ch = 1080 / z, [zx, zy] = cues.zoom_center;
839
+ const ox = clamp(Math.round(zx - cw / 2), 0, W - cw), oy = clamp(Math.round(zy - ch / 2), 0, H - ch);
840
+ for (let y = 0; y < ch; y++) for (let x = 0; x < cw; x++) { const X = x + ox, Y = y + oy, i = Y * W + X; let L = lut;
841
+ let d = -1;
842
+ if (wv !== null) { d = Math.hypot(X - lw[0], Y - lw[1]); const q = Math.round(clamp((d - wv) / 12 + 0.5, 0, 1) * 2 + bay(X, Y) - 0.5); L = q <= 0 ? lut : q === 1 ? LUT_M : LUT_N; }
843
+ if (lt && !(SKYF[IB[i]] && L === lut && wv === null)) { if (d < 0) d = Math.hypot(X - lt[0], Y - lt[1]); const r1 = lt[3];
844
+ if (d < r1) L = LUT_L1; else if (d < lt[2]) { const u = (d - r1) / (lt[2] - r1); L = bay(X, Y) > u ? (u < 0.5 ? LUT_L1 : LUT_L2) : (bay(X + 2, Y + 1) > u ? LUT_L2 : L); } }
845
+ const m = MP[L[IB[i]]], o = (y * cw + x) * 4; RGBA[o] = m[0]; RGBA[o + 1] = m[1]; RGBA[o + 2] = m[2]; RGBA[o + 3] = 255; }
846
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, cw, ch, 0, gl.RGBA, gl.UNSIGNED_BYTE, RGBA);
847
+ gl.uniform2f(U('uSrc'), cw, ch);
848
+ gl.viewport(0, 0, cv.width, cv.height);
849
+ gl.uniform1f(U('uCrt'), NOCRT ? 0 : 1);
850
+ const pw = (k, d) => (f < PWR.on.length && !SHEET ? PWR[k][f] : d);
851
+ gl.uniform1f(U('uOn'), pw('on', 1)); gl.uniform1f(U('uOnX'), pw('onx', 1)); gl.uniform1f(U('uWhite'), pw('wh', 0));
852
+ gl.uniform1f(U('uGain'), pw('gain', 1)); gl.uniform1f(U('uRoll'), pw('roll', 0)); gl.uniform1f(U('uWob'), pw('wob', 0));
853
+ gl.uniform2f(U('uShake'), 0, SHEET ? 0 : shakeY);
854
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
855
+ gl.finish();
856
+ };
857
+ window.__frame = f => { drawFrame(f); return IB; };
858
+
859
+ // ------------------------------------------------------------------ reel v5: link elements in 1920x1080 output px (pure in t)
860
+ if (V5) {
861
+ const KB = 0.03; // CRT barrel k (shader); 1 logical px = zoomAt(f) output px (6, or 8/10/12 in the push-in)
862
+ const crop = f => { const z = zoomAt(f), cw = 1920 / z, ch = 1080 / z, [zx, zy] = cues.zoom_center;
863
+ return [clamp(Math.round(zx - cw / 2), 0, W - cw), clamp(Math.round(zy - ch / 2), 0, H - ch), cw, ch]; };
864
+ const toOut = (X, Y, f) => { // logical (continuous) -> output px: zoom crop, shake, power-on roll, barrel inverse
865
+ const [ox, oy, cw, ch] = crop(f); const ux = (X - ox) / cw; let uy = (Y - oy) / ch;
866
+ if (NOCRT) return [ux * 1920, uy * 1080];
867
+ uy -= shakeAt(f) / ch; if (f < PWR.roll.length && PWR.roll[f]) uy = ((uy - PWR.roll[f]) % 1 + 1) % 1;
868
+ const tx = ux * 2 - 1, ty = uy * 2 - 1; let cx = tx, cy = ty;
869
+ for (let i = 0; i < 12; i++) { const k = (1 + KB) / (1 + KB * (cx * cx + cy * cy)); cx = tx * k; cy = ty * k; }
870
+ return [(cx * 0.5 + 0.5) * 1920, (cy * 0.5 + 0.5) * 1080];
871
+ };
872
+ const r1 = v => Math.round(v * 10) / 10;
873
+ const circ = (id, X, Y, r, f, ex) => { const [x, y] = toOut(X, Y, f), [xl] = toOut(X - r, Y, f), [xr] = toOut(X + r, Y, f), [, yt] = toOut(X, Y - r, f), [, yb] = toOut(X, Y + r, f);
874
+ return { id, type: 'circle', x: r1(x), y: r1(y), r: r1((xr - xl + yb - yt) / 4), ...ex }; };
875
+ const box = (id, X0, Y0, X1, Y1, f, ex) => { const [a, b] = toOut(X0, Y0, f), [c, d] = toOut(X1, Y1, f);
876
+ return { id, type: 'rect', x: r1((a + c) / 2), y: r1((b + d) / 2), w: r1(c - a), h: r1(d - b), rot: 0, ...ex }; };
877
+ const ORB_PAL = ['#0d0b1a', '#3d4a8a', '#4f6fc0', '#5b9bea', '#8cc8f5', '#c8ecff', '#ffffff'];
878
+ window.linkAt = (t) => {
879
+ const f = Math.min(299, Math.max(0, Math.round(t * FPS))), on = f >= 5, zpx = zoomAt(f), els = [];
880
+ const keepIB = IB.slice(), keepSh = shakeY; linkPass = true; drawFrame(f); linkPass = false; // scratch redraw (sun occlusion), then restore
881
+ // dot: the crystal ORB (11 logical px incl. outline); before the burst: a=0 at the chest mouth where it pops out (6.43)
882
+ const g = gemPos(f), gp = g || [CHEST_X - CAM_END + 10, GROUND - 22];
883
+ els.push(circ('dot', gp[0] + 0.5, gp[1] + 0.5, 5.5, f, { a: g ? 1 : 0, look: 'pixel', fill: '#8cc8f5', stroke: '#0d0b1a', sw: r1(zpx),
884
+ grad: ['#ffffff', '#3d4a8a'], pixel: r1(zpx), palette: ORB_PAL,
885
+ note: g ? (f < FCHEER ? 'crystal orb rising out of the chest' : f < FGET ? 'crystal orb lobbing into the hero\'s hands' : 'crystal orb held overhead (item get), 4-point star + light pool') : 'crystal orb not out yet (pops out of the chest at 6.43)' }));
886
+ // sun: pixel disc r 10 logical + dithered halo; a = visible fraction of the solid disc (mountains / clouds / zoom crop)
887
+ const sy = Math.round(44 + f * 0.41), sx = 236; let tot = 0, vis = 0;
888
+ if (sy < 140) for (let y = sy - 10; y <= sy + 10; y++) for (let x = sx - 10; x <= sx + 10; x++) if (Math.hypot(x - sx, y - sy) <= 10) { tot++; if (y >= 0 && y < H && IB[y * W + x] === E.sun) vis++; }
889
+ if (tot) { const [ox, oy, cw, ch] = crop(f); const inView = sx + 10 > ox && sx - 10 < ox + cw && sy + 10 > oy && sy - 10 < oy + ch;
890
+ els.push(circ('sun', sx + 0.5, sy + 0.5, 10.5, f, { a: on && inView ? r1(vis / tot) : 0, look: 'pixel', fill: '#' + HEX[makeLUT(f)[E.sun]], stroke: '#' + HEX[makeLUT(f)[E.sunG]], sw: 0, pixel: r1(zpx),
891
+ halo: r1(22 * zpx), note: 'pixel sun, solid disc + 2-level dithered halo to r 22 logical; sinks behind the ridge' })); }
892
+ // pool: the orb's light (palette re-light, 2-level Bayer edge)
893
+ const lt = lightAt(f); if (lt) els.push(circ('pool', lt[0] + 0.5, lt[1] + 0.5, lt[2], f, { a: 1, look: 'pixel', fill: '#4f6fc0', stroke: '#3d4a8a', sw: 0, r_inner: r1(lt[3] * zpx), note: 'crystal light pool (blue palette re-light, dithered rim)' }));
894
+ // title 「像素风」 logo block
895
+ if (f >= fLogo - 12) { const L = LOGO5, X = (W - L.w) >> 1, i = f - (fLogo - 12);
896
+ const dy = f < fLogo ? Math.round(-128 * (1 - (i / 12) ** 2)) : ({ 0: 0, 1: -5, 2: -2, 3: 0 }[Math.floor((f - fLogo) / 2)] ?? 0), y0 = Y5 + dy;
897
+ const a = f >= fLogo ? 1 : clamp((y0 + L.h) / L.h, 0, 1);
898
+ els.push(box('title', X, y0, X + L.w, y0 + L.h, f, { a: r1(a), note: '「像素风」 pixel logo (Fusion Pixel 12 x4 cells)' })); }
899
+ if (f >= fSub + 3) els.push(box('ribbon', 160 - 54, RIB5, 160 + 54, RIB5 + 19, f, { a: 1, note: 'red ribbon "PIXEL ART"' }));
900
+ // chest (18x14 sprite + outline) while on screen
901
+ const chx = CHEST_X - cam(f); if (chx + 20 > 0 && chx < W) { const open = f >= FLASH; els.push(box('chest', chx, open ? 133 : 135, chx + 20, 151, f, { a: on ? 1 : 0, note: open ? 'open chest' : 'treasure chest' })); }
902
+ // dialogue box (text block) 4.6–5.8
903
+ { let h = 0; if (f >= fBoxO && f < fBoxC) h = f < fBoxO + 2 ? 10 : f < fBoxO + 4 ? 20 : 30; else if (f >= fBoxC && f < fBoxC + 3) h = [20, 10, 4][f - fBoxC];
904
+ if (h) els.push(box('dialog', 72, 99 - (h >> 1), 248, 99 - (h >> 1) + h, f, { a: h === 30 ? 1 : 0.5, note: 'dialogue box 神秘宝箱: 欢迎来到像素世界!' })); }
905
+ // ground: grass top row (horizon-like line)
906
+ { const [x0, y0] = toOut(8, 150, f), [x1, y1] = toOut(312, 150, f), [, ym] = toOut(160, 150, f);
907
+ if (ym > 0 && ym < 1080) els.push({ id: 'ground', type: 'line', x0: r1(x0), y0: r1(y0), x1: r1(x1), y1: r1(y1), ym: r1(ym), w: r1(zpx), a: on ? 1 : 0, note: 'grass top of the ground (y 150 logical)' }); }
908
+ // CRT power-on (f0 centre dot 36x14 px -> f1-f3 scan line -> opens by f5): an IN target at the very start
909
+ if (!NOCRT && f === 0) els.push({ id: 'crt', type: 'circle', x: 959.5, y: 539.5, r: 7, rx: 18, ry: 7, a: 1, look: 'glow', fill: '#ffffff', stroke: '#b8c2d9', sw: 0, note: 'CRT power-on dot (squashes to the scan line at f1)' });
910
+ else if (!NOCRT && f <= 3) els.push({ id: 'crt', type: 'line', x0: 40, y0: 539.5, x1: 1880, y1: 539.5, w: r1(PWR.on[f] * 1080), a: 1, note: 'CRT power-on scan line (opens to full picture by f5)' });
911
+ IB.set(keepIB); shakeY = keepSh;
912
+ return els;
913
+ };
914
+ }