189 lines
4.9 KiB
Dart
189 lines
4.9 KiB
Dart
|
|
// packages/feature_game_finddiff/lib/controllers/finddiff_controller.dart
|
||
|
|
|
||
|
|
import 'dart:async';
|
||
|
|
import 'dart:math';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import '../models/finddiff_models.dart';
|
||
|
|
|
||
|
|
class FindDiffController with ChangeNotifier {
|
||
|
|
late FindDiffDifficulty difficulty;
|
||
|
|
late String userId;
|
||
|
|
late String? userName;
|
||
|
|
|
||
|
|
List<FindDiffItem> _items = [];
|
||
|
|
List<FindDiffItem> get items => _items;
|
||
|
|
|
||
|
|
// 게임 상태
|
||
|
|
int _currentRound = 0;
|
||
|
|
int get currentRound => _currentRound;
|
||
|
|
|
||
|
|
int _score = 0;
|
||
|
|
int get score => _score;
|
||
|
|
|
||
|
|
int _incorrectCount = 0;
|
||
|
|
int get incorrectCount => _incorrectCount;
|
||
|
|
|
||
|
|
bool _isGameCompleted = false;
|
||
|
|
bool get isGameCompleted => _isGameCompleted;
|
||
|
|
|
||
|
|
// 타이머
|
||
|
|
Timer? _timer;
|
||
|
|
int _remainingTime = 0;
|
||
|
|
int get remainingTime => _remainingTime;
|
||
|
|
|
||
|
|
// [🔥 신규] 게임 시작 여부 (가이드 확인 후 true)
|
||
|
|
bool _isGameStarted = false;
|
||
|
|
bool get isGameStarted => _isGameStarted;
|
||
|
|
|
||
|
|
// [🔥 신규] 피드백 상태
|
||
|
|
bool _showFeedback = false;
|
||
|
|
bool get showFeedback => _showFeedback;
|
||
|
|
bool _isLastAnswerCorrect = false;
|
||
|
|
bool get isLastAnswerCorrect => _isLastAnswerCorrect;
|
||
|
|
|
||
|
|
final Random _random = Random();
|
||
|
|
|
||
|
|
void setUserInfo(String userId, String? userName) {
|
||
|
|
this.userId = userId;
|
||
|
|
this.userName = userName;
|
||
|
|
}
|
||
|
|
|
||
|
|
void startNewGame(FindDiffDifficulty level) {
|
||
|
|
difficulty = level;
|
||
|
|
_score = 0;
|
||
|
|
_incorrectCount = 0;
|
||
|
|
_currentRound = 1;
|
||
|
|
_isGameCompleted = false;
|
||
|
|
_isGameStarted = false; // 대기 상태
|
||
|
|
_showFeedback = false;
|
||
|
|
|
||
|
|
_generateLevel();
|
||
|
|
// [🔥 수정] 여기서 타이머를 시작하지 않음
|
||
|
|
notifyListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
void restartGame() {
|
||
|
|
startNewGame(difficulty);
|
||
|
|
}
|
||
|
|
|
||
|
|
// [🔥 신규] UI에서 호출할 타이머 시작 함수
|
||
|
|
void startGameTimer() {
|
||
|
|
if (_isGameStarted) return;
|
||
|
|
_isGameStarted = true;
|
||
|
|
_startTimer();
|
||
|
|
notifyListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
void _generateLevel() {
|
||
|
|
_items.clear();
|
||
|
|
final int totalItems = difficulty.totalItems;
|
||
|
|
final int targetIndex = _random.nextInt(totalItems);
|
||
|
|
|
||
|
|
// 1. 기본 속성 결정
|
||
|
|
IconData baseIcon = Icons.circle;
|
||
|
|
Color baseColor = Colors.blue;
|
||
|
|
double baseAngle = 0.0;
|
||
|
|
|
||
|
|
// 난이도별 속성 설정
|
||
|
|
if (difficulty.diffType == FindDiffType.icon) {
|
||
|
|
final pair = FindDiffDifficulties.iconPairs[_random.nextInt(FindDiffDifficulties.iconPairs.length)];
|
||
|
|
baseIcon = pair[0];
|
||
|
|
} else {
|
||
|
|
baseIcon = FindDiffDifficulties.basicIcons[_random.nextInt(FindDiffDifficulties.basicIcons.length)];
|
||
|
|
baseColor = Color.fromARGB(255, _random.nextInt(200), _random.nextInt(200), _random.nextInt(200));
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 아이템 생성
|
||
|
|
for (int i = 0; i < totalItems; i++) {
|
||
|
|
bool isTarget = (i == targetIndex);
|
||
|
|
|
||
|
|
IconData icon = baseIcon;
|
||
|
|
Color color = baseColor;
|
||
|
|
double angle = baseAngle;
|
||
|
|
|
||
|
|
if (isTarget) {
|
||
|
|
// 정답 아이템 변형
|
||
|
|
switch (difficulty.diffType) {
|
||
|
|
case FindDiffType.color:
|
||
|
|
int offset = (difficulty.levelIndex >= 4) ? 30 : 60;
|
||
|
|
color = Color.fromARGB(
|
||
|
|
255,
|
||
|
|
(baseColor.red + offset) % 255,
|
||
|
|
(baseColor.green + offset) % 255,
|
||
|
|
(baseColor.blue + offset) % 255,
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
case FindDiffType.icon:
|
||
|
|
final pair = FindDiffDifficulties.iconPairs.firstWhere((p) => p.contains(baseIcon));
|
||
|
|
icon = (baseIcon == pair[0]) ? pair[1] : pair[0];
|
||
|
|
break;
|
||
|
|
case FindDiffType.rotate:
|
||
|
|
angle = 0.5; // 약 30도 회전
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_items.add(FindDiffItem(
|
||
|
|
id: i,
|
||
|
|
icon: icon,
|
||
|
|
color: color,
|
||
|
|
angle: angle,
|
||
|
|
isTarget: isTarget,
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
_remainingTime = difficulty.timeLimitSeconds;
|
||
|
|
}
|
||
|
|
|
||
|
|
void _startTimer() {
|
||
|
|
_timer?.cancel();
|
||
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||
|
|
_remainingTime--;
|
||
|
|
|
||
|
|
if (_remainingTime <= 0) {
|
||
|
|
// 시간 초과: 오답 처리 및 다음 문제
|
||
|
|
_handleAnswer(false);
|
||
|
|
}
|
||
|
|
notifyListeners();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void onItemTapped(FindDiffItem item) {
|
||
|
|
if (!_isGameStarted || _isGameCompleted || _showFeedback) return;
|
||
|
|
|
||
|
|
_handleAnswer(item.isTarget);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _handleAnswer(bool isCorrect) {
|
||
|
|
_timer?.cancel(); // 잠시 멈춤
|
||
|
|
_showFeedback = true;
|
||
|
|
_isLastAnswerCorrect = isCorrect;
|
||
|
|
|
||
|
|
if (isCorrect) {
|
||
|
|
_score++;
|
||
|
|
} else {
|
||
|
|
_incorrectCount++;
|
||
|
|
}
|
||
|
|
|
||
|
|
notifyListeners();
|
||
|
|
|
||
|
|
// 피드백 후 다음 라운드
|
||
|
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||
|
|
if (_score >= 10) { // [목표] 10문제 맞추면 클리어
|
||
|
|
_isGameCompleted = true;
|
||
|
|
} else {
|
||
|
|
_currentRound++;
|
||
|
|
_showFeedback = false;
|
||
|
|
_generateLevel();
|
||
|
|
_startTimer(); // 타이머 재개
|
||
|
|
}
|
||
|
|
notifyListeners();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_timer?.cancel();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
}
|