116 lines
3.7 KiB
Dart
116 lines
3.7 KiB
Dart
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<String, dynamic> 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<int> backgroundGradient;
|
|
final int accentColor;
|
|
|
|
/// petals | snow | leaves | none
|
|
final String particleType;
|
|
|
|
/// Optional tile color override; null = built-in candy palette.
|
|
final List<int>? tilePalette;
|
|
|
|
/// Optional board background override.
|
|
final int? boardTint;
|
|
|
|
Map<String, dynamic> 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<String, dynamic> 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<String, dynamic>)
|
|
.map((k, v) => MapEntry(k, v as String)),
|
|
theme: SeasonTheme.fromJson(json['theme'] as Map<String, dynamic>),
|
|
stages: [
|
|
for (final stage in json['stages'] as List)
|
|
StageConfig.fromJson(stage as Map<String, dynamic>),
|
|
],
|
|
);
|
|
}
|
|
|
|
final int schemaVersion;
|
|
final String seasonId;
|
|
final int version;
|
|
final Map<String, String> title;
|
|
final SeasonTheme theme;
|
|
final List<StageConfig> stages;
|
|
|
|
String titleFor(String languageCode) =>
|
|
title[languageCode] ?? title['en'] ?? seasonId;
|
|
|
|
Map<String, dynamic> toJson() => {
|
|
'schemaVersion': schemaVersion,
|
|
'seasonId': seasonId,
|
|
'version': version,
|
|
'title': title,
|
|
'theme': theme.toJson(),
|
|
'stages': [for (final stage in stages) stage.toJson()],
|
|
};
|
|
}
|