Spaces:
Sleeping
Sleeping
| /** | |
| * HintManager - Context-aware hint system for RTS game | |
| * Shows helpful hints at the right moment with smooth animations | |
| */ | |
| class HintManager { | |
| constructor() { | |
| this.hintQueue = []; | |
| this.currentHint = null; | |
| this.shownHints = new Set(); | |
| this.enabled = true; | |
| this.hintElement = null; | |
| this.translations = {}; | |
| this.initializeHintElement(); | |
| this.loadHintHistory(); | |
| } | |
| initializeHintElement() { | |
| // Create hint container | |
| this.hintElement = document.createElement('div'); | |
| this.hintElement.id = 'hint-display'; | |
| this.hintElement.className = 'hint-container hidden'; | |
| // Add to body | |
| document.body.appendChild(this.hintElement); | |
| console.log('[HintManager] Initialized'); | |
| } | |
| loadHintHistory() { | |
| // Load previously shown hints from localStorage | |
| try { | |
| const history = localStorage.getItem('rts_shown_hints'); | |
| if (history) { | |
| this.shownHints = new Set(JSON.parse(history)); | |
| console.log(`[HintManager] Loaded ${this.shownHints.size} shown hints from history`); | |
| } | |
| } catch (error) { | |
| console.warn('[HintManager] Failed to load hint history:', error); | |
| } | |
| } | |
| saveHintHistory() { | |
| // Save shown hints to localStorage | |
| try { | |
| localStorage.setItem('rts_shown_hints', JSON.stringify([...this.shownHints])); | |
| } catch (error) { | |
| console.warn('[HintManager] Failed to save hint history:', error); | |
| } | |
| } | |
| setTranslations(translations) { | |
| this.translations = translations; | |
| } | |
| translate(key) { | |
| return this.translations[key] || key; | |
| } | |
| showHint(hintKey, options = {}) { | |
| if (!this.enabled) return; | |
| // Check if hint was already shown (unless force is true) | |
| if (!options.force && this.shownHints.has(hintKey)) { | |
| return; | |
| } | |
| // Default options | |
| const config = { | |
| duration: 3000, // 3 seconds | |
| priority: 0, // Higher priority = shown first | |
| force: false, // Show even if already shown | |
| position: 'center', // 'center', 'top', 'bottom' | |
| ...options | |
| }; | |
| // Add to queue with priority | |
| this.hintQueue.push({ key: hintKey, config }); | |
| this.hintQueue.sort((a, b) => b.config.priority - a.config.priority); | |
| // Mark as shown | |
| this.shownHints.add(hintKey); | |
| this.saveHintHistory(); | |
| // Show if no current hint | |
| if (!this.currentHint) { | |
| this.displayNext(); | |
| } | |
| } | |
| displayNext() { | |
| if (this.hintQueue.length === 0) { | |
| this.currentHint = null; | |
| return; | |
| } | |
| const { key, config } = this.hintQueue.shift(); | |
| this.currentHint = { key, config }; | |
| // Get translated text | |
| const text = this.translate(key); | |
| // Update hint element | |
| this.hintElement.textContent = text; | |
| this.hintElement.className = `hint-container ${config.position}`; | |
| // Fade in | |
| setTimeout(() => { | |
| this.hintElement.classList.add('visible'); | |
| }, 50); | |
| // Auto-hide after duration | |
| setTimeout(() => { | |
| this.hide(); | |
| }, config.duration); | |
| console.log(`[HintManager] Showing hint: ${key}`); | |
| } | |
| hide() { | |
| if (!this.hintElement) return; | |
| // Fade out | |
| this.hintElement.classList.remove('visible'); | |
| // Wait for animation, then show next | |
| setTimeout(() => { | |
| this.displayNext(); | |
| }, 500); // Match CSS transition duration | |
| } | |
| enable() { | |
| this.enabled = true; | |
| console.log('[HintManager] Hints enabled'); | |
| } | |
| disable() { | |
| this.enabled = false; | |
| this.hide(); | |
| console.log('[HintManager] Hints disabled'); | |
| } | |
| toggle() { | |
| this.enabled = !this.enabled; | |
| if (!this.enabled) { | |
| this.hide(); | |
| } | |
| return this.enabled; | |
| } | |
| reset() { | |
| // Clear history and show all hints again | |
| this.shownHints.clear(); | |
| this.saveHintHistory(); | |
| console.log('[HintManager] Hint history reset'); | |
| } | |
| // Contextual hint triggers | |
| onGameStart() { | |
| this.showHint('hint.game.welcome', { priority: 10, duration: 4000 }); | |
| setTimeout(() => { | |
| this.showHint('hint.game.build_barracks', { priority: 9, duration: 4000 }); | |
| }, 4500); | |
| } | |
| onFirstUnitSelected() { | |
| this.showHint('hint.controls.movement', { priority: 8 }); | |
| } | |
| onFirstBuildingPlaced() { | |
| this.showHint('hint.game.train_units', { priority: 7 }); | |
| } | |
| onMultipleUnitsSelected(count) { | |
| if (count >= 3) { | |
| this.showHint('hint.controls.control_groups', { priority: 6 }); | |
| } | |
| } | |
| onLowCredits() { | |
| this.showHint('hint.game.low_credits', { priority: 5 }); | |
| } | |
| onLowPower() { | |
| this.showHint('hint.game.low_power', { priority: 5 }); | |
| } | |
| onFirstCombat() { | |
| this.showHint('hint.combat.attack_move', { priority: 7 }); | |
| } | |
| onControlGroupAssigned() { | |
| this.showHint('hint.controls.control_groups_select', { priority: 6, duration: 4000 }); | |
| } | |
| onCameraKeyboard() { | |
| this.showHint('hint.controls.camera_keyboard', { priority: 4 }); | |
| } | |
| onLanguageChanged() { | |
| this.showHint('hint.ui.language_changed', { priority: 8, duration: 2000 }); | |
| } | |
| } | |
| // Export for use in game.js | |
| if (typeof module !== 'undefined' && module.exports) { | |
| module.exports = HintManager; | |
| } | |