dopaminejs 1.2.0 → 2.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 (45) hide show
  1. package/dist/dopamine.js +1947 -0
  2. package/dist/dopamine.umd.cjs +45 -0
  3. package/package.json +24 -32
  4. package/src/core/Animator.js +62 -0
  5. package/src/core/Collider.js +50 -0
  6. package/src/core/Component.js +38 -0
  7. package/src/core/DopamineKernel.js +153 -0
  8. package/src/core/EventBus.js +183 -0
  9. package/src/core/Game.js +89 -0
  10. package/src/core/GameObject.js +83 -0
  11. package/src/core/PluginRegistry.js +197 -0
  12. package/src/core/Scene.js +65 -0
  13. package/src/core/Sprite.js +51 -0
  14. package/src/core/SystemRegistry.js +242 -0
  15. package/src/core/Vector2.js +50 -0
  16. package/src/{audio → dopamine/audio}/SoundManager.js +1 -3
  17. package/src/dopamine/components/ParticleEmitter.js +50 -0
  18. package/src/dopamine/components/ScreenShake.js +49 -0
  19. package/src/dopamine/components/index.js +4 -0
  20. package/src/dopamine/effects/WebGLParticleSystem.js +348 -0
  21. package/src/dopamine/index.js +62 -0
  22. package/src/dopamine/utils/TextureGenerator.js +90 -0
  23. package/src/index.js +34 -62
  24. package/src/interfaces/IAudioSystem.js +58 -0
  25. package/src/interfaces/IParticleSystem.js +51 -0
  26. package/src/interfaces/IPhysicsSystem.js +52 -0
  27. package/src/interfaces/ISystem.js +48 -0
  28. package/src/interfaces/index.js +7 -0
  29. package/src/renderer/Renderer.js +37 -0
  30. package/src/systems/Director.js +27 -0
  31. package/src/systems/Input.js +77 -0
  32. package/src/systems/Loader.js +44 -0
  33. package/src/systems/Physics.js +117 -0
  34. package/src/systems/Ticker.js +61 -0
  35. package/ARCHITECTURE.md +0 -71
  36. package/CHANGELOG.md +0 -36
  37. package/README.md +0 -264
  38. package/dist/dopamine.mjs +0 -737
  39. package/dist/dopamine.umd.js +0 -45
  40. /package/src/{core → dopamine/core}/DataService.js +0 -0
  41. /package/src/{core → dopamine/core}/EventEmitter.js +0 -0
  42. /package/src/{core → dopamine/core}/RewardSystem.js +0 -0
  43. /package/src/{effects → dopamine/effects}/ParticleSystem.js +0 -0
  44. /package/src/{ui → dopamine/ui}/GameUI.js +0 -0
  45. /package/src/{ui → dopamine/ui}/dopamine.css +0 -0
