Add comprehensive Chinese reading course (乐读) with 4 chapters of vocabulary, texts, and exercises. Include architecture documentation for module development and progress tracking system. Content: - LEDU book metadata with 12 chapter outline - Chapter 1: Food culture (民以食为天) - 45+ vocabulary, etiquette - Chapter 2: Shopping (货比三家) - comparative shopping vocabulary - Chapter 3: Sports & fitness (生命在于运动) - exercise habits - Chapter 4: Additional vocabulary and grammar Documentation: - Architecture principles and patterns - Module creation guide (Game, DRS, Progress) - Interface system (C++ style contracts) - Progress tracking and prerequisites Game Enhancements: - MarioEducational helper classes (Physics, Renderer, Sound, Enemies) - VocabularyModule TTS improvements - Updated CLAUDE.md with project status 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
313 lines
8.4 KiB
Markdown
313 lines
8.4 KiB
Markdown
# 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 = `
|
|
<div class="exercise-container">
|
|
<h2>${this.content.question}</h2>
|
|
<input type="text" id="answer-input" />
|
|
<button id="submit-btn">Submit</button>
|
|
</div>
|
|
`;
|
|
|
|
// 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`
|