1238 lines
43 KiB
Dart
1238 lines
43 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'dart:async';
|
|
import 'dart:math';
|
|
import 'package:flame/game.dart';
|
|
import '../landing_page.dart';
|
|
import 'level2_hindubuddha.dart';
|
|
import '../utils/level_manager.dart';
|
|
import '../utils/audio_manager.dart';
|
|
import 'package:permainan_kata_anak_sd/services/database_service.dart';
|
|
import 'package:firebase_auth/firebase_auth.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 Level1HinduBuddha extends StatefulWidget {
|
|
const Level1HinduBuddha({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
_Level1HinduBuddhaState createState() => _Level1HinduBuddhaState();
|
|
}
|
|
|
|
class _Level1HinduBuddhaState extends State<Level1HinduBuddha> {
|
|
// Definisikan gridSize sebagai konstanta kelas
|
|
static const int GRID_SIZE = 12;
|
|
static const int MAX_TIME = 300; // 5 menit dalam detik
|
|
|
|
late Timer timer;
|
|
int seconds = MAX_TIME;
|
|
int elapsedSeconds =
|
|
0; // Tambah variabel untuk menghitung waktu yang digunakan
|
|
int score = 0;
|
|
|
|
// Data kata dan pertanyaan terkait dengan urutan
|
|
final List<Map<String, dynamic>> orderedQuestions = [
|
|
{
|
|
'word': 'YUPA',
|
|
'question':
|
|
'Salah satu peninggalan dari kerajaan Kutai',
|
|
'image': 'assets/images_hindubuddha/peninggalan_kutai.jpg',
|
|
'isFound': false,
|
|
'isUnlocked': true, // Pertanyaan pertama sudah unlocked
|
|
'isVisible': true, // Pertanyaan pertama sudah visible
|
|
},
|
|
{
|
|
'word': 'TARUMANEGARA',
|
|
'question':
|
|
'Kerajaan yang pernah dipimpin oleh Purnawarman',
|
|
'image': 'assets/images_raja/raja_tarumanegara.png',
|
|
'isFound': false,
|
|
'isUnlocked': false,
|
|
'isVisible': false,
|
|
},
|
|
{
|
|
'word': 'BALITUNG',
|
|
'question': 'Raja terkenal yang membawa Mataram menuju puncak kejayaan',
|
|
'image': 'assets/images_raja/raja_mataram.png',
|
|
'isFound': false,
|
|
'isUnlocked': false,
|
|
'isVisible': false,
|
|
},
|
|
{
|
|
'word': 'MATARAM',
|
|
'question': 'Kerajaan yang membangun Candi Borobudur',
|
|
'image': 'assets/images_hindubuddha/peninggalan_mataram.jpg',
|
|
'isFound': false,
|
|
'isUnlocked': false,
|
|
'isVisible': false,
|
|
},
|
|
{
|
|
'word': 'SUTASOMA',
|
|
'question':
|
|
'Karya sastra apa yang ditulis Mpu Tantular pada masa Majapahit',
|
|
'image': 'assets/images_hindubuddha/peninggalan_majapahit.png',
|
|
'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();
|
|
// Tampilkan notifikasi jumlah pertanyaan setelah build pertama
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_showQuestionCountDialog();
|
|
});
|
|
}
|
|
|
|
void initializeGame() {
|
|
grid = List.generate(GRID_SIZE, (_) => List.filled(GRID_SIZE, ''));
|
|
placeWords();
|
|
fillEmptySpaces();
|
|
}
|
|
|
|
void placeWords() {
|
|
// Definisikan kata-kata yang akan diacak
|
|
List<String> words = [
|
|
'YUPA',
|
|
'TARUMANEGARA',
|
|
'BALITUNG',
|
|
'MATARAM',
|
|
'SUTASOMA'
|
|
];
|
|
|
|
// Bersihkan grid
|
|
grid = List.generate(GRID_SIZE, (_) => List.filled(GRID_SIZE, ''));
|
|
|
|
// Posisi tetap untuk kata-kata horizontal (baris)
|
|
final positions = [2, 4, 6, 8]; // Jarak aman antar kata
|
|
|
|
// Acak urutan kata-kata kecuali TARUMANEGARA
|
|
final random = Random();
|
|
words.remove('TARUMANEGARA'); // Hapus sementara TARUMANEGARA
|
|
for (int i = 0; i < 5; i++) {
|
|
words.shuffle(random);
|
|
}
|
|
|
|
// Tempatkan TARUMANEGARA secara vertikal di awal
|
|
placeWordInGrid(PlacedWord(
|
|
word: 'TARUMANEGARA',
|
|
startRow: 0, // Mulai dari baris 0
|
|
startCol: 0, // Mulai dari kolom 0
|
|
direction: WordDirection.vertical,
|
|
));
|
|
|
|
// Tempatkan kata-kata lain secara horizontal
|
|
for (int i = 0; i < words.length; i++) {
|
|
String word = words[i];
|
|
int row = positions[i];
|
|
|
|
// Pastikan kata tidak melebihi batas grid
|
|
if (word.length > GRID_SIZE - 2) {
|
|
// Jika terlalu panjang, mulai dari kolom 1
|
|
placeWordInGrid(PlacedWord(
|
|
word: word,
|
|
startRow: row,
|
|
startCol: 1,
|
|
direction: WordDirection.horizontal,
|
|
));
|
|
} else {
|
|
// Jika cukup ruang, acak posisi kolom awal (hindari kolom 0)
|
|
int maxStartCol = GRID_SIZE - word.length - 1;
|
|
int startCol = random.nextInt(maxStartCol) + 2; // Mulai dari kolom 2
|
|
|
|
placeWordInGrid(PlacedWord(
|
|
word: word,
|
|
startRow: row,
|
|
startCol: startCol,
|
|
direction: WordDirection.horizontal,
|
|
));
|
|
}
|
|
}
|
|
|
|
fillEmptySpaces();
|
|
}
|
|
|
|
bool canPlaceWord(
|
|
String word, int startRow, int startCol, WordDirection direction) {
|
|
if (startRow < 0 || startCol < 0) return false;
|
|
|
|
int row = startRow;
|
|
int col = startCol;
|
|
|
|
for (int i = 0; i < word.length; i++) {
|
|
// Cek batas grid
|
|
if (row >= GRID_SIZE || col >= GRID_SIZE) return false;
|
|
|
|
// Cek apakah sel sudah terisi dengan huruf berbeda
|
|
if (grid[row][col].isNotEmpty && grid[row][col] != word[i]) {
|
|
return false;
|
|
}
|
|
|
|
// Pindah ke sel berikutnya sesuai arah
|
|
switch (direction) {
|
|
case WordDirection.horizontal:
|
|
col++;
|
|
break;
|
|
case WordDirection.vertical:
|
|
row++;
|
|
break;
|
|
case WordDirection.diagonal:
|
|
row++;
|
|
col++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void placeWordInGrid(PlacedWord word) {
|
|
int row = word.startRow;
|
|
int col = word.startCol;
|
|
|
|
for (int i = 0; i < word.word.length; i++) {
|
|
if (!isValidPosition(row, col)) break;
|
|
|
|
grid[row][col] = word.word[i];
|
|
|
|
switch (word.direction) {
|
|
case WordDirection.horizontal:
|
|
col++;
|
|
break;
|
|
case WordDirection.vertical:
|
|
row++;
|
|
break;
|
|
case WordDirection.diagonal:
|
|
row++;
|
|
col++;
|
|
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 += 20;
|
|
|
|
// 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() {
|
|
AudioManager.successsound();
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
height: 400,
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/dialog_complete.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'Anda menemukan kata "${orderedQuestions[currentQuestionIndex - 1]['word']}"',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 14,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
if (currentQuestionIndex < orderedQuestions.length)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 10),
|
|
child: Text(
|
|
'Pertanyaan selanjutnya telah dibuka',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 14,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
const SizedBox(height: 30),
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pop(context);
|
|
},
|
|
child: Container(
|
|
width: 150,
|
|
height: 50,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: const Center(
|
|
child: Text(
|
|
'OK',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 16,
|
|
color: Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void showCompletionDialog() async {
|
|
AudioManager.winsound();
|
|
timer.cancel(); // Hentikan timer ketika permainan selesai
|
|
// Simpan status level 1 selesai
|
|
LevelManager.setLevelCompleted(1, true, LevelManager.TYPE_HINDU_BUDDHA);
|
|
final user = FirebaseAuth.instance.currentUser;
|
|
if (user != null) {
|
|
await DatabaseService().saveScore(
|
|
userId: user.uid,
|
|
score: score,
|
|
formattedTime: formatTime(elapsedSeconds),
|
|
levelName: "Level 1",
|
|
category: "Hindu Buddha",
|
|
);
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
height: 400, // Sesuaikan tinggi background
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/dialog_complete.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
// Isi konten berada tepat di tengah gambar
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Anda telah menyelesaikan semua jawaban dengan benar.',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 14,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Total Skor: $score',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 12,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Total Waktu: ${formatTime(elapsedSeconds)}',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 12,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 25),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushReplacement(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const Level1HinduBuddha(),
|
|
),
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/restart.png'),
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 20),
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => LandingPage(
|
|
onStart: () {},
|
|
onStats: () {},
|
|
),
|
|
),
|
|
(route) => false,
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/menu.png'),
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 20),
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const Level2HinduBuddha(),
|
|
),
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/next.png'),
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
AudioManager.playClickSound();
|
|
_showMenuDialog(context);
|
|
return false;
|
|
},
|
|
child: Scaffold(
|
|
body: Container(
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/images/background_permainan.png'),
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
const SizedBox(height: 40),
|
|
|
|
// Header (Timer, Menu, Score)
|
|
Row(
|
|
children: [
|
|
// Kiri (Timer)
|
|
Flexible(
|
|
flex: 2,
|
|
child: Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 22.0),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 5),
|
|
width: 120,
|
|
height: 45,
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/grey_button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
'${formatTime(seconds)}',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.black,
|
|
fontFamily: 'Bestime',
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Tengah (Menu Icon)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
_showMenuDialog(context);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
|
child: Image.asset(
|
|
'assets/icons/menu.png',
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Kanan (Skor)
|
|
Flexible(
|
|
flex: 2,
|
|
child: Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 22.0),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 5),
|
|
width: 120,
|
|
height: 45,
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/grey_button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
'$score',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
color: Colors.black,
|
|
fontFamily: 'Bestime',
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: 15),
|
|
|
|
// Grid Area
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
physics:
|
|
const NeverScrollableScrollPhysics(), // Prevent scrolling while selecting
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
children: [
|
|
// Word Search Grid
|
|
AspectRatio(
|
|
aspectRatio: 1, // Make grid square
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: GestureDetector(
|
|
onPanStart: (details) {
|
|
setState(() {
|
|
startDrag = details.globalPosition;
|
|
selectedCells.clear();
|
|
});
|
|
},
|
|
onPanUpdate: (details) {
|
|
updateSelectedCells(details.globalPosition);
|
|
},
|
|
onPanEnd: (details) {
|
|
checkSelection();
|
|
setState(() {
|
|
startDrag = null;
|
|
selectedCells.clear();
|
|
});
|
|
},
|
|
child: GridView.builder(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
padding: const EdgeInsets.all(16.0),
|
|
gridDelegate:
|
|
SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: GRID_SIZE,
|
|
childAspectRatio: 1,
|
|
crossAxisSpacing: 1,
|
|
mainAxisSpacing: 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,
|
|
fontFamily: 'Bestime',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Gambar peninggalan
|
|
Container(
|
|
height: 200,
|
|
width: double.infinity,
|
|
margin: const EdgeInsets.symmetric(vertical: 16),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Image.asset(
|
|
orderedQuestions[currentQuestionIndex]['image'],
|
|
fit: BoxFit.fill,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Container(
|
|
color: Colors.grey[200],
|
|
child: const Center(
|
|
child: Icon(
|
|
Icons.image_not_supported,
|
|
size: 60,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
|
|
// Pertanyaan yang sedang aktif
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Stack(
|
|
children: [
|
|
// Outline text
|
|
Text(
|
|
orderedQuestions[currentQuestionIndex]
|
|
['question'],
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 15,
|
|
foreground: Paint()
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 2
|
|
..color =
|
|
const Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
// Fill text
|
|
Text(
|
|
orderedQuestions[currentQuestionIndex]
|
|
['question'],
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 15,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
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(() {
|
|
if (seconds > 0) {
|
|
seconds--;
|
|
elapsedSeconds++; // Increment waktu yang telah berlalu
|
|
} else {
|
|
timer.cancel();
|
|
showTimeUpDialog();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
void showTimeUpDialog() async {
|
|
AudioManager.losesound();
|
|
final user = FirebaseAuth.instance.currentUser;
|
|
if (user != null) {
|
|
await DatabaseService().saveScore(
|
|
userId: user.uid,
|
|
score: score,
|
|
formattedTime: formatTime(elapsedSeconds),
|
|
levelName: "Level 1",
|
|
category: "Hindu Buddha",
|
|
);
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
height: 400, // Sesuaikan dengan tinggi gambar background
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/timeout.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Waktu permainan telah habis.',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 14,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Total Skor: $score',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 12,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Total Waktu: ${formatTime(elapsedSeconds)}',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 12,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 30),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushReplacement(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const Level1HinduBuddha(),
|
|
),
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/restart.png'),
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 20),
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => LandingPage(
|
|
onStart: () {},
|
|
onStats: () {},
|
|
),
|
|
),
|
|
(route) => false,
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 40,
|
|
height: 40,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/menu.png'),
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
timer.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
void _showMenuDialog(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
height: 400,
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/cover_menu.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// Button Lanjut
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pop(context);
|
|
},
|
|
child: Container(
|
|
width: 170,
|
|
height: 50,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/grey_button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: const Center(
|
|
child: Text(
|
|
'Lanjut',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 16,
|
|
color: Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 18),
|
|
// Button Restart
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushReplacement(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const Level1HinduBuddha(),
|
|
),
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 170,
|
|
height: 50,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/grey_button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: const Center(
|
|
child: Text(
|
|
'Restart',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 16,
|
|
color: Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 18),
|
|
// Button Keluar
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pushAndRemoveUntil(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => LandingPage(
|
|
onStart: () {},
|
|
onStats: () {},
|
|
),
|
|
),
|
|
(route) => false,
|
|
);
|
|
},
|
|
child: Container(
|
|
width: 170,
|
|
height: 50,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/grey_button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: const Center(
|
|
child: Text(
|
|
'Keluar',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 16,
|
|
color: Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _showQuestionCountDialog() {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: double.infinity,
|
|
height: 300,
|
|
decoration: BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/covertext.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'Level ini memiliki total ${orderedQuestions.length} pertanyaan.',
|
|
style: const TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 14,
|
|
color: Color.fromARGB(255, 182, 134, 86),
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 30),
|
|
GestureDetector(
|
|
onTap: () {
|
|
AudioManager.playClickSound();
|
|
Navigator.pop(context);
|
|
},
|
|
child: Container(
|
|
width: 150,
|
|
height: 50,
|
|
decoration: const BoxDecoration(
|
|
image: DecorationImage(
|
|
image: AssetImage('assets/icons/button.png'),
|
|
fit: BoxFit.fill,
|
|
),
|
|
),
|
|
child: const Center(
|
|
child: Text(
|
|
'Mulai',
|
|
style: TextStyle(
|
|
fontFamily: 'Bestime',
|
|
fontSize: 16,
|
|
color: Color.fromARGB(255, 65, 44, 23),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Game level 1
|
|
class GameLevel1 extends FlameGame {
|
|
@override
|
|
Color backgroundColor() => Colors.white;
|
|
|
|
@override
|
|
Future<void> onLoad() async {
|
|
// Bisa ditambahkan komponen lain jika diperlukan
|
|
}
|
|
}
|