@@ -0,0 +1,51 @@
1
+ import { ISystem } from './ISystem.js';
2
+
3
+ /**
4
+ * IParticleSystem - Interface for particle system implementations
5
+ *
6
+ * Allows different renderers (Canvas, WebGL, custom) to be swapped
7
+ */
8
+ export const IParticleSystem = {
9
+ ...ISystem,
10
+
11
+ /**
12
+ * Register a sprite for particles
13
+ * @param {string} key - Sprite identifier
14
+ * @param {string} url - Path to image
15
+ */
16
+ registerSprite(key, url) { },
17
+
18
+ /**
19
+ * Register a custom effect
20
+ * @param {string} name - Effect name
21
+ * @param {Function} callback - Effect function (x, y, ...args) => void
22
+ */
23
+ registerEffect(name, callback) { },
24
+
25
+ /**
26
+ * Play a registered effect
27
+ * @param {string} name - Effect name
28
+ * @param {number} x - X position
29
+ * @param {number} y - Y position
30
+ * @param {...any} args - Additional arguments
31
+ */
32
+ play(name, x, y, ...args) { },
33
+
34
+ /**
35
+ * Emit particles with custom configuration
36
+ * @param {Object} config - Particle configuration
37
+ */
38
+ emit(config) { },
39
+
40
+ /**
41
+ * Clear all particles
42
+ */
43
+ clear() { },
44
+
45
+ // Built-in effects (optional, but recommended)
46
+ confetti(x, y, count) { },
47
+ coinShower(x, y, count) { },
48
+ sparkle(x, y, count, color) { },
49
+ fire(x, y, count) { },
50
+ starBurst(x, y, count) { },
51
+ };
@@ -0,0 +1,52 @@
1
+ import { ISystem } from './ISystem.js';
2
+
3
+ /**
4
+ * IPhysicsSystem - Interface for physics implementations
5
+ *
6
+ * Allows different physics engines (custom, Matter.js, Box2D) to be swapped
7
+ */
8
+ export const IPhysicsSystem = {
9
+ ...ISystem,
10
+
11
+ /**
12
+ * Add a physics body to the simulation
13
+ * @param {Object} body - Physics body (implementation-specific)
14
+ */
15
+ addBody(body) { },
16
+
17
+ /**
18
+ * Remove a physics body from the simulation
19
+ * @param {Object} body - Physics body to remove
20
+ */
21
+ removeBody(body) { },
22
+
23
+ /**
24
+ * Check collision between two bodies
25
+ * @param {Object} a - First body
26
+ * @param {Object} b - Second body
27
+ * @returns {boolean} - True if colliding
28
+ */
29
+ checkCollision(a, b) { },
30
+
31
+ /**
32
+ * Perform a raycast
33
+ * @param {Object} origin - Ray origin {x, y}
34
+ * @param {Object} direction - Ray direction {x, y}
35
+ * @param {number} distance - Maximum ray distance
36
+ * @returns {Object|null} - Hit result or null
37
+ */
38
+ raycast(origin, direction, distance) { },
39
+
40
+ /**
41
+ * Set gravity for the physics world
42
+ * @param {number} x - Gravity X component
43
+ * @param {number} y - Gravity Y component
44
+ */
45
+ setGravity(x, y) { },
46
+
47
+ /**
48
+ * Step the physics simulation
49
+ * @param {number} dt - Delta time
50
+ */
51
+ step(dt) { },
52
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ISystem - Base interface for all systems
3
+ *
4
+ * All systems should implement this interface.
5
+ * Methods are optional - only implement what you need.
6
+ */
7
+ export const ISystem = {
8
+ /**
9
+ * Called when system is registered to the kernel
10
+ * @param {DopamineKernel} kernel - The kernel instance
11
+ */
12
+ init(kernel) { },
13
+
14
+ /**
15
+ * Called each frame (variable timestep)
16
+ * @param {number} dt - Delta time in seconds
17
+ */
18
+ update(dt) { },
19
+
20
+ /**
21
+ * Called at fixed timestep (for physics)
22
+ * @param {number} dt - Fixed delta time
23
+ */
24
+ fixedUpdate(dt) { },
25
+
26
+ /**
27
+ * Called when system is unregistered
28
+ */
29
+ destroy() { },
30
+ };
31
+
32
+ /**
33
+ * Base System class that implements ISystem
34
+ * Systems can extend this for convenience
35
+ */
36
+ export class System {
37
+ constructor() {
38
+ this.kernel = null;
39
+ }
40
+
41
+ init(kernel) {
42
+ this.kernel = kernel;
43
+ }
44
+
45
+ update(dt) { }
46
+ fixedUpdate(dt) { }
47
+ destroy() { }
48
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Export all system interfaces
3
+ */
4
+ export { ISystem, System } from './ISystem.js';
5
+ export { IPhysicsSystem } from './IPhysicsSystem.js';
6
+ export { IAudioSystem } from './IAudioSystem.js';
7
+ export { IParticleSystem } from './IParticleSystem.js';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Handles the Canvas and Context for rendering.
3
+ */
4
+ export class Renderer {
5
+ constructor(options = {}) {
6
+ this.width = options.width || 800;
7
+ this.height = options.height || 600;
8
+ this.backgroundColor = options.backgroundColor || '#000000';
9
+
10
+ this.canvas = document.createElement('canvas');
11
+ this.canvas.width = this.width;
12
+ this.canvas.height = this.height;
13
+ this.ctx = this.canvas.getContext('2d');
14
+
15
+ // Append to body or specified container
16
+ const parent = options.parent ?
17
+ (typeof options.parent === 'string' ? document.querySelector(options.parent) : options.parent)
18
+ : document.body;
19
+
20
+ parent.appendChild(this.canvas);
21
+ }
22
+
23
+ /**
24
+ * Clear the canvas.
25
+ */
26
+ clear() {
27
+ this.ctx.fillStyle = this.backgroundColor;
28
+ this.ctx.fillRect(0, 0, this.width, this.height);
29
+ }
30
+
31
+ resize(width, height) {
32
+ this.width = width;
33
+ this.height = height;
34
+ this.canvas.width = width;
35
+ this.canvas.height = height;
36
+ }
37
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Manages Scene transitions and state.
3
+ */
4
+ export class Director {
5
+ constructor(game) {
6
+ this.game = game;
7
+ this.currentScene = null;
8
+ }
9
+
10
+ /**
11
+ * Switch to a new scene.
12
+ * @param {Scene} scene
13
+ */
14
+ run(scene) {
15
+ if (this.currentScene) {
16
+ this.currentScene.onExit();
17
+ }
18
+
19
+ this.currentScene = scene;
20
+ this.currentScene.game = this.game;
21
+
22
+ // Let the game know (if game engine needs to reference it directly)
23
+ this.game.scene = scene;
24
+
25
+ scene.onEnter();
26
+ }
27
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Input System
3
+ * Tracks keyboard and mouse input.
4
+ */
5
+ export class Input {
6
+ constructor() {
7
+ this.keys = new Map();
8
+ this.mouse = { x: 0, y: 0, buttons: new Map() };
9
+ this.kernel = null;
10
+
11
+ // Bind event listeners
12
+ this._onKeyDown = this._onKeyDown.bind(this);
13
+ this._onKeyUp = this._onKeyUp.bind(this);
14
+ this._onMouseMove = this._onMouseMove.bind(this);
15
+ this._onMouseDown = this._onMouseDown.bind(this);
16
+ this._onMouseUp = this._onMouseUp.bind(this);
17
+ }
18
+
19
+ /**
20
+ * ISystem interface - called when registered
21
+ */
22
+ init(kernel) {
23
+ this.kernel = kernel;
24
+
25
+ // Attach event listeners
26
+ window.addEventListener('keydown', this._onKeyDown);
27
+ window.addEventListener('keyup', this._onKeyUp);
28
+ window.addEventListener('mousemove', this._onMouseMove);
29
+ window.addEventListener('mousedown', this._onMouseDown);
30
+ window.addEventListener('mouseup', this._onMouseUp);
31
+ }
32
+
33
+ /**
34
+ * ISystem interface - cleanup
35
+ */
36
+ destroy() {
37
+ window.removeEventListener('keydown', this._onKeyDown);
38
+ window.removeEventListener('keyup', this._onKeyUp);
39
+ window.removeEventListener('mousemove', this._onMouseMove);
40
+ window.removeEventListener('mousedown', this._onMouseDown);
41
+ window.removeEventListener('mouseup', this._onMouseUp);
42
+ }
43
+
44
+ _onKeyDown(e) {
45
+ this.keys.set(e.key, true);
46
+ }
47
+
48
+ _onKeyUp(e) {
49
+ this.keys.set(e.key, false);
50
+ }
51
+
52
+ _onMouseMove(e) {
53
+ this.mouse.x = e.clientX;
54
+ this.mouse.y = e.clientY;
55
+ }
56
+
57
+ _onMouseDown(e) {
58
+ this.mouse.buttons.set(e.button, true);
59
+ }
60
+
61
+ _onMouseUp(e) {
62
+ this.mouse.buttons.set(e.button, false);
63
+ }
64
+
65
+ isKeyDown(key) {
66
+ return this.keys.get(key) || false;
67
+ }
68
+
69
+ isMouseButtonDown(button = 0) {
70
+ return this.mouse.buttons.get(button) || false;
71
+ }
72
+ }
73
+
74
+ // DEPRECATED: Keep for backward compatibility
75
+ export const GlobalInput = new Input();
76
+ // Note: GlobalInput won't have listeners attached unless init() is called
77
+ console.warn('[DopamineJS] GlobalInput is deprecated. Use kernel.input instead.');
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Asset Loader
3
+ * Loads and caches images, audio, etc.
4
+ */
5
+ export class Loader {
6
+ constructor() {
7
+ this.assets = new Map();
8
+ this.kernel = null;
9
+ }
10
+
11
+ /**
12
+ * ISystem interface - called when registered
13
+ */
14
+ init(kernel) {
15
+ this.kernel = kernel;
16
+ }
17
+
18
+ /**
19
+ * ISystem interface - cleanup
20
+ */
21
+ destroy() {
22
+ this.assets.clear();
23
+ }
24
+
25
+ async loadImage(key, url) {
26
+ return new Promise((resolve, reject) => {
27
+ const img = new Image();
28
+ img.onload = () => {
29
+ this.assets.set(key, img);
30
+ resolve(img);
31
+ };
32
+ img.onerror = reject;
33
+ img.src = url;
34
+ });
35
+ }
36
+
37
+ get(key) {
38
+ return this.assets.get(key);
39
+ }
40
+ }
41
+
42
+ // DEPRECATED: Keep for backward compatibility
43
+ export const GlobalLoader = new Loader();
44
+ console.warn('[DopamineJS] GlobalLoader is deprecated. Use kernel.loader instead.');
@@ -0,0 +1,117 @@
1
+ import { Collider } from '../core/Collider.js';
2
+
3
+ export class Physics {
4
+ constructor() {
5
+ this.colliders = [];
6
+ this.kernel = null;
7
+ }
8
+
9
+ /**
10
+ * ISystem interface - called when registered
11
+ */
12
+ init(kernel) {
13
+ this.kernel = kernel;
14
+ }
15
+
16
+ /**
17
+ * ISystem interface - called at fixed timestep
18
+ */
19
+ fixedUpdate(dt) {
20
+ this.step();
21
+ }
22
+
23
+ /**
24
+ * ISystem interface - cleanup
25
+ */
26
+ destroy() {
27
+ this.colliders = [];
28
+ }
29
+
30
+ add(collider) {
31
+ this.colliders.push(collider);
32
+ }
33
+
34
+ remove(collider) {
35
+ const idx = this.colliders.indexOf(collider);
36
+ if (idx > -1) this.colliders.splice(idx, 1);
37
+ }
38
+
39
+ /**
40
+ * Main Physics Step.
41
+ * Checks all pairs and triggers callbacks.
42
+ */
43
+ step() {
44
+ // Naive O(N^2)
45
+ for (let i = 0; i < this.colliders.length; i++) {
46
+ for (let j = i + 1; j < this.colliders.length; j++) {
47
+ const a = this.colliders[i];
48
+ const b = this.colliders[j];
49
+
50
+ if (this._intersects(a, a.getBounds(), b, b.getBounds())) {
51
+ // Notify GameObjects
52
+ this._notifyCollision(a.gameObject, b.gameObject);
53
+ this._notifyCollision(b.gameObject, a.gameObject);
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ _notifyCollision(go, other) {
60
+ if (!go || !go.components) return;
61
+ go.components.forEach(c => {
62
+ if (c.onCollisionEnter) c.onCollisionEnter(other);
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Check if a specific collider overlaps with anything in the target group (tag).
68
+ * @param {Collider} source
69
+ * @param {string} targetTag
70
+ */
71
+ checkOverlap(source, targetTag) {
72
+ // ... (Keep existing manual check for flexibility)
73
+ const hits = [];
74
+ const sourceBounds = source.getBounds();
75
+
76
+ for (const target of this.colliders) {
77
+ if (target === source) continue;
78
+ if (targetTag && target.tag !== targetTag) continue;
79
+
80
+ const targetBounds = target.getBounds();
81
+
82
+ if (this._intersects(source, sourceBounds, target, targetBounds)) {
83
+ hits.push(target);
84
+ }
85
+ }
86
+ return hits;
87
+ }
88
+
89
+ _intersects(a, bA, b, bB) {
90
+ // Box vs Box
91
+ if (a.type === 'box' && b.type === 'box') {
92
+ return (bA.left < bB.right && bA.right > bB.left &&
93
+ bA.top < bB.bottom && bA.bottom > bB.top);
94
+ }
95
+ // Circle vs Circle
96
+ if (a.type === 'circle' && b.type === 'circle') {
97
+ const dx = bA.x - bB.x;
98
+ const dy = bA.y - bB.y;
99
+ const dist = Math.sqrt(dx * dx + dy * dy);
100
+ return dist < (bA.radius + bB.radius);
101
+ }
102
+
103
+ // TODO: Box vs Circle
104
+ return false;
105
+ }
106
+
107
+ // IPhysicsSystem interface methods (for plugin compatibility)
108
+ addBody(body) { this.add(body); }
109
+ removeBody(body) { this.remove(body); }
110
+ checkCollision(a, b) { return this._intersects(a, a.getBounds(), b, b.getBounds()); }
111
+ raycast(origin, direction, distance) { /* TODO */ return null; }
112
+ setGravity(x, y) { /* TODO: Add gravity support */ }
113
+ }
114
+
115
+ // DEPRECATED: Keep for backward compatibility, but log warning
116
+ export const GlobalPhysics = new Physics();
117
+ console.warn('[DopamineJS] GlobalPhysics is deprecated. Use kernel.physics instead.');
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The Ticker class manages the game loop.
3
+ * It uses requestAnimationFrame to provide a smooth loop.
4
+ */
5
+ export class Ticker {
6
+ constructor() {
7
+ this.running = false;
8
+ this.lastTime = 0;
9
+ this.callbacks = new Set();
10
+ this._tick = this._tick.bind(this);
11
+ }
12
+
13
+ /**
14
+ * Add a callback to the loop.
15
+ * @param {Function} callback - Function to call each frame (receives dt).
16
+ */
17
+ add(callback) {
18
+ this.callbacks.add(callback);
19
+ }
20
+
21
+ /**
22
+ * Remove a callback from the loop.
23
+ * @param {Function} callback
24
+ */
25
+ remove(callback) {
26
+ this.callbacks.delete(callback);
27
+ }
28
+
29
+ /**
30
+ * Start the loop.
31
+ */
32
+ start() {
33
+ if (this.running) return;
34
+ this.running = true;
35
+ this.lastTime = performance.now();
36
+ requestAnimationFrame(this._tick);
37
+ }
38
+
39
+ /**
40
+ * Stop the loop.
41
+ */
42
+ stop() {
43
+ this.running = false;
44
+ }
45
+
46
+ _tick(time) {
47
+ if (!this.running) return;
48
+
49
+ const dt = (time - this.lastTime) / 1000; // Delta time in seconds
50
+ this.lastTime = time;
51
+
52
+ // Cap dt to prevent huge jumps if tab was inactive
53
+ const safeDt = Math.min(dt, 0.1);
54
+
55
+ for (const callback of this.callbacks) {
56
+ callback(safeDt);
57
+ }
58
+
59
+ requestAnimationFrame(this._tick);
60
+ }
61
+ }
package/ARCHITECTURE.md DELETED
@@ -1,71 +0,0 @@
1
- # DopamineJS Architecture & Roadmap 🏗️
2
-
3
- **Vision**: To become the standard "Game Feel Engine" for the web—a plug-and-play layer that handles the "juice" (feedback, rewards, satisfaction) so developers can focus on core mechanics.
4
-
5
- ## 🧠 Design Philosophy
6
- 1. **Zero-Config Start**: It must work beautifully out of the box.
7
- 2. **Infinite Extensibility**: Every default (sound, particle, UI style) must be replaceable.
8
- 3. **Asset Agnostic**: Support synthesized sounds/shapes (no assets) AND custom assets (mp3/png).
9
- 4. **AI-First**: APIs should be self-documenting and predictable for AI agents to use effectively.
10
-
11
- ## 🗺️ Roadmap
12
-
13
- ### Phase 1: Audio Extensibility (v1.1.0)
14
- **Goal**: Allow developers to bring their own soundscapes.
15
-
16
- - **[NEW] Asset Loader**: A lightweight preloader for audio files.
17
- - **[UPDATE] SoundManager**:
18
- - Add `registerSound(key, url)` method.
19
- - Support `Howler.js` integration (optional) or robust Web Audio buffer caching.
20
- - **Feature**: "Sound Packs" - Allow switching between 'Retro', 'Modern', 'Cute' preset packs.
21
-
22
- ### Phase 2: Visual Customization (v1.2.0)
23
- **Goal**: Break free from geometric primitives.
24
-
25
- - **[UPDATE] ParticleSystem**:
26
- - **Sprite Support**: Allow `image` or `sprite` properties in particle config.
27
- - **Custom Emitters**: Define custom particle behaviors (gravity, velocity, life, decay) via JSON config.
28
- - **Editor**: (Long term) A web-based particle editor that exports JSON for DopamineJS.
29
-
30
- ### Phase 3: UI Theming & Templates (v1.3.0)
31
- **Goal**: Make the UI fit any game art style.
32
-
33
- - **[NEW] Theme Engine**:
34
- - Use CSS Variables for all colors, fonts, and spacing.
35
- - `Dopamine.setTheme('dark-cyberpunk')`
36
- - **[UPDATE] GameUI**:
37
- - **Icon Sets**: Allow passing an icon map (SVG strings or URLs) to replace default emojis.
38
- - **Slots**: Allow developers to inject custom HTML into notifications or level-up screens.
39
-
40
- ### Phase 4: The "Dopamine Ecosystem" (v2.0.0)
41
- **Goal**: Community-driven content.
42
-
43
- - **Plugin System**: Middleware for the `RewardSystem` (e.g., "BattlePass Plugin", "Leaderboard Plugin").
44
- - **Backend Integration**: Webhooks for `onLevelUp` or `onAchievement` to validate rewards on a server.
45
-
46
- ## 📝 Developer Guide: How to Extend (Current Best Practices)
47
-
48
- While we build these features, here is how you can extend DopamineJS today:
49
-
50
- ### Custom Achievements
51
- You can already define any achievement logic you want:
52
-
53
- ```javascript
54
- achievements: {
55
- 'die_100_times': {
56
- name: 'You Tried',
57
- icon: '<img src="skull.png" width="20">', // HTML is supported in icons!
58
- check: (p) => p.stats.deaths >= 100
59
- }
60
- }
61
- ```
62
-
63
- ### Custom CSS
64
- Override the default styles by adding your own CSS *after* importing DopamineJS:
65
-
66
- ```css
67
- /* Override the XP bar color */
68
- .xp-bar-fill {
69
- background: linear-gradient(90deg, #ff00cc, #3333ff) !important;
70
- }
71
- ```
package/CHANGELOG.md DELETED
@@ -1,36 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- ## [1.2.0] - 2025-12-04
6
- ### Added
7
- - **Visual Customization**:
8
- - `registerSprite(key, url)`: Use custom images for particles.
9
- - `emit(config)`: Create fully custom particle explosions.
10
- - `registerEffect(name, callback)`: Define reusable custom effects.
11
- - **Multi-Screen Support**: Pass `container` selector to `ParticleSystem` to target specific elements.
12
- - **Optimization**: Implemented Object Pooling to reduce garbage collection and improve performance.
13
-
14
- ## [1.1.0] - 2025-12-04
15
- ### Added
16
- - **Audio Extensibility**: Support for custom audio files (`.mp3`, `.wav`).
17
- - `SoundManager.registerSound(key, url)`: Register custom assets.
18
- - `SoundManager.play(key)`: Unified API to play custom sounds or fallback to synth.
19
- - Support for `customSounds` in the initial configuration.
20
-
21
- ## [1.0.2] - 2025-12-04
22
- ### Fixed
23
- - Fixed `npm run release` script failing due to missing `vitest` dependency.
24
- - Uncommented `npm publish` in deployment script to ensure package is actually published to npm.
25
-
26
- ## [1.0.1] - 2025-12-04
27
- ### Added
28
- - Initial release of DopamineJS.
29
- - Core systems: `RewardSystem`, `ParticleSystem`, `SoundManager`, `GameUI`.
30
- - Basic gamification features: XP, Levels, Streaks, Achievements.
31
- - Built-in visual effects: Confetti, Coin Shower, Sparkles, Fire, Star Burst.
32
- - Synthesized sound effects.
33
- - Comprehensive README with "Vibe Coding" instructions for AI agents.
34
-
35
- ## [1.0.0] - 2025-12-04
36
- - Initial scaffold.