import 'stage.dart'; /// Visual identity of a season. Colors are int ARGB so this file stays /// pure Dart (architecture guard forbids Flutter imports here). class SeasonTheme { const SeasonTheme({ this.tileSet = 'spring', this.background = '', this.backgroundGradient = defaultGradient, this.accentColor = 0xFFFF7EB3, this.particleType = 'petals', this.tilePalette, this.boardTint, }); factory SeasonTheme.fromJson(Map json) => SeasonTheme( tileSet: (json['tileSet'] as String?) ?? 'spring', background: (json['background'] as String?) ?? '', backgroundGradient: json['backgroundGradient'] != null ? [for (final c in json['backgroundGradient'] as List) (c as num).toInt()] : defaultGradient, accentColor: (json['accentColor'] as int?) ?? 0xFFFF7EB3, particleType: (json['particleType'] as String?) ?? 'petals', tilePalette: json['tilePalette'] != null ? [for (final c in json['tilePalette'] as List) (c as num).toInt()] : null, boardTint: json['boardTint'] as int?, ); /// Season 1 "First Bloom": deep navy dusk. static const defaultGradient = [0xFF0E1430, 0xFF16204A, 0xFF2A2E5E]; static const fallback = SeasonTheme(); final String tileSet; final String background; /// Top-to-bottom screen gradient, int ARGB. final List backgroundGradient; final int accentColor; /// petals | snow | leaves | none final String particleType; /// Optional tile color override; null = built-in candy palette. final List? tilePalette; /// Optional board background override. final int? boardTint; Map toJson() => { 'tileSet': tileSet, 'background': background, 'backgroundGradient': backgroundGradient, 'accentColor': accentColor, 'particleType': particleType, if (tilePalette != null) 'tilePalette': tilePalette, if (boardTint != null) 'boardTint': boardTint, }; } /// A season's full content: metadata, theme, and its stages. The unit of /// remote (or bundled) delivery. class SeasonPack { const SeasonPack({ required this.schemaVersion, required this.seasonId, required this.version, required this.title, required this.theme, required this.stages, }); /// Bump when the pack format changes incompatibly; older app builds skip /// packs with a newer schema instead of crashing. static const int supportedSchema = 1; factory SeasonPack.fromJson(Map json) { final schema = json['schemaVersion'] as int; if (schema > supportedSchema) { throw FormatException('Unsupported pack schema: $schema'); } return SeasonPack( schemaVersion: schema, seasonId: json['seasonId'] as String, version: json['version'] as int, title: (json['title'] as Map) .map((k, v) => MapEntry(k, v as String)), theme: SeasonTheme.fromJson(json['theme'] as Map), stages: [ for (final stage in json['stages'] as List) StageConfig.fromJson(stage as Map), ], ); } final int schemaVersion; final String seasonId; final int version; final Map title; final SeasonTheme theme; final List stages; String titleFor(String languageCode) => title[languageCode] ?? title['en'] ?? seasonId; Map toJson() => { 'schemaVersion': schemaVersion, 'seasonId': seasonId, 'version': version, 'title': title, 'theme': theme.toJson(), 'stages': [for (final stage in stages) stage.toJson()], }; }