@guinetik/gcanvas 1.0.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 (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. package/vitest.config.js +13 -0
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Lore Prism - Collectible data fragments from beyond spacetime
3
+ *
4
+ * Mysterious prisms that grant Kerr energy, score, and cryptic knowledge.
5
+ * Hint at the wormhole secret and the true nature of the diagram.
6
+ */
7
+
8
+ import { Prism } from "../../../src/index.js";
9
+ import { CONFIG } from "./constants.js";
10
+
11
+ // ============================================================================
12
+ // LORE MESSAGES
13
+ // ============================================================================
14
+
15
+ const LORE_EARLY = [
16
+ { text: "Time flows in one direction. Or so they believed.", source: "Fragment 001" },
17
+ { text: "The diamond contains all futures. All pasts.", source: "Axiom" },
18
+ { text: "Light moves at 45 degrees. Always. This is law.", source: "First Principle" },
19
+ { text: "i- is where you began. i+ is where you end.", source: "Doctrine" },
20
+ { text: "The singularity is patient. It has already consumed you.", source: "Paradox" },
21
+ { text: "Your worldline is written. Follow it.", source: "Determinism" },
22
+ { text: "They mapped infinity. They should not have.", source: "Warning" },
23
+ { text: "Penrose drew the boundaries. Not what lies beyond.", source: "Whisper" },
24
+ ];
25
+
26
+ const LORE_WORMHOLE_HINTS = [
27
+ { text: "Some paths fold back upon themselves.", source: "Anomaly" },
28
+ { text: "The ancients spoke of doors between moments.", source: "Archive" },
29
+ { text: "Not all who fall into darkness stay fallen.", source: "Heresy" },
30
+ { text: "What if the end was also a beginning?", source: "Query" },
31
+ { text: "Blue light. Spiraling. A tear in the weave.", source: "Witness" },
32
+ { text: "They found a way to cheat causality.", source: "Redacted" },
33
+ { text: "The worm turns. The hole opens. Time bleeds.", source: "Cipher" },
34
+ { text: "Seek the spiral. It remembers your origin.", source: "Guidance" },
35
+ ];
36
+
37
+ const LORE_POST_WORMHOLE = [
38
+ { text: "You have seen beyond the veil.", source: "Awakening" },
39
+ { text: "The loop is not a prison. It is a gift.", source: "Revelation" },
40
+ { text: "Each return makes you stronger.", source: "Observer" },
41
+ { text: "Purple light in the void. Something waits.", source: "Vision" },
42
+ { text: "The artifact was not meant to be found.", source: "Defiance" },
43
+ { text: "Survive the unsurvivable. This is the way.", source: "Mantra" },
44
+ { text: "Inside the singularity, time has no meaning.", source: "Tactical" },
45
+ { text: "The void keeps the essence of travelers.", source: "Mystery" },
46
+ ];
47
+
48
+ const LORE_KERR = [
49
+ { text: "Rotating black holes bleed energy. Harvest it.", source: "Exploit" },
50
+ { text: "Frame dragging pulls you in. Use it.", source: "Strategy" },
51
+ { text: "The ergosphere is danger. And opportunity.", source: "Duality" },
52
+ { text: "Kerr knew what he unleashed.", source: "History" },
53
+ ];
54
+
55
+ // ============================================================================
56
+ // LORE PRISM - COLLECTIBLE
57
+ // ============================================================================
58
+
59
+ export class LorePrism {
60
+ constructor(u, v, lorePool = "early") {
61
+ this.u = u;
62
+ this.v = v;
63
+
64
+ // Size for collision
65
+ this.radius = 0.04;
66
+
67
+ // Animation
68
+ this.rotationPhase = Math.random() * Math.PI * 2;
69
+ this.pulsePhase = Math.random() * Math.PI * 2;
70
+ this.hoverOffset = 0;
71
+
72
+ // State
73
+ this.collected = false;
74
+ this.spawnTime = 0;
75
+
76
+ // Select lore from appropriate pool
77
+ this.lore = this.selectLore(lorePool);
78
+
79
+ // Create the prism shape
80
+ this.prism = new Prism(25, {
81
+ width: 18,
82
+ height: 18,
83
+ faceTopColor: "#4df",
84
+ faceBottomColor: "#28a",
85
+ faceLeftColor: "#3bc",
86
+ faceRightColor: "#2ab",
87
+ faceFrontColor: "#4ce",
88
+ faceBackColor: "#1a9",
89
+ stroke: "#8ff",
90
+ lineWidth: 1,
91
+ });
92
+ }
93
+
94
+ selectLore(pool) {
95
+ let messages;
96
+ switch (pool) {
97
+ case "wormhole":
98
+ messages = LORE_WORMHOLE_HINTS;
99
+ break;
100
+ case "post_wormhole":
101
+ messages = LORE_POST_WORMHOLE;
102
+ break;
103
+ case "kerr":
104
+ messages = LORE_KERR;
105
+ break;
106
+ default:
107
+ messages = LORE_EARLY;
108
+ }
109
+ return messages[Math.floor(Math.random() * messages.length)];
110
+ }
111
+
112
+ update(dt) {
113
+ this.rotationPhase += dt * 1.5;
114
+ this.pulsePhase += dt * 3;
115
+ this.hoverOffset = Math.sin(this.pulsePhase) * 3;
116
+ this.spawnTime += dt;
117
+
118
+ // Rotate the prism
119
+ this.prism.setRotation(
120
+ this.rotationPhase * 0.5,
121
+ this.rotationPhase,
122
+ this.rotationPhase * 0.3
123
+ );
124
+ }
125
+
126
+ getCircle() {
127
+ return { x: this.u, y: this.v, radius: this.radius };
128
+ }
129
+
130
+ get active() {
131
+ return !this.collected;
132
+ }
133
+ }
134
+
135
+ // ============================================================================
136
+ // LORE DISPLAY - Shows message when prism collected
137
+ // ============================================================================
138
+
139
+ export class LoreDisplay {
140
+ constructor(game) {
141
+ this.game = game;
142
+ this.active = false;
143
+ this.currentLore = null;
144
+ this.displayTimer = 0;
145
+ this.displayDuration = 3.5;
146
+ this.fadeDuration = 0.4;
147
+
148
+ // Glitch effect
149
+ this.glitchOffset = 0;
150
+ this.glitchTimer = 0;
151
+ }
152
+
153
+ /**
154
+ * Show a lore message (called when prism collected)
155
+ */
156
+ show(lore) {
157
+ this.currentLore = lore;
158
+ this.active = true;
159
+ this.displayTimer = 0;
160
+ this.glitchTimer = 0;
161
+ }
162
+
163
+ update(dt) {
164
+ if (!this.active) return;
165
+
166
+ this.displayTimer += dt;
167
+ this.glitchTimer += dt;
168
+
169
+ // Glitch effect
170
+ if (Math.random() < 0.08) {
171
+ this.glitchOffset = (Math.random() - 0.5) * 6;
172
+ } else {
173
+ this.glitchOffset *= 0.85;
174
+ }
175
+
176
+ // Fade out
177
+ if (this.displayTimer >= this.displayDuration + this.fadeDuration) {
178
+ this.active = false;
179
+ this.currentLore = null;
180
+ }
181
+ }
182
+
183
+ render(ctx, targetX = null, targetY = null) {
184
+ if (!this.active || !this.currentLore) return;
185
+
186
+ // Use target position (light cone tip) or fallback to center
187
+ const centerX = targetX !== null ? targetX : this.game.width / 2;
188
+ const centerY = targetY !== null ? targetY : 100;
189
+
190
+ // Calculate alpha
191
+ let alpha = 1;
192
+ if (this.displayTimer < this.fadeDuration) {
193
+ alpha = this.displayTimer / this.fadeDuration;
194
+ } else if (this.displayTimer >= this.displayDuration) {
195
+ alpha = 1 - ((this.displayTimer - this.displayDuration) / this.fadeDuration);
196
+ }
197
+
198
+ ctx.save();
199
+ ctx.globalAlpha = alpha;
200
+
201
+ const glitchX = this.glitchOffset;
202
+
203
+ // Background panel
204
+ const panelWidth = Math.min(450, this.game.width - 60);
205
+ const panelHeight = 60;
206
+
207
+ ctx.fillStyle = "rgba(0, 20, 30, 0.9)";
208
+ ctx.fillRect(
209
+ centerX - panelWidth / 2 + glitchX,
210
+ centerY - panelHeight / 2,
211
+ panelWidth,
212
+ panelHeight
213
+ );
214
+
215
+ // Border
216
+ ctx.strokeStyle = `rgba(100, 220, 255, ${0.7 + Math.sin(this.glitchTimer * 8) * 0.3})`;
217
+ ctx.lineWidth = 2;
218
+ ctx.strokeRect(
219
+ centerX - panelWidth / 2 + glitchX,
220
+ centerY - panelHeight / 2,
221
+ panelWidth,
222
+ panelHeight
223
+ );
224
+
225
+ // Corner accents
226
+ const cornerSize = 8;
227
+ ctx.fillStyle = "#4df";
228
+ // Top left
229
+ ctx.fillRect(centerX - panelWidth / 2 + glitchX, centerY - panelHeight / 2, cornerSize, 2);
230
+ ctx.fillRect(centerX - panelWidth / 2 + glitchX, centerY - panelHeight / 2, 2, cornerSize);
231
+ // Top right
232
+ ctx.fillRect(centerX + panelWidth / 2 - cornerSize + glitchX, centerY - panelHeight / 2, cornerSize, 2);
233
+ ctx.fillRect(centerX + panelWidth / 2 - 2 + glitchX, centerY - panelHeight / 2, 2, cornerSize);
234
+ // Bottom left
235
+ ctx.fillRect(centerX - panelWidth / 2 + glitchX, centerY + panelHeight / 2 - 2, cornerSize, 2);
236
+ ctx.fillRect(centerX - panelWidth / 2 + glitchX, centerY + panelHeight / 2 - cornerSize, 2, cornerSize);
237
+ // Bottom right
238
+ ctx.fillRect(centerX + panelWidth / 2 - cornerSize + glitchX, centerY + panelHeight / 2 - 2, cornerSize, 2);
239
+ ctx.fillRect(centerX + panelWidth / 2 - 2 + glitchX, centerY + panelHeight / 2 - cornerSize, 2, cornerSize);
240
+
241
+ // Main text
242
+ ctx.textAlign = "center";
243
+ ctx.textBaseline = "middle";
244
+
245
+ const hue = 185 + Math.sin(this.glitchTimer * 3) * 15;
246
+ ctx.fillStyle = `hsl(${hue}, 80%, 80%)`;
247
+ ctx.font = "italic 15px monospace";
248
+ ctx.fillText(`"${this.currentLore.text}"`, centerX + glitchX, centerY - 6);
249
+
250
+ // Source
251
+ ctx.font = "10px monospace";
252
+ ctx.fillStyle = `rgba(100, 200, 220, 0.7)`;
253
+ ctx.fillText(`— ${this.currentLore.source}`, centerX + glitchX, centerY + 18);
254
+
255
+ // Scan line
256
+ const scanY = (this.glitchTimer * 40) % panelHeight;
257
+ ctx.fillStyle = "rgba(100, 220, 255, 0.05)";
258
+ ctx.fillRect(
259
+ centerX - panelWidth / 2 + glitchX,
260
+ centerY - panelHeight / 2 + scanY,
261
+ panelWidth,
262
+ 2
263
+ );
264
+
265
+ ctx.restore();
266
+ }
267
+ }
268
+
269
+ // ============================================================================
270
+ // CONFIG ADDITIONS
271
+ // ============================================================================
272
+
273
+ export const LORE_CONFIG = {
274
+ spawnChance: 0.4, // 40% chance per spawn cycle
275
+ spawnInterval: 6.0, // Check every 6 seconds
276
+ kerrReward: 25, // Kerr energy gained
277
+ scoreReward: 500, // Score gained
278
+ };