import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; import 'app_theme.dart'; // --- Achievement System --- class AchievementDef { final String id; final String title; final String description; final int rewardStars; const AchievementDef({ required this.id, required this.title, required this.description, required this.rewardStars, }); } // --- Level Progress Manager --- class LevelManager { static final LevelManager instance = LevelManager._internal(); LevelManager._internal(); final Map _bestStars = {}; int highestUnlockedLevel = 1; int totalStars = 0; // 当前可用星星(用于兑换) String selectedSkinId = 'classic'; final Set ownedSkins = {'classic'}; final Map _unlockedAchievements = {}; static const _bestStarsKey = 'bestStars'; static const _highestLevelKey = 'highestUnlockedLevel'; static const _totalStarsKey = 'totalStars'; static const _ownedSkinsKey = 'ownedSkins'; static const _selectedSkinKey = 'selectedSkin'; static const _achievementsKey = 'achievements'; static const List achievementsCatalog = [ AchievementDef( id: 'first_clear', title: 'First Steps', description: 'Clear any level for the first time.', rewardStars: 3, ), AchievementDef( id: 'perfect_run', title: 'Perfect Ink', description: 'Earn 5 stars in a single level.', rewardStars: 5, ), AchievementDef( id: 'speed_runner', title: 'Speed Runner', description: 'Clear a level within 30 seconds.', rewardStars: 5, ), AchievementDef( id: 'slope_master', title: 'Slope Master', description: 'Clear a slope level (1–3).', rewardStars: 2, ), AchievementDef( id: 'bounce_master', title: 'Bounce Master', description: 'Clear a bounce level (4–6).', rewardStars: 2, ), AchievementDef( id: 'maze_master', title: 'Maze Navigator', description: 'Clear a multi-layer maze level (7–9).', rewardStars: 2, ), AchievementDef( id: 'gap_master', title: 'Gap Sniper', description: 'Clear a narrow-gap level (10–12).', rewardStars: 2, ), ]; Future load() async { final prefs = await SharedPreferences.getInstance(); final bestStr = prefs.getString(_bestStarsKey); if (bestStr != null) { final Map decoded = jsonDecode(bestStr); _bestStars ..clear() ..addEntries( decoded.entries.map( (e) => MapEntry(int.parse(e.key), e.value as int), ), ); } highestUnlockedLevel = prefs.getInt(_highestLevelKey) ?? 1; if (highestUnlockedLevel < 1) highestUnlockedLevel = 1; totalStars = prefs.getInt(_totalStarsKey) ?? 0; final owned = prefs.getStringList(_ownedSkinsKey); ownedSkins ..clear() ..addAll(owned?.toSet() ?? {'classic'}); selectedSkinId = prefs.getString(_selectedSkinKey) ?? 'classic'; if (!ownedSkins.contains(selectedSkinId)) { selectedSkinId = 'classic'; } final unlockedList = prefs.getStringList(_achievementsKey); _unlockedAchievements ..clear() ..addEntries( (unlockedList ?? const []).map((id) => MapEntry(id, true)), ); } Future _persist() async { final prefs = await SharedPreferences.getInstance(); final mapAsStringInt = _bestStars.map( (key, value) => MapEntry(key.toString(), value), ); await prefs.setString(_bestStarsKey, jsonEncode(mapAsStringInt)); await prefs.setInt(_highestLevelKey, highestUnlockedLevel); await prefs.setInt(_totalStarsKey, totalStars); await prefs.setStringList(_ownedSkinsKey, ownedSkins.toList()); await prefs.setString(_selectedSkinKey, selectedSkinId); await prefs.setStringList( _achievementsKey, _unlockedAchievements.entries .where((e) => e.value) .map((e) => e.key) .toList(), ); } Future clearAll() async { _bestStars.clear(); highestUnlockedLevel = 1; totalStars = 0; ownedSkins ..clear() ..add('classic'); selectedSkinId = 'classic'; await _persist(); } void saveProgress(int levelId, int stars, int maxLevel) { final oldBest = _bestStars[levelId] ?? 0; if (stars > oldBest) { _bestStars[levelId] = stars; // 只把"新提高"的星星计入总数,避免重复刷 totalStars += (stars - oldBest); } // 通关解锁下一关(至少 1 星) if (levelId == highestUnlockedLevel && stars > 0) { if (highestUnlockedLevel < maxLevel) { highestUnlockedLevel = highestUnlockedLevel + 1; } } // 异步持久化 _persist(); } int getStars(int levelId) => _bestStars[levelId] ?? 0; // 皮肤相关 bool isSkinOwned(String skinId) => ownedSkins.contains(skinId); bool isSkinSelected(String skinId) => selectedSkinId == skinId; bool canAfford(int cost) => totalStars >= cost; void selectSkin(String skinId) { if (!isSkinOwned(skinId)) return; selectedSkinId = skinId; _persist(); } bool buySkin(SkinThemeData skin) { if (isSkinOwned(skin.id)) return false; if (!canAfford(skin.cost)) return false; totalStars -= skin.cost; ownedSkins.add(skin.id); selectedSkinId = skin.id; _persist(); return true; } bool isAchievementUnlocked(String id) => _unlockedAchievements[id] == true; List unlockAchievementsForWin({ required int levelId, required int stars, required Duration? clearTime, }) { final newlyUnlocked = []; void _tryUnlock(String id) { if (isAchievementUnlocked(id)) return; final def = achievementsCatalog.firstWhere( (a) => a.id == id, orElse: () => achievementsCatalog.first, ); _unlockedAchievements[id] = true; totalStars += def.rewardStars; newlyUnlocked.add(def); } // 首次通关任意关卡 if (stars > 0 && _unlockedAchievements.isEmpty) { _tryUnlock('first_clear'); } // 一次性拿满 5 星 if (stars == 5) { _tryUnlock('perfect_run'); } // 在 30 秒内通关一关 if (clearTime != null && clearTime.inSeconds <= 30) { _tryUnlock('speed_runner'); } // 主题关成就,基于关卡编号区间 if (levelId >= 1 && levelId <= 3) { _tryUnlock('slope_master'); } else if (levelId >= 4 && levelId <= 6) { _tryUnlock('bounce_master'); } else if (levelId >= 7 && levelId <= 9) { _tryUnlock('maze_master'); } else if (levelId >= 10 && levelId <= 12) { _tryUnlock('gap_master'); } if (newlyUnlocked.isNotEmpty) { _persist(); } return newlyUnlocked; } }