import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:math'; import 'package:flame/game.dart'; import '../landing_page.dart'; import '../utils/level_manager.dart'; // Pindahkan enum ke level atas enum WordDirection { horizontal, vertical, diagonal } // Pindahkan class ke level atas class PlacedWord { final String word; final int startRow; final int startCol; final WordDirection direction; PlacedWord( {required this.word, required this.startRow, required this.startCol, required this.direction}); } class Level3HinduBuddha extends StatefulWidget { const Level3HinduBuddha({Key? key}) : super(key: key); @override _Level3HinduBuddhaState createState() => _Level3HinduBuddhaState(); } class _Level3HinduBuddhaState extends State { // Definisikan gridSize sebagai konstanta kelas static const int GRID_SIZE = 17; late Timer timer; int seconds = 0; int score = 0; // Data kata dan pertanyaan terkait dengan urutan final List> orderedQuestions = [ { 'word': 'KUTAI', 'question': 'Kerajaan Hindu tertua di Indonesia yang terletak di Kalimantan Timur', 'isFound': false, 'isUnlocked': true, // Pertanyaan pertama sudah unlocked 'isVisible': true, // Pertanyaan pertama sudah visible }, { 'word': 'TARUMANEGARA', 'question': 'Kerajaan Hindu tertua di Pulau Jawa yang terkenal dengan prasasti Tugu', 'isFound': false, 'isUnlocked': false, 'isVisible': false, }, { 'word': 'SRIWIJAYA', 'question': 'Kerajaan maritim Buddha terbesar yang berpusat di Palembang', 'isFound': false, 'isUnlocked': false, 'isVisible': false, }, { 'word': 'MATARAM', 'question': 'Kerajaan yang membangun Candi Borobudur', 'isFound': false, 'isUnlocked': false, 'isVisible': false, }, { 'word': 'MAJAPAHIT', 'question': 'Kerajaan Hindu terbesar di Nusantara yang dipimpin oleh Hayam Wuruk', 'isFound': false, 'isUnlocked': false, 'isVisible': false, } ]; int currentQuestionIndex = 0; late List> grid; String? lastFoundWord; // Untuk seleksi kata Offset? startDrag; Offset? currentDrag; List selectedCells = []; List correctCells = []; @override void initState() { super.initState(); startTimer(); initializeGame(); } void initializeGame() { grid = List.generate(GRID_SIZE, (_) => List.filled(GRID_SIZE, '')); placeWords(); fillEmptySpaces(); } void placeWords() { // Definisikan kata-kata yang akan diacak List words = [ 'KUTAI', 'TARUMANEGARA', 'SRIWIJAYA', 'MATARAM', 'MAJAPAHIT' ]; // Bersihkan grid grid = List.generate(GRID_SIZE, (_) => List.filled(GRID_SIZE, '')); // Acak urutan kata final random = Random(); words.shuffle(random); // Urutkan kata dari yang terpanjang ke terpendek words.sort((a, b) => b.length.compareTo(a.length)); // Definisikan zona aman untuk setiap kata final List placements = []; int verticalPosition = 1; // Mulai dari baris 1 int horizontalPosition = 1; // Mulai dari kolom 1 // Tempatkan kata-kata dengan zona aman for (String word in words) { // Pilih arah penempatan secara acak (horizontal/vertical) bool isHorizontal = random.nextBool(); if (word.length > GRID_SIZE - 2) { // Kata terlalu panjang, selalu horizontal placements.add(PlacedWord( word: word, startRow: verticalPosition, startCol: 1, direction: WordDirection.horizontal, )); verticalPosition += 3; } else if (isHorizontal && horizontalPosition + word.length < GRID_SIZE - 1) { // Coba horizontal jika muat placements.add(PlacedWord( word: word, startRow: verticalPosition, startCol: horizontalPosition, direction: WordDirection.horizontal, )); verticalPosition += 3; horizontalPosition = 1; } else if (verticalPosition + word.length < GRID_SIZE - 1) { // Coba vertikal jika muat placements.add(PlacedWord( word: word, startRow: verticalPosition, startCol: horizontalPosition, direction: WordDirection.vertical, )); horizontalPosition += 3; if (horizontalPosition > GRID_SIZE - word.length) { horizontalPosition = 1; verticalPosition += 2; } } else { // Fallback ke horizontal di baris baru verticalPosition += 3; horizontalPosition = 1; placements.add(PlacedWord( word: word, startRow: verticalPosition, startCol: horizontalPosition, direction: WordDirection.horizontal, )); } } // Tempatkan kata-kata ke dalam grid for (var placement in placements) { placeWordInGrid(placement); } fillEmptySpaces(); } void placeWordInGrid(PlacedWord word) { int row = word.startRow; int col = word.startCol; for (int i = 0; i < word.word.length; i++) { if (row >= GRID_SIZE || col >= GRID_SIZE) break; switch (word.direction) { case WordDirection.horizontal: if (col + i < GRID_SIZE) { grid[row][col + i] = word.word[i]; } break; case WordDirection.vertical: if (row + i < GRID_SIZE) { grid[row + i][col] = word.word[i]; } break; case WordDirection.diagonal: if (row + i < GRID_SIZE && col + i < GRID_SIZE) { grid[row + i][col + i] = word.word[i]; } break; } } } void updateSelectedCells(Offset currentPosition) { if (startDrag == null) return; final RenderBox box = context.findRenderObject() as RenderBox; final Offset localStart = box.globalToLocal(startDrag!); final Offset localCurrent = box.globalToLocal(currentPosition); // Dapatkan ukuran dan posisi grid final gridStart = Offset(16.0, 120.0); // Sesuaikan dengan padding dan posisi grid final gridSize = box.size.width - 32.0; // Kurangi padding kiri dan kanan final cellSize = gridSize / GRID_SIZE; // Hitung posisi sel dengan mempertimbangkan offset grid int startRow = ((localStart.dy - gridStart.dy) ~/ cellSize).clamp(0, GRID_SIZE - 1); int startCol = ((localStart.dx - gridStart.dx) ~/ cellSize).clamp(0, GRID_SIZE - 1); int currentRow = ((localCurrent.dy - gridStart.dy) ~/ cellSize).clamp(0, GRID_SIZE - 1); int currentCol = ((localCurrent.dx - gridStart.dx) ~/ cellSize).clamp(0, GRID_SIZE - 1); setState(() { selectedCells = calculateSelectedCells(startRow, startCol, currentRow, currentCol); }); } bool isValidPosition(int row, int col) { return row >= 0 && row < GRID_SIZE && col >= 0 && col < GRID_SIZE; } List calculateSelectedCells( int startRow, int startCol, int endRow, int endCol) { List cells = []; // Hitung arah final int rowStep = startRow == endRow ? 0 : (endRow - startRow).sign; final int colStep = startCol == endCol ? 0 : (endCol - startCol).sign; // Hitung jumlah sel final int steps = max((endRow - startRow).abs(), (endCol - startCol).abs()); // Tambahkan sel ke seleksi for (int i = 0; i <= steps; i++) { final int row = startRow + (i * rowStep); final int col = startCol + (i * colStep); if (isValidPosition(row, col)) { cells.add(Offset(col.toDouble(), row.toDouble())); } } return cells; } void fillEmptySpaces() { final random = Random(); const letters = 'AEIMNRSTUW'; for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { if (grid[i][j].isEmpty) { grid[i][j] = letters[random.nextInt(letters.length)]; } } } } void checkSelection() { if (selectedCells.length < 2) return; String selectedWord = ''; List orderedCells = List.from(selectedCells); // Urutkan sel berdasarkan arah seleksi if (orderedCells.first.dx > orderedCells.last.dx) { // Jika seleksi dari kanan ke kiri, balik urutan sel orderedCells = orderedCells.reversed.toList(); } for (var cell in orderedCells) { int row = cell.dy.toInt(); int col = cell.dx.toInt(); selectedWord += grid[row][col]; } // Cek kata normal dan terbalik if ((selectedWord == orderedQuestions[currentQuestionIndex]['word'] || selectedWord == orderedQuestions[currentQuestionIndex]['word'] .split('') .reversed .join()) && !orderedQuestions[currentQuestionIndex]['isFound']) { setState(() { orderedQuestions[currentQuestionIndex]['isFound'] = true; correctCells.addAll(selectedCells); score += 10; // Unlock pertanyaan berikutnya jika ada if (currentQuestionIndex < orderedQuestions.length - 1) { currentQuestionIndex++; orderedQuestions[currentQuestionIndex]['isUnlocked'] = true; orderedQuestions[currentQuestionIndex]['isVisible'] = true; } showCorrectAnswerDialog(); // Cek apakah semua pertanyaan sudah ditemukan if (orderedQuestions.every((q) => q['isFound'])) { showCompletionDialog(); } }); } } void showCorrectAnswerDialog() { showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text('Jawaban Benar!'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Anda menemukan kata "${orderedQuestions[currentQuestionIndex - 1]['word']}"'), if (currentQuestionIndex < orderedQuestions.length) Text('\nPertanyaan selanjutnya telah dibuka'), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('OK'), ), ], ), ); } void showCompletionDialog() { // Simpan status level 3 selesai LevelManager.setLevelCompleted(3, true); showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text('Selamat!'), content: const Text('Anda telah menyelesaikan semua jawaban dengan benar.'), actions: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.refresh), onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const Level3HinduBuddha(), ), ); }, ), IconButton( icon: const Icon(Icons.menu), onPressed: () { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => LandingPage( onStart: () {}, onStats: () {}, ), ), (route) => false, ); }, ), IconButton( icon: const Icon(Icons.arrow_forward), onPressed: () { // Implementasi untuk level berikutnya // Navigator.push(context, MaterialPageRoute(builder: (context) => NextLevelPage())); }, ), ], ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ const SizedBox(height: 40), // Timer, Menu, dan Score dalam satu baris Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( 'Waktu: ${formatTime(seconds)}', style: const TextStyle(fontSize: 20, color: Colors.black), ), IconButton( icon: const Icon(Icons.menu), onPressed: () { _showMenuDialog(context); }, ), Text( 'Skor: $score', style: const TextStyle(fontSize: 20, color: Colors.black), ), ], ), const SizedBox(height: 20), // Word Search Grid dengan container yang jelas Expanded( child: Container( decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), child: GestureDetector( onPanStart: (details) { final RenderBox box = context.findRenderObject() as RenderBox; setState(() { startDrag = box.globalToLocal(details.globalPosition); selectedCells.clear(); }); }, onPanUpdate: (details) { updateSelectedCells(details.globalPosition); }, onPanEnd: (details) { checkSelection(); setState(() { startDrag = null; selectedCells.clear(); }); }, child: GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: GRID_SIZE, childAspectRatio: 1, ), itemCount: GRID_SIZE * GRID_SIZE, itemBuilder: (context, index) { final row = index ~/ GRID_SIZE; final col = index % GRID_SIZE; final isSelected = selectedCells .contains(Offset(col.toDouble(), row.toDouble())); final isCorrect = correctCells .contains(Offset(col.toDouble(), row.toDouble())); return Container( decoration: BoxDecoration( border: Border.all(color: Colors.grey.withOpacity(0.5)), color: isCorrect ? Colors.green.withOpacity(0.3) : isSelected ? Colors.blue.withOpacity(0.3) : Colors.white, ), child: Center( child: Text( grid[row][col], style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), ), ); }, ), ), ), ), // Pertanyaan yang sedang aktif Container( padding: const EdgeInsets.all(16), child: Text( orderedQuestions[currentQuestionIndex]['question'], style: const TextStyle( fontSize: 16, color: Colors.black, ), textAlign: TextAlign.center, ), ), ], ), ), ); } String formatTime(int seconds) { int minutes = seconds ~/ 60; int remainingSeconds = seconds % 60; return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}'; } void startTimer() { timer = Timer.periodic(Duration(seconds: 1), (timer) { setState(() { seconds++; }); }); } @override void dispose() { timer.cancel(); super.dispose(); } void _showMenuDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, // User harus memilih salah satu opsi builder: (BuildContext context) { return AlertDialog( backgroundColor: Colors.black.withOpacity(0.9), title: const Text( 'Menu Permainan', style: TextStyle(color: Colors.white), textAlign: TextAlign.center, ), content: Column( mainAxisSize: MainAxisSize.min, children: [ // Button Lanjut ElevatedButton( onPressed: () { Navigator.pop(context); // Kembali ke permainan }, style: ElevatedButton.styleFrom( minimumSize: const Size(200, 50), ), child: const Text('Lanjut'), ), const SizedBox(height: 10), // Button Restart ElevatedButton( onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => Level3HinduBuddha(), ), ); }, style: ElevatedButton.styleFrom( minimumSize: const Size(200, 50), ), child: const Text('Restart'), ), const SizedBox(height: 10), // Button Keluar ElevatedButton( onPressed: () { // Kembali ke landing page Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => LandingPage( onStart: () {}, onStats: () {}, ), ), (route) => false, // Hapus semua halaman sebelumnya ); }, style: ElevatedButton.styleFrom( minimumSize: const Size(200, 50), ), child: const Text('Keluar'), ), ], ), ); }, ); } } // Game level 1 class GameLevel3 extends FlameGame { @override Color backgroundColor() => Colors.white; @override Future onLoad() async { // Bisa ditambahkan komponen lain jika diperlukan } }