import '../engine/game_event.dart'; /// Stage win condition with immutable progress tracking. New objective types /// plug in as subclasses with a JSON tag. sealed class Objective { const Objective(); factory Objective.clearGems(int count) => ClearGemsObjective(count, 0); factory Objective.reachScore(int target) => ReachScoreObjective(target, 0); factory Objective.clearLines(int count) => ClearLinesObjective(count, 0); factory Objective.fromJson(Map json) { switch (json['type']) { case 'clearGems': return Objective.clearGems(json['count'] as int); case 'reachScore': return Objective.reachScore(json['target'] as int); case 'clearLines': return Objective.clearLines(json['count'] as int); default: throw FormatException('Unknown objective type: ${json['type']}'); } } int get target; int get current; bool get isComplete => current >= target; Objective onEvent(GameEvent event); Map toJson(); } class ClearGemsObjective extends Objective { const ClearGemsObjective(this.target, this.current); @override final int target; @override final int current; @override Objective onEvent(GameEvent event) => switch (event) { LinesCleared(:final gems) => ClearGemsObjective(target, current + gems), _ => this, }; @override Map toJson() => {'type': 'clearGems', 'count': target}; } class ReachScoreObjective extends Objective { const ReachScoreObjective(this.target, this.current); @override final int target; @override final int current; @override Objective onEvent(GameEvent event) => switch (event) { ScoreChanged(:final total) => ReachScoreObjective(target, total), _ => this, }; @override Map toJson() => {'type': 'reachScore', 'target': target}; } class ClearLinesObjective extends Objective { const ClearLinesObjective(this.target, this.current); @override final int target; @override final int current; @override Objective onEvent(GameEvent event) => switch (event) { LinesCleared(:final lines) => ClearLinesObjective(target, current + lines), _ => this, }; @override Map toJson() => {'type': 'clearLines', 'count': target}; }