import 'package:equatable/equatable.dart'; import 'package:match_three/game/models/gem.dart'; enum StarCriteria { SCORE, MOVES_REMAINING, TIME_REMAINING, EFFICIENCY, COMBINED, } class Level extends Equatable { final int id; final String name; final String description; final LevelConstraints constraints; final LevelObjectives objectives; final StarRating starRating; final List availableGemTypes; final bool unlocked; const Level({ required this.id, required this.name, required this.description, required this.constraints, required this.objectives, required this.starRating, required this.availableGemTypes, this.unlocked = false, }); factory Level.fromJson(Map json) { return Level( id: json['id'], name: json['name'], description: json['description'], constraints: LevelConstraints.fromJson(json['constraints']), objectives: LevelObjectives.fromJson(json['objectives']), starRating: StarRating.fromJson(json['starRating']), availableGemTypes: List.from(json['availableGemTypes']), unlocked: json['unlocked'] ?? false, ); } Map toJson() { return { 'id': id, 'name': name, 'description': description, 'constraints': constraints.toJson(), 'objectives': objectives.toJson(), 'starRating': starRating.toJson(), 'availableGemTypes': availableGemTypes, 'unlocked': unlocked, }; } Level copyWith({ int? id, String? name, String? description, LevelConstraints? constraints, LevelObjectives? objectives, StarRating? starRating, List? availableGemTypes, bool? unlocked, }) { return Level( id: id ?? this.id, name: name ?? this.name, description: description ?? this.description, constraints: constraints ?? this.constraints, objectives: objectives ?? this.objectives, starRating: starRating ?? this.starRating, availableGemTypes: availableGemTypes ?? this.availableGemTypes, unlocked: unlocked ?? this.unlocked, ); } @override List get props => [ id, name, description, constraints, objectives, starRating, availableGemTypes, unlocked, ]; } class LevelConstraints extends Equatable { final int? targetScore; final int? maxMoves; final int? timeLimit; // in seconds const LevelConstraints({ this.targetScore, this.maxMoves, this.timeLimit, }); factory LevelConstraints.fromJson(Map json) { return LevelConstraints( targetScore: json['targetScore'], maxMoves: json['maxMoves'], timeLimit: json['timeLimit'], ); } Map toJson() { return { 'targetScore': targetScore, 'maxMoves': maxMoves, 'timeLimit': timeLimit, }; } bool get hasTimeLimit => timeLimit != null; bool get hasMoveLimit => maxMoves != null; bool get hasScoreTarget => targetScore != null; @override List get props => [targetScore, maxMoves, timeLimit]; } class LevelObjectives extends Equatable { final Map clearGemTypes; // gemType -> count required const LevelObjectives({ this.clearGemTypes = const {}, }); factory LevelObjectives.fromJson(Map json) { Map clearGemTypes = {}; if (json['clearGemTypes'] != null) { final clearGemTypesJson = json['clearGemTypes'] as Map; clearGemTypes = clearGemTypesJson.map( (key, value) => MapEntry(int.parse(key), value as int), ); } return LevelObjectives( clearGemTypes: clearGemTypes, ); } Map toJson() { return { 'clearGemTypes': clearGemTypes.map( (key, value) => MapEntry(key.toString(), value), ), }; } bool get hasGemTypeObjectives => clearGemTypes.isNotEmpty; @override List get props => [clearGemTypes]; } class StarRating extends Equatable { final StarCriteria criteria; final StarThresholds thresholds; const StarRating({ required this.criteria, required this.thresholds, }); factory StarRating.fromJson(Map json) { return StarRating( criteria: StarCriteria.values.firstWhere( (e) => e.name == json['criteria'], ), thresholds: StarThresholds.fromJson(json['thresholds']), ); } Map toJson() { return { 'criteria': criteria.name, 'thresholds': thresholds.toJson(), }; } @override List get props => [criteria, thresholds]; } class StarThresholds extends Equatable { final int oneStar; final int twoStar; final int threeStar; const StarThresholds({ required this.oneStar, required this.twoStar, required this.threeStar, }); factory StarThresholds.fromJson(Map json) { return StarThresholds( oneStar: json['oneStar'], twoStar: json['twoStar'], threeStar: json['threeStar'], ); } Map toJson() { return { 'oneStar': oneStar, 'twoStar': twoStar, 'threeStar': threeStar, }; } @override List get props => [oneStar, twoStar, threeStar]; } class LevelProgress extends Equatable { final int levelId; final bool completed; final int stars; final int bestScore; final bool unlocked; const LevelProgress({ required this.levelId, this.completed = false, this.stars = 0, this.bestScore = 0, this.unlocked = false, }); factory LevelProgress.fromJson(Map json) { return LevelProgress( levelId: json['levelId'], completed: json['completed'] ?? false, stars: json['stars'] ?? 0, bestScore: json['bestScore'] ?? 0, unlocked: json['unlocked'] ?? false, ); } Map toJson() { return { 'levelId': levelId, 'completed': completed, 'stars': stars, 'bestScore': bestScore, 'unlocked': unlocked, }; } LevelProgress copyWith({ int? levelId, bool? completed, int? stars, int? bestScore, bool? unlocked, }) { return LevelProgress( levelId: levelId ?? this.levelId, completed: completed ?? this.completed, stars: stars ?? this.stars, bestScore: bestScore ?? this.bestScore, unlocked: unlocked ?? this.unlocked, ); } @override List get props => [levelId, completed, stars, bestScore, unlocked]; }