# Creating New Modules ## 🎮 Game Module Template ### Basic Structure ```javascript import Module from '../core/Module.js'; class GameName extends Module { constructor(name, dependencies, config) { super(name, ['eventBus']); // Declare dependencies // Validate dependencies if (!dependencies.eventBus) { throw new Error('GameName requires EventBus dependency'); } this._eventBus = dependencies.eventBus; this._config = config; Object.seal(this); // Prevent modification } async init() { this._validateNotDestroyed(); // Set up event listeners this._eventBus.on('game:start', this._handleStart.bind(this), this.name); this._setInitialized(); } async destroy() { this._validateNotDestroyed(); // Cleanup: remove event listeners, DOM elements, timers this._eventBus.off('game:start', this._handleStart, this.name); this._setDestroyed(); } // Private methods _handleStart(event) { this._validateInitialized(); // Game logic here } } export default GameName; ``` ### Registration in Application.js ```javascript modules: [ { name: 'gameName', path: './games/GameName.js', dependencies: ['eventBus'], config: { difficulty: 'medium', scoreToWin: 100 } } ] ``` ## 📋 DRS Exercise Module Template ### Using DRSExerciseInterface ```javascript import DRSExerciseInterface from '../DRS/interfaces/DRSExerciseInterface.js'; class MyExercise extends DRSExerciseInterface { constructor() { super('MyExercise'); // Internal state this.score = 0; this.attempts = 0; this.startTime = null; this.container = null; } // ⚠️ REQUIRED - Initialize exercise async init(config, content) { this.config = config; this.content = content; this.startTime = Date.now(); // Validate content if (!content || !content.question) { throw new Error('MyExercise requires content with question'); } } // ⚠️ REQUIRED - Render UI async render(container) { this.container = container; container.innerHTML = `

${this.content.question}

`; // Event listeners container.querySelector('#submit-btn').addEventListener('click', () => { const answer = container.querySelector('#answer-input').value; this.handleUserInput('submit', { answer }); }); } // ⚠️ REQUIRED - Clean up async destroy() { if (this.container) { this.container.innerHTML = ''; } } // ⚠️ REQUIRED - Validate answer async validate(userAnswer) { this.attempts++; const isCorrect = userAnswer.toLowerCase() === this.content.correctAnswer.toLowerCase(); const score = isCorrect ? 100 - (this.attempts - 1) * 10 : 0; return { isCorrect, score: Math.max(score, 0), feedback: isCorrect ? 'Correct!' : 'Try again', explanation: `The correct answer is: ${this.content.correctAnswer}` }; } // ⚠️ REQUIRED - Get results getResults() { return { score: this.score, attempts: this.attempts, timeSpent: Date.now() - this.startTime, completed: this.score > 0, details: { question: this.content.question, correctAnswer: this.content.correctAnswer } }; } // ⚠️ REQUIRED - Handle user input handleUserInput(event, data) { if (event === 'submit') { this.validate(data.answer).then(result => { this.score = result.score; this.displayFeedback(result); }); } } // ⚠️ REQUIRED - Mark as completed async markCompleted(results) { // Save to progress system await window.app.getCore().progressTracker.markExerciseCompleted( 'my-exercise', this.content.id, results ); } // ⚠️ REQUIRED - Get progress getProgress() { return { percentage: this.score > 0 ? 100 : 0, currentStep: 1, totalSteps: 1, itemsCompleted: this.score > 0 ? 1 : 0, itemsTotal: 1 }; } // ⚠️ REQUIRED - Get exercise type getExerciseType() { return 'my-exercise'; } // ⚠️ REQUIRED - Get exercise config getExerciseConfig() { return { type: 'my-exercise', difficulty: this.config?.difficulty || 'medium', estimatedTime: 120, // seconds prerequisites: [], metadata: { hasAI: false, requiresInternet: false } }; } // Helper methods displayFeedback(result) { const feedbackDiv = this.container.querySelector('.feedback') || document.createElement('div'); feedbackDiv.className = 'feedback'; feedbackDiv.textContent = result.feedback; if (!this.container.querySelector('.feedback')) { this.container.appendChild(feedbackDiv); } } } export default MyExercise; ``` ## 📊 Progress Item Template ### Using ProgressItemInterface ```javascript import ProgressItemInterface from '../DRS/interfaces/ProgressItemInterface.js'; class MyCustomItem extends ProgressItemInterface { constructor(id, metadata) { super('my-custom-item', id, metadata); } // ⚠️ REQUIRED - Validate item data validate() { if (!this.metadata.requiredField) { throw new Error('MyCustomItem requires requiredField in metadata'); } return true; } // ⚠️ REQUIRED - Convert to JSON serialize() { return { ...this._getBaseSerialization(), customData: this.metadata.custom, timestamp: Date.now() }; } // ⚠️ REQUIRED - Return item weight getWeight() { return ProgressItemInterface.WEIGHTS['my-custom-item'] || 5; } // ⚠️ REQUIRED - Check prerequisites canComplete(userProgress) { // Check if user has completed prerequisites const prerequisite = this.metadata.prerequisite; if (prerequisite) { return userProgress.hasCompleted(prerequisite); } return true; } } export default MyCustomItem; ``` ## ✅ Checklist for New Modules ### For Game Modules - [ ] Extends `Module` base class - [ ] Validates dependencies in constructor - [ ] Uses `Object.seal(this)` at end of constructor - [ ] Implements `init()` and calls `_setInitialized()` - [ ] Implements `destroy()` and calls `_setDestroyed()` - [ ] Uses EventBus for all communication - [ ] No direct access to other modules - [ ] Registered in `Application.js` modules array ### For DRS Exercise Modules - [ ] Extends `DRSExerciseInterface` - [ ] Implements all 10 required methods - [ ] Validates content in `init()` - [ ] Cleans up in `destroy()` - [ ] Returns correct format from `validate()` - [ ] Integrates with progress system - [ ] Tested with ImplementationValidator ### For Progress Items - [ ] Extends `ProgressItemInterface` - [ ] Implements all 4 required methods - [ ] Validates data correctly - [ ] Returns proper weight - [ ] Checks prerequisites properly - [ ] Added to ImplementationValidator ## 🚨 Common Mistakes to Avoid 1. **Forgetting Object.seal()** - Module can be modified externally 2. **Not validating dependencies** - Module fails at runtime 3. **Direct module access** - Use EventBus instead 4. **Missing required methods** - Red screen error at startup 5. **Not cleaning up** - Memory leaks on destroy 6. **Hardcoded paths** - Use dynamic content loading 7. **Skipping ImplementationValidator** - Interface violations not caught ## 📚 Examples in Codebase - **Game Module**: `src/games/FlashcardLearning.js` - **DRS Exercise**: `src/DRS/exercise-modules/VocabularyModule.js` - **Progress Item**: `src/DRS/services/ProgressItemInterface.js` - **Validation**: `src/DRS/services/ImplementationValidator.js`