Wire season flow: map screen, progress save, win recording, next stage
SaveRepository (versioned JSON over prefs) with best-result merging and unlock walking; ContentRepository loads the bundled pack; SeasonFlow/ Progress notifiers orchestrate stage start -> win record -> advance. Season map grid with stars/locks, Home -> Map -> Game navigation, close button, next-stage action on the win overlay. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../game/models/season.dart';
|
||||
|
||||
/// Resolves season content. Phase 3: bundled assets only; remote download
|
||||
/// and caching land in Phase 4.
|
||||
class ContentRepository {
|
||||
static const bundledSeasonIds = ['season_001'];
|
||||
|
||||
Future<SeasonPack> loadBundledSeason(String seasonId) async {
|
||||
final raw =
|
||||
await rootBundle.loadString('assets/seasons/$seasonId/pack.json');
|
||||
return SeasonPack.fromJson(jsonDecode(raw) as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<List<SeasonPack>> availableSeasons() async => [
|
||||
for (final id in bundledSeasonIds) await loadBundledSeason(id),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class StageProgress {
|
||||
const StageProgress({required this.stars, required this.bestScore});
|
||||
|
||||
final int stars;
|
||||
final int bestScore;
|
||||
}
|
||||
|
||||
/// Versioned JSON-over-prefs persistence for progress. Total payload stays
|
||||
/// tiny (<100 KB), so a key-value blob beats a database here.
|
||||
class SaveRepository {
|
||||
SaveRepository(this._prefs) {
|
||||
final raw = _prefs.getString(_key);
|
||||
if (raw != null) {
|
||||
final json = jsonDecode(raw) as Map<String, dynamic>;
|
||||
final progress = json['progress'] as Map<String, dynamic>? ?? {};
|
||||
for (final entry in progress.entries) {
|
||||
final value = entry.value as Map<String, dynamic>;
|
||||
_progress[entry.key] = StageProgress(
|
||||
stars: value['stars'] as int,
|
||||
bestScore: value['bestScore'] as int,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const _key = 'save_v1';
|
||||
|
||||
static Future<SaveRepository> open() async =>
|
||||
SaveRepository(await SharedPreferences.getInstance());
|
||||
|
||||
final SharedPreferences _prefs;
|
||||
final Map<String, StageProgress> _progress = {};
|
||||
|
||||
static String _id(String seasonId, String stageId) => '$seasonId/$stageId';
|
||||
|
||||
/// Immutable copy of all progress, keyed `seasonId/stageId`.
|
||||
Map<String, StageProgress> snapshot() => Map.unmodifiable(_progress);
|
||||
|
||||
StageProgress? progressFor(String seasonId, String stageId) =>
|
||||
_progress[_id(seasonId, stageId)];
|
||||
|
||||
Future<void> recordResult({
|
||||
required String seasonId,
|
||||
required String stageId,
|
||||
required int stars,
|
||||
required int score,
|
||||
}) async {
|
||||
final id = _id(seasonId, stageId);
|
||||
final old = _progress[id];
|
||||
_progress[id] = StageProgress(
|
||||
stars: old == null || stars > old.stars ? stars : old.stars,
|
||||
bestScore:
|
||||
old == null || score > old.bestScore ? score : old.bestScore,
|
||||
);
|
||||
await _flush();
|
||||
}
|
||||
|
||||
int totalStars(String seasonId) {
|
||||
var total = 0;
|
||||
for (final entry in _progress.entries) {
|
||||
if (entry.key.startsWith('$seasonId/')) total += entry.value.stars;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/// Index of the furthest playable stage: the first without a star, or the
|
||||
/// last stage when everything is starred.
|
||||
int highestUnlockedIndex(String seasonId, List<String> stageIds) {
|
||||
for (var i = 0; i < stageIds.length; i++) {
|
||||
final progress = progressFor(seasonId, stageIds[i]);
|
||||
if (progress == null || progress.stars == 0) return i;
|
||||
}
|
||||
return stageIds.length - 1;
|
||||
}
|
||||
|
||||
Future<void> _flush() => _prefs.setString(
|
||||
_key,
|
||||
jsonEncode({
|
||||
'saveVersion': 1,
|
||||
'progress': {
|
||||
for (final entry in _progress.entries)
|
||||
entry.key: {
|
||||
'stars': entry.value.stars,
|
||||
'bestScore': entry.value.bestScore,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user