...
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
// packages/feature_game_sequence/lib/controllers/sequence_game_controller.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/sequence_models.dart';
|
||||
|
||||
enum SequenceGameState {
|
||||
idle,
|
||||
presenting,
|
||||
postDelay,
|
||||
input,
|
||||
processing,
|
||||
completed,
|
||||
}
|
||||
|
||||
class SequenceGameController with ChangeNotifier {
|
||||
late SequenceDifficulty difficulty;
|
||||
late String userId;
|
||||
late String? userName;
|
||||
|
||||
SequenceGameState _currentState = SequenceGameState.idle;
|
||||
SequenceGameState get currentState => _currentState;
|
||||
|
||||
List<SequenceButtonId> _currentSequence = [];
|
||||
List<SequenceButtonId> get currentSequence => _currentSequence;
|
||||
List<SequenceButtonId> _userInput = [];
|
||||
List<SequenceButtonId> get userInput => _userInput;
|
||||
|
||||
late List<String> _activeSymbolPool;
|
||||
List<String> get activeSymbolPool => _activeSymbolPool;
|
||||
|
||||
int _currentSequenceLength = 0;
|
||||
int get currentSequenceLength => _currentSequenceLength;
|
||||
|
||||
int _maxAchievedLength = 0;
|
||||
int get maxAchievedLength => _maxAchievedLength;
|
||||
|
||||
int _roundsCompleted = 0;
|
||||
int get roundsCompleted => _roundsCompleted;
|
||||
|
||||
int _roundsFailed = 0;
|
||||
int get roundsFailed => _roundsFailed;
|
||||
|
||||
SequenceButtonId? _activePresentationId;
|
||||
SequenceButtonId? get activePresentationId => _activePresentationId;
|
||||
|
||||
bool _isLastInputCorrect = true;
|
||||
bool get isLastInputCorrect => _isLastInputCorrect;
|
||||
|
||||
bool _showFeedback = false;
|
||||
bool get showFeedback => _showFeedback;
|
||||
|
||||
Timer? _scoreTimer;
|
||||
Timer? _roundTimer;
|
||||
double _remainingRoundTime = 0.0;
|
||||
double get remainingRoundTime => _remainingRoundTime;
|
||||
|
||||
int _secondsElapsed = 0;
|
||||
int get secondsElapsed => _secondsElapsed;
|
||||
|
||||
int _presentationStep = 0;
|
||||
int get presentationStep => _presentationStep;
|
||||
|
||||
set presentationStep(int step) {
|
||||
_presentationStep = step;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
final Random _random = Random();
|
||||
|
||||
String getSymbolForId(SequenceButtonId id) {
|
||||
if (id.index >= _activeSymbolPool.length) {
|
||||
return '?';
|
||||
}
|
||||
return _activeSymbolPool[id.index];
|
||||
}
|
||||
|
||||
void setUserInfo(String userId, String? userName) {
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
void startNewGame(SequenceDifficulty level) {
|
||||
difficulty = level;
|
||||
_currentSequence.clear();
|
||||
_userInput.clear();
|
||||
_currentSequenceLength = level.initialLength;
|
||||
_maxAchievedLength = level.initialLength - 1;
|
||||
_currentState = SequenceGameState.idle;
|
||||
_secondsElapsed = 0;
|
||||
_roundsCompleted = 0;
|
||||
_roundsFailed = 0;
|
||||
_showFeedback = false;
|
||||
_activePresentationId = null;
|
||||
_presentationStep = 0;
|
||||
|
||||
_initializeSymbolPool();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_startPresentationPhase();
|
||||
});
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void restartGame() {
|
||||
startNewGame(difficulty);
|
||||
}
|
||||
|
||||
void setActivePresentation(SequenceButtonId? id) {
|
||||
_activePresentationId = id;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 🔽 [🔥 수정] 레벨 10 이상만 믹스, 나머지는 단일 테마
|
||||
void _initializeSymbolPool() {
|
||||
if (difficulty.levelIndex >= 10) {
|
||||
// Lv 10+: 모든 풀을 합치고 섞음 (Mixed)
|
||||
final List<String> allSymbols = [
|
||||
...SequenceDifficulties.getSymbolPool(SequenceContentPool.number),
|
||||
...SequenceDifficulties.getSymbolPool(SequenceContentPool.letter),
|
||||
...SequenceDifficulties.getSymbolPool(SequenceContentPool.koreanChar),
|
||||
...SequenceDifficulties.getSymbolPool(SequenceContentPool.emoji),
|
||||
// (단어는 길이가 길어 혼합 시 시각적 밸런스를 위해 제외하거나 포함 가능. 여기선 제외)
|
||||
];
|
||||
_activeSymbolPool = (allSymbols..shuffle()).sublist(0, difficulty.buttonsCount);
|
||||
} else {
|
||||
// Lv 1~9: 지정된 단일 풀 사용
|
||||
_activeSymbolPool = SequenceDifficulties.getSymbolPool(difficulty.contentPoolType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _startPresentationPhase() {
|
||||
_roundTimer?.cancel();
|
||||
_currentState = SequenceGameState.presenting;
|
||||
_presentationStep = 0;
|
||||
_generateNextSequence();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _generateNextSequence() {
|
||||
_currentSequence.clear();
|
||||
final int maxIndex = difficulty.buttonsCount;
|
||||
for (int i = 0; i < _currentSequenceLength; i++) {
|
||||
final int randIndex = _random.nextInt(maxIndex);
|
||||
_currentSequence.add(SequenceButtonId.values[randIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void runPostPresentationDelay() {
|
||||
_currentState = SequenceGameState.postDelay;
|
||||
notifyListeners();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
if (currentState == SequenceGameState.postDelay) {
|
||||
startInputPhase();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void startInputPhase() {
|
||||
_startScoreTimer();
|
||||
|
||||
_userInput.clear();
|
||||
_currentState = SequenceGameState.input;
|
||||
_startRoundTimer();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _startRoundTimer() {
|
||||
_roundTimer?.cancel();
|
||||
_remainingRoundTime = difficulty.roundTimeLimit;
|
||||
_roundTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
|
||||
if (_remainingRoundTime <= 0) {
|
||||
_roundTimer?.cancel();
|
||||
_roundsFailed++;
|
||||
_handleFeedbackAndTermination();
|
||||
} else {
|
||||
_remainingRoundTime -= 0.1;
|
||||
notifyListeners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _startScoreTimer() {
|
||||
_scoreTimer?.cancel();
|
||||
_scoreTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
_secondsElapsed++;
|
||||
});
|
||||
}
|
||||
|
||||
void recordUserInput(SequenceButtonId inputId) {
|
||||
if (_currentState != SequenceGameState.input || _showFeedback) return;
|
||||
|
||||
_userInput.add(inputId);
|
||||
_currentState = SequenceGameState.processing;
|
||||
notifyListeners();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
_checkUserInput();
|
||||
});
|
||||
}
|
||||
|
||||
void _checkUserInput() {
|
||||
if (_currentState != SequenceGameState.processing) return;
|
||||
|
||||
final int inputLength = _userInput.length;
|
||||
final int currentInputIndex = inputLength - 1;
|
||||
|
||||
if (_userInput[currentInputIndex] != _currentSequence[currentInputIndex]) {
|
||||
_roundsFailed++;
|
||||
_isLastInputCorrect = false;
|
||||
_handleFeedbackAndTermination();
|
||||
return;
|
||||
}
|
||||
|
||||
_isLastInputCorrect = true;
|
||||
|
||||
if (inputLength == _currentSequenceLength) {
|
||||
_roundsCompleted++;
|
||||
_maxAchievedLength = _currentSequenceLength;
|
||||
|
||||
_handleFeedbackAndNextRound();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentState = SequenceGameState.input;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _handleFeedbackAndTermination() {
|
||||
_stopAllTimers();
|
||||
_showFeedback = true;
|
||||
notifyListeners();
|
||||
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
_stopAllTimers();
|
||||
_currentState = SequenceGameState.completed;
|
||||
_showFeedback = false;
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
void _handleFeedbackAndNextRound() {
|
||||
_roundTimer?.cancel();
|
||||
_showFeedback = true;
|
||||
notifyListeners();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 1000), () {
|
||||
_showFeedback = false;
|
||||
|
||||
if (_currentSequenceLength < difficulty.maxGameLength) {
|
||||
_currentSequenceLength++;
|
||||
} else {
|
||||
_stopAllTimers();
|
||||
_currentState = SequenceGameState.completed;
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
_startPresentationPhase();
|
||||
});
|
||||
}
|
||||
|
||||
void _stopAllTimers() {
|
||||
_scoreTimer?.cancel();
|
||||
_roundTimer?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stopAllTimers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user