/** * 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; }