MIF_E31222389/lib/game/level3_hindubuddha.dart

632 lines
19 KiB
Dart

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<Level3HinduBuddha> {
// 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<Map<String, dynamic>> 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<List<String>> grid;
String? lastFoundWord;
// Untuk seleksi kata
Offset? startDrag;
Offset? currentDrag;
List<Offset> selectedCells = [];
List<Offset> 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<String> 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<PlacedWord> 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<Offset> calculateSelectedCells(
int startRow, int startCol, int endRow, int endCol) {
List<Offset> 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<Offset> 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<void> onLoad() async {
// Bisa ditambahkan komponen lain jika diperlukan
}
}