- Add gridWidth and gridHeight properties to level configuration - Update GameGrid to accept custom dimensions instead of using constants - Modify GridComponent to calculate gem size based on grid dimensions - Update MatchThreeGame constructor to pass grid dimensions - Ensure proper scaling and positioning for variable grid sizes
294 lines
7.0 KiB
Dart
294 lines
7.0 KiB
Dart
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<int> availableGemTypes;
|
|
final bool unlocked;
|
|
final int gridWidth;
|
|
final int gridHeight;
|
|
|
|
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,
|
|
this.gridWidth =
|
|
9, // Default to current grid size for backward compatibility
|
|
this.gridHeight = 8,
|
|
});
|
|
|
|
factory Level.fromJson(Map<String, dynamic> 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<int>.from(json['availableGemTypes']),
|
|
unlocked: json['unlocked'] ?? false,
|
|
gridWidth: json['gridWidth'] ?? 9, // Default for backward compatibility
|
|
gridHeight: json['gridHeight'] ?? 8,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id': id,
|
|
'name': name,
|
|
'description': description,
|
|
'constraints': constraints.toJson(),
|
|
'objectives': objectives.toJson(),
|
|
'starRating': starRating.toJson(),
|
|
'availableGemTypes': availableGemTypes,
|
|
'unlocked': unlocked,
|
|
'gridWidth': gridWidth,
|
|
'gridHeight': gridHeight,
|
|
};
|
|
}
|
|
|
|
Level copyWith({
|
|
int? id,
|
|
String? name,
|
|
String? description,
|
|
LevelConstraints? constraints,
|
|
LevelObjectives? objectives,
|
|
StarRating? starRating,
|
|
List<int>? availableGemTypes,
|
|
bool? unlocked,
|
|
int? gridWidth,
|
|
int? gridHeight,
|
|
}) {
|
|
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,
|
|
gridWidth: gridWidth ?? this.gridWidth,
|
|
gridHeight: gridHeight ?? this.gridHeight,
|
|
);
|
|
}
|
|
|
|
@override
|
|
List<Object?> get props => [
|
|
id,
|
|
name,
|
|
description,
|
|
constraints,
|
|
objectives,
|
|
starRating,
|
|
availableGemTypes,
|
|
unlocked,
|
|
gridWidth,
|
|
gridHeight,
|
|
];
|
|
}
|
|
|
|
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<String, dynamic> json) {
|
|
return LevelConstraints(
|
|
targetScore: json['targetScore'],
|
|
maxMoves: json['maxMoves'],
|
|
timeLimit: json['timeLimit'],
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> 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<Object?> get props => [targetScore, maxMoves, timeLimit];
|
|
}
|
|
|
|
class LevelObjectives extends Equatable {
|
|
final Map<int, int> clearGemTypes; // gemType -> count required
|
|
|
|
const LevelObjectives({
|
|
this.clearGemTypes = const {},
|
|
});
|
|
|
|
factory LevelObjectives.fromJson(Map<String, dynamic> json) {
|
|
Map<int, int> clearGemTypes = {};
|
|
|
|
if (json['clearGemTypes'] != null) {
|
|
final clearGemTypesJson = json['clearGemTypes'] as Map<String, dynamic>;
|
|
clearGemTypes = clearGemTypesJson.map(
|
|
(key, value) => MapEntry(int.parse(key), value as int),
|
|
);
|
|
}
|
|
|
|
return LevelObjectives(
|
|
clearGemTypes: clearGemTypes,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'clearGemTypes': clearGemTypes.map(
|
|
(key, value) => MapEntry(key.toString(), value),
|
|
),
|
|
};
|
|
}
|
|
|
|
bool get hasGemTypeObjectives => clearGemTypes.isNotEmpty;
|
|
|
|
@override
|
|
List<Object?> 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<String, dynamic> json) {
|
|
return StarRating(
|
|
criteria: StarCriteria.values.firstWhere(
|
|
(e) => e.name == json['criteria'],
|
|
),
|
|
thresholds: StarThresholds.fromJson(json['thresholds']),
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'criteria': criteria.name,
|
|
'thresholds': thresholds.toJson(),
|
|
};
|
|
}
|
|
|
|
@override
|
|
List<Object?> 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<String, dynamic> json) {
|
|
return StarThresholds(
|
|
oneStar: json['oneStar'],
|
|
twoStar: json['twoStar'],
|
|
threeStar: json['threeStar'],
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'oneStar': oneStar,
|
|
'twoStar': twoStar,
|
|
'threeStar': threeStar,
|
|
};
|
|
}
|
|
|
|
@override
|
|
List<Object?> 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<String, dynamic> json) {
|
|
return LevelProgress(
|
|
levelId: json['levelId'],
|
|
completed: json['completed'] ?? false,
|
|
stars: json['stars'] ?? 0,
|
|
bestScore: json['bestScore'] ?? 0,
|
|
unlocked: json['unlocked'] ?? false,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> 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<Object?> get props => [levelId, completed, stars, bestScore, unlocked];
|
|
}
|