/
VictoriaKoltsova
/
AI_2026_Module2_Project2
Обзор
Документация
Войти
/
VictoriaKoltsova
/
AI_2026_Module2_Project2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
js/storage.js
237 строк
10 KB
moskoltsova2009-ai
Build UK Quest game platform
09 авг 2026, 23:28
09 авг 2026, 23:28
54234c0
Код
Авторство
О чём код?
(() => { const STORAGE_KEY = "ukQuestProgressV1"; const initialState = { totalXP: 0, coins: 120, completedLevels: [], unlockedLevel: 1, unlockedCities: ["London"], foundTargetWords: {}, foundBonusWords: {}, hintedCells: {}, discoveredVocabulary: [], unlockedAchievements: [], perfectLevels: [], levelsUsingHints: [], soundEnabled: true, theme: "dark", achievementRewardsClaimed: [], unlockedPerks: [], towerEscape: { started: false, introCompleted: false, completed: false, currentLocation: "guard-room", completedLocations: [], inventory: [], clues: [], completedPuzzles: [], runSecrets: [], historicalSecrets: [], hintsUsed: 0, transcriptUsed: false, runStartTime: null, rewardedActions: [], grammar: { presentPassiveMistakes: 0, pastPassiveMistakes: 0, futurePassiveMistakes: 0, mixedPassiveMistakes: 0 }, }, roadRush: { currentCity: "bristol", unlockedCities: ["bristol"], completedCities: [], currentRace: null, bestScores: {}, englishLives: 3, englishStreak: 0, bestEnglishStreak: 0, wrongAnswers: [], selectedSkin: "street-blue", unlockedSkins: ["street-blue"], achievements: [], correctGrammarTypes: { presentSimple: false, presentContinuous: false, pastSimple: false, pastContinuous: false, presentPerfect: false, pastPerfect: false, }, totalCollisions: 0, totalDodged: 0, cleanDodged: 0, totalEnglishCorrect: 0, totalEnglishAnswered: 0, languageBoostsUsed: 0, tourCompleted: false, rewardedCities: [], lastQuestionIds: {}, }, }; let memoryState = structuredClone(initialState); function sanitizeState(value) { const safe = value && typeof value === "object" ? value : {}; const levels = window.UKQuestData?.LEVELS || []; const cities = window.UKQuestData?.CITIES || []; const levelIds = new Set(levels.map((level) => level.id)); const completedLevels = [...new Set(Array.isArray(safe.completedLevels) ? safe.completedLevels.filter((id) => levelIds.has(id)) : [])]; const foundTargetWords = safe.foundTargetWords || {}; const dictionary = window.UKQuestData?.VOCABULARY || {}; const previouslyDiscovered = Array.isArray(safe.discoveredVocabulary) ? safe.discoveredVocabulary : []; const discoveredFromProgress = Object.values(foundTargetWords) .filter(Array.isArray) .flat() .filter((word) => dictionary[word]); const discoveredVocabulary = [...new Set([...previouslyDiscovered, ...discoveredFromProgress])]; const cityAchievementIds = new Set(cities.map((city) => city.achievement)); const unlockedAchievements = Array.isArray(safe.unlockedAchievements) ? [...new Set(safe.unlockedAchievements.filter((id) => !cityAchievementIds.has(id) && id !== "UK_EXPLORER"))] : []; if (discoveredVocabulary.length >= 25 && !unlockedAchievements.includes("WORD_HUNTER")) { unlockedAchievements.push("WORD_HUNTER"); } cities.forEach((city) => { if (city.levels.every((id) => completedLevels.includes(id))) unlockedAchievements.push(city.achievement); }); if (levels.length && completedLevels.length === levels.length) unlockedAchievements.push("UK_EXPLORER"); const highestCompleted = completedLevels.length ? Math.max(...completedLevels) : 0; const unlockedLevel = Math.min(levels.length || 1, Math.max(1, Number(safe.unlockedLevel) || 1, highestCompleted + 1)); const unlockedCities = cities.filter((city) => city.firstLevel <= unlockedLevel).map((city) => city.name); const towerSafe = safe.towerEscape && typeof safe.towerEscape === "object" ? safe.towerEscape : {}; const towerEscape = { ...structuredClone(initialState.towerEscape), ...towerSafe, completedLocations: Array.isArray(towerSafe.completedLocations) ? towerSafe.completedLocations : [], inventory: Array.isArray(towerSafe.inventory) ? towerSafe.inventory : [], clues: Array.isArray(towerSafe.clues) ? towerSafe.clues : [], completedPuzzles: Array.isArray(towerSafe.completedPuzzles) ? towerSafe.completedPuzzles : [], runSecrets: Array.isArray(towerSafe.runSecrets) ? towerSafe.runSecrets : [], historicalSecrets: Array.isArray(towerSafe.historicalSecrets) ? towerSafe.historicalSecrets : [], rewardedActions: Array.isArray(towerSafe.rewardedActions) ? towerSafe.rewardedActions : [], grammar: { ...structuredClone(initialState.towerEscape.grammar), ...(towerSafe.grammar || {}) }, }; const roadSafe = safe.roadRush && typeof safe.roadRush === "object" ? safe.roadRush : {}; const roadRush = { ...structuredClone(initialState.roadRush), ...roadSafe, unlockedCities: Array.isArray(roadSafe.unlockedCities) && roadSafe.unlockedCities.length ? [...new Set(roadSafe.unlockedCities)] : ["bristol"], completedCities: Array.isArray(roadSafe.completedCities) ? [...new Set(roadSafe.completedCities)] : [], bestScores: roadSafe.bestScores && typeof roadSafe.bestScores === "object" ? roadSafe.bestScores : {}, englishLives: Math.max(0, Math.min(3, Number.isFinite(Number(roadSafe.englishLives)) ? Number(roadSafe.englishLives) : 3)), wrongAnswers: Array.isArray(roadSafe.wrongAnswers) ? roadSafe.wrongAnswers.slice(-30) : [], unlockedSkins: Array.isArray(roadSafe.unlockedSkins) && roadSafe.unlockedSkins.length ? [...new Set(roadSafe.unlockedSkins)] : ["street-blue"], achievements: Array.isArray(roadSafe.achievements) ? [...new Set(roadSafe.achievements)] : [], correctGrammarTypes: { ...structuredClone(initialState.roadRush.correctGrammarTypes), ...(roadSafe.correctGrammarTypes || {}) }, rewardedCities: Array.isArray(roadSafe.rewardedCities) ? [...new Set(roadSafe.rewardedCities)] : [], lastQuestionIds: roadSafe.lastQuestionIds && typeof roadSafe.lastQuestionIds === "object" ? roadSafe.lastQuestionIds : {}, }; if (!roadRush.unlockedSkins.includes("street-blue")) roadRush.unlockedSkins.unshift("street-blue"); if (!roadRush.unlockedCities.includes("bristol")) roadRush.unlockedCities.unshift("bristol"); if (!roadRush.unlockedSkins.includes(roadRush.selectedSkin)) roadRush.selectedSkin = "street-blue"; return { ...structuredClone(initialState), ...safe, completedLevels, unlockedLevel, unlockedCities: unlockedCities.length ? unlockedCities : ["London"], discoveredVocabulary, unlockedAchievements: [...new Set(unlockedAchievements)], perfectLevels: Array.isArray(safe.perfectLevels) ? safe.perfectLevels : [], levelsUsingHints: Array.isArray(safe.levelsUsingHints) ? safe.levelsUsingHints : [], soundEnabled: safe.soundEnabled !== false, theme: safe.theme === "light" ? "light" : "dark", achievementRewardsClaimed: Array.isArray(safe.achievementRewardsClaimed) ? safe.achievementRewardsClaimed : [], unlockedPerks: Array.isArray(safe.unlockedPerks) ? safe.unlockedPerks : [], towerEscape, roadRush, foundTargetWords, foundBonusWords: safe.foundBonusWords || {}, hintedCells: safe.hintedCells || {}, }; } function loadProgress() { try { const saved = localStorage.getItem(STORAGE_KEY); return saved ? sanitizeState(JSON.parse(saved)) : sanitizeState(memoryState); } catch { return sanitizeState(memoryState); } } function saveProgress(progress) { const safe = sanitizeState(progress); memoryState = structuredClone(safe); try { localStorage.setItem(STORAGE_KEY, JSON.stringify(safe)); } catch { // Some browsers restrict storage for pages opened directly from disk. } window.dispatchEvent(new CustomEvent("ukquest:progress", { detail: safe })); return safe; } function updateProgress(mutator) { const progress = loadProgress(); mutator(progress); return saveProgress(progress); } function resetProgress() { try { localStorage.removeItem(STORAGE_KEY); } catch { /* use in-memory state */ } memoryState = structuredClone(initialState); return saveProgress(structuredClone(initialState)); } function getPlayerLevel(totalXP) { return Math.floor(Math.max(0, Number(totalXP) || 0) / 500) + 1; } function getLevelProgress(totalXP) { const xp = Math.max(0, Number(totalXP) || 0); const current = xp % 500; return { current, target: 500, percent: (current / 500) * 100 }; } function unlockAchievement(progress, achievementId) { if (progress.unlockedAchievements.includes(achievementId)) return false; progress.unlockedAchievements.push(achievementId); const achievement = window.UKQuestAchievements?.getAchievement(achievementId); if (achievement && !progress.achievementRewardsClaimed.includes(achievementId)) { progress.totalXP += achievement.xp || 0; progress.coins += achievement.coins || 0; progress.achievementRewardsClaimed.push(achievementId); if (achievement.perk && !progress.unlockedPerks.includes(achievement.perk)) progress.unlockedPerks.push(achievement.perk); } return true; } function getExplorerRank(totalXP) { const xp = Math.max(0, Number(totalXP) || 0); const ranks = [ { min: 0, title: "Rookie Explorer", description: "Your UK journey has just begun." }, { min: 500, title: "City Scout", description: "You can follow clues and finish city missions." }, { min: 1200, title: "Puzzle Traveller", description: "You solve different kinds of English puzzles." }, { min: 2200, title: "UK Investigator", description: "You connect words, evidence and hidden details." }, { min: 3500, title: "Master Explorer", description: "You are an advanced UK Quest player." }, { min: 5000, title: "Quest Legend", description: "You have mastered every kind of UK Quest challenge." }, ]; const index = Math.max(0, ranks.findLastIndex((rank) => xp >= rank.min)); const current = ranks[index]; const next = ranks[index + 1] || null; return { ...current, index: index + 1, next, percent: next ? ((xp - current.min) / (next.min - current.min)) * 100 : 100 }; } window.UKQuestStorage = { loadProgress, saveProgress, updateProgress, resetProgress, getPlayerLevel, getLevelProgress, getExplorerRank, unlockAchievement, }; })();