Added screens: pause, game over. Added modes: game by levels, game by time limit. Added sounds. Added database for storing points.

This commit is contained in:
Alex Vasilev 2024-09-27 16:40:14 +03:00
parent dd2ac2fcce
commit f4c1e7697c
57 changed files with 3505 additions and 799 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 12H5" stroke="#202020" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 19L5 12L12 5" stroke="#202020" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 11.2686V12.0046C19.999 13.7297 19.4404 15.4083 18.4075 16.79C17.3745 18.1718 15.9226 19.1826 14.2683 19.6717C12.6139 20.1608 10.8458 20.1021 9.22757 19.5042C7.60934 18.9064 6.22772 17.8015 5.28877 16.3542C4.34981 14.907 3.90383 13.195 4.01734 11.4736C4.13085 9.75223 4.79777 8.11364 5.91862 6.80224C7.03948 5.49083 8.55423 4.57688 10.2369 4.1967C11.9197 3.81651 13.6802 3.99045 15.256 4.69258" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 6L12.0769 13L10 10.9021" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 718 B

3
assets/svg/check.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 5L7.05799 13.3614C6.6736 13.7208 6.0764 13.7208 5.69201 13.3614L2 9.90909" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 9L12 15L18 9" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 18L15 12L9 6" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

4
assets/svg/clipboard.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 3H13.5C13.8978 3 14.2794 3.16389 14.5607 3.45561C14.842 3.74733 15 4.143 15 4.55556V15.4444C15 15.857 14.842 16.2527 14.5607 16.5444C14.2794 16.8361 13.8978 17 13.5 17H4.5C4.10218 17 3.72064 16.8361 3.43934 16.5444C3.15804 16.2527 3 15.857 3 15.4444V4.55556C3 4.143 3.15804 3.74733 3.43934 3.45561C3.72064 3.16389 4.10218 3 4.5 3H6" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.25 2H6.75C6.33579 2 6 2.33579 6 2.75V4.25C6 4.66421 6.33579 5 6.75 5H11.25C11.6642 5 12 4.66421 12 4.25V2.75C12 2.33579 11.6642 2 11.25 2Z" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 772 B

5
assets/svg/music.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.66667 13.6667V4.40268C6.66667 3.91384 7.02008 3.49665 7.50227 3.41629L14.8356 2.19407C15.4451 2.09248 16 2.56252 16 3.18046V12.1111" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.33333 16C5.622 16 6.66667 14.9553 6.66667 13.6666C6.66667 12.378 5.622 11.3333 4.33333 11.3333C3.04467 11.3333 2 12.378 2 13.6666C2 14.9553 3.04467 16 4.33333 16Z" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.6667 13.6667C14.9553 13.6667 16 12.8832 16 11.9167C16 10.9502 14.9553 10.1667 13.6667 10.1667C12.378 10.1667 11.3333 10.9502 11.3333 11.9167C11.3333 12.8832 12.378 13.6667 13.6667 13.6667Z" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 882 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="#202020" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 15V9" stroke="#202020" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 15V9" stroke="#202020" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 514 B

3
assets/svg/star.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.09177 2.97113C8.44903 2.19577 9.55097 2.19577 9.90823 2.97114L11.3156 6.02547C11.4659 6.35169 11.7796 6.57224 12.1374 6.60326L15.5552 6.89953C16.4483 6.97695 16.7963 8.0998 16.1034 8.66866L13.6245 10.704C13.3282 10.9473 13.1971 11.3386 13.287 11.7113L14.0284 14.7861C14.2326 15.6326 13.3324 16.3174 12.5711 15.8949L9.48525 14.1824C9.18343 14.0149 8.81657 14.0149 8.51475 14.1824L5.42894 15.8949C4.66759 16.3174 3.76743 15.6326 3.97156 14.7861L4.71303 11.7113C4.80291 11.3386 4.67178 10.9473 4.37547 10.704L1.89657 8.66866C1.20374 8.0998 1.55168 6.97695 2.44478 6.89953L5.86258 6.60326C6.22042 6.57224 6.53414 6.35169 6.68445 6.02547L8.09177 2.97113Z" stroke="#202020" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 804 B

6
assets/svg/ticket.svg Normal file
View File

@ -0,0 +1,6 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.88727 3.1875V5.0025" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.88727 13.3198V14.8378" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.88727 10.7429V7.1272" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 15C15.3666 15 16 14.3496 16 13V11.113C16 10.8368 15.7711 10.6206 15.5074 10.5388C14.8453 10.3334 14.3675 9.72409 14.3675 9.00077C14.3675 8.27745 14.8453 7.66765 15.5074 7.46203C15.7711 7.38013 16 7.16394 16 6.88779V5C16 3.65044 15.3658 3 14 3H3C1.63423 3 1 3.65044 1 5V6.95114C1 7.22728 1.22831 7.44341 1.4937 7.51969C2.15524 7.70983 2.63251 8.27785 2.63251 9.00077C2.63251 9.72409 2.15473 10.3334 1.49263 10.5388C1.22889 10.6206 1 10.8368 1 11.113V13C1 14.3496 1.63345 15 3 15H14Z" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

4
assets/svg/volume.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3333 3.21478C11.3333 2.35385 10.3178 1.89533 9.67201 2.46467L6.17233 5.55011C5.98967 5.71115 5.75453 5.8 5.51102 5.8H4C2.89543 5.8 2 6.69543 2 7.8V10.2C2 11.3046 2.89543 12.2 4 12.2H5.51102C5.75453 12.2 5.98967 12.2889 6.17233 12.4499L9.67202 15.5353C10.3178 16.1047 11.3333 15.6461 11.3333 14.7852V3.21478Z" stroke="#202020" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.4444 6.22815C15.3744 6.78144 16 7.81559 16 9.00005C16 10.1845 15.3744 11.2186 14.4444 11.7719" stroke="#202020" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 679 B

103
lib/db/main_db.dart Normal file
View File

@ -0,0 +1,103 @@
import 'package:isar/isar.dart';
import 'package:match_magic/models/isar/high_score.dart';
import 'package:match_magic/models/isar/settings.dart';
import 'package:path_provider/path_provider.dart';
class MainDB {
MainDB._();
static MainDB? _instance;
static MainDB get instance => _instance ??= MainDB._();
Isar? _isar;
Isar get isar => _isar!;
Future<bool> initMainDB({Isar? mock}) async {
final dir = await getApplicationDocumentsDirectory();
if (mock != null) {
_isar = mock;
return true;
}
if (_isar != null && isar.isOpen) return false;
_isar = await Isar.open(
[SettingsSchema, HighScoreSchema],
directory: dir.path,
inspector: false,
);
return true;
}
Future<void> saveSoundEnabled(bool isEnabled) async {
print('saveSoundEnabled $isEnabled');
final settings = await isar.settings.where().findFirst();
await isar.writeTxn(() async {
if (settings != null) {
settings.isSoundEnabled = isEnabled;
await isar.settings.put(settings);
} else {
final newSettings = Settings(
isSoundEnabled: isEnabled,
isMusicEnabled: true,
);
await isar.settings.put(newSettings);
}
});
}
Future<void> saveMusicEnabled(bool isEnabled) async {
final settings = await isar.settings.where().findFirst();
await isar.writeTxn(() async {
if (settings != null) {
settings.isMusicEnabled = isEnabled;
await isar.settings.put(settings);
} else {
final newSettings = Settings(
isSoundEnabled: true,
isMusicEnabled: isEnabled,
);
await isar.settings.put(newSettings);
}
});
}
Future<bool> getSoundEnabled() async {
final settings = await isar.settings.where().findFirst();
return settings?.isSoundEnabled ?? true;
}
Future<bool> getMusicEnabled() async {
final settings = await isar.settings.where().findFirst();
return settings?.isMusicEnabled ?? true;
}
Future<void> addHighScore(int score, Function()? callback) async {
final highScore = HighScore(
highScoreValue: score,
);
await isar.writeTxn(() async {
final existingHighScore = await isar.highScores.where().findFirst();
if (existingHighScore != null) {
if (score > existingHighScore.highScoreValue) {
existingHighScore.highScoreValue = score;
await isar.highScores.put(existingHighScore);
} else {
await isar.highScores.put(existingHighScore);
}
} else {
await isar.highScores.put(highScore);
}
});
if (callback != null) {
callback();
}
}
Future<int?> getHighScoreValue() async {
final highScore = await isar.highScores.where().findFirst();
return highScore?.highScoreValue;
}
}

View File

@ -3,40 +3,65 @@ import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/game.dart'; import 'package:flame/game.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:match_magic/db/main_db.dart';
import 'package:match_magic/game/game_mode_manager.dart';
import 'package:match_magic/game/sprite_loader.dart';
import 'package:match_magic/screens/game_over_screen.dart';
import 'package:match_magic/utilities/audio_manager.dart'; import 'package:match_magic/utilities/audio_manager.dart';
import 'package:match_magic/widgets/overlays/game_overlay/hint_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/pause_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/restart_button.dart';
import 'tile.dart'; import 'tile.dart';
import 'package:flame/sprite.dart';
import 'swap_notifier.dart';
class Board extends FlameGame { class Board extends FlameGame {
final List<Sprite> sprites; BuildContext context;
final SwapNotifier swapNotifier; final bool gameMode;
final Sprite magicCubeSprite; late GameModeManager gameModeManager;
static const int rows = 8; static const int rows = 8;
static const int cols = 8; static const int cols = 8;
late double tileSize; late double tileSize;
Tile? selectedTile;
List<List<Tile?>> tiles = []; List<List<Tile?>> tiles = [];
int? selectedRow; int? selectedRow;
int? selectedCol; int? selectedCol;
bool animating = false; bool animating = false;
bool isGridInitialized = false;
Tile? lastMovedTile; Tile? lastMovedTile;
Tile? lastMovedByGravity; Tile? lastMovedByGravity;
static int score = 0;
late TextComponent _playerScore;
late TextComponent _playerMoves;
late TextComponent _remainingTime;
bool isFirstLaunch = true; bool isFirstLaunch = true;
Board({ bool isGameOver = false;
required this.sprites, static bool isSoundPlaying = true;
required this.swapNotifier,
required this.magicCubeSprite, Board(
}); this.context, {
required this.gameMode,
}) {
gameModeManager = GameModeManager(
currentMode: gameMode,
onGameOver: _onGameOver,
);
}
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
super.onLoad(); super.onLoad();
_resetGame(); await loadImages();
gameModeManager.initializeMode();
newGame();
} }
void _resetGame() { void newGame() {
isFirstLaunch = true;
resetGame();
gameModeManager.initializeMode();
tiles.clear(); tiles.clear();
selectedRow = null; selectedRow = null;
selectedCol = null; selectedCol = null;
@ -44,16 +69,82 @@ class Board extends FlameGame {
tileSize = size.x / cols; tileSize = size.x / cols;
_initializeGrid(isFirstLaunch); _initializeGrid(isFirstLaunch);
_removeInitialMatches(); _removeInitialMatches();
isFirstLaunch = false; isFirstLaunch = false;
_playerScore = TextComponent(
text: 'Score: ',
position: Vector2(60, 70),
anchor: Anchor.centerLeft,
);
add(_playerScore);
if (gameModeManager.currentMode) {
_playerMoves = TextComponent(
text: 'Moves: ',
position: Vector2(200, 70),
anchor: Anchor.centerLeft,
);
add(_playerMoves);
} else {
_remainingTime = TextComponent(
text: 'Time: ',
position: Vector2(200, 70),
anchor: Anchor.centerLeft,
);
add(_remainingTime);
}
} }
void restartGame() { void resetGame() {
isFirstLaunch = true; isGameOver = false;
swapNotifier.resetScore(); this.overlays.remove(GameOverMenu.id);
_resetGame();
score = 0;
gameModeManager.resetMode();
selectedTile = null;
final List<Component> tilesToRemove = [];
for (final component in children) {
if (component is Tile) {
tilesToRemove.add(component);
}
}
for (final tile in tilesToRemove) {
remove(tile);
}
}
@override
void render(Canvas canvas) {
super.render(canvas);
// Progress bar renderer in level mode
if (gameModeManager.currentMode == GameMode.levelProgression) {
final progressBarWidth = size.x * (score / gameModeManager.targetScore);
final progressBarHeight = 20.0;
final rect = Rect.fromLTWH(10, size.y - progressBarHeight - 10,
progressBarWidth, progressBarHeight);
final paint = Paint()..color = Colors.green;
canvas.drawRect(rect, paint);
}
}
@override
void update(double dt) {
_playerScore.text = 'Score: $score';
if (gameModeManager.currentMode) {
_playerMoves.text = 'Moves: ${GameModeManager.movesLeft}';
} else {
_remainingTime.text = 'Time: ${gameModeManager.remainingTime}';
}
if (!isGameOver && gameModeManager.isGameOverCondition()) {
_onGameOver();
}
super.update(dt);
} }
void _initializeGrid(bool animate) { void _initializeGrid(bool animate) {
int totalTiles = rows * cols;
int completedTiles = 0;
for (int row = 0; row < rows; row++) { for (int row = 0; row < rows; row++) {
List<Tile?> rowTiles = []; List<Tile?> rowTiles = [];
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
@ -64,7 +155,7 @@ class Board extends FlameGame {
: Vector2(col * tileSize, row * tileSize); : Vector2(col * tileSize, row * tileSize);
var tile = Tile( var tile = Tile(
sprite: sprites[spriteIndex], sprite: Tile.crystals[spriteIndex],
spriteIndex: spriteIndex, spriteIndex: spriteIndex,
size: Vector2.all(tileSize), size: Vector2.all(tileSize),
position: initialPosition, position: initialPosition,
@ -80,22 +171,31 @@ class Board extends FlameGame {
tile.add( tile.add(
MoveEffect.to( MoveEffect.to(
Vector2(col * tileSize, row * tileSize), Vector2(col * tileSize, row * tileSize + 200),
EffectController( EffectController(
duration: 0.5, duration: 0.5,
startDelay: delay, startDelay: delay,
curve: Curves.bounceOut, curve: Curves.bounceOut,
), ),
onComplete: () {
completedTiles++;
if (completedTiles == totalTiles) {
isGridInitialized = true;
}
},
), ),
); );
} }
} }
tiles.add(rowTiles); tiles.add(rowTiles);
} }
if (!animate) {
isGridInitialized = true;
}
} }
int _randomElement() { int _randomElement() {
return Random().nextInt(sprites.length); return Random().nextInt(Tile.crystals.length);
} }
void _removeInitialMatches() { void _removeInitialMatches() {
@ -106,7 +206,7 @@ class Board extends FlameGame {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
if (_hasMatch(row, col)) { if (_hasMatch(row, col)) {
int spriteIndex = _randomElement(); int spriteIndex = _randomElement();
tiles[row][col]!.sprite = sprites[spriteIndex]; tiles[row][col]!.sprite = Tile.crystals[spriteIndex];
tiles[row][col]!.spriteIndex = spriteIndex; tiles[row][col]!.spriteIndex = spriteIndex;
hasMatches = true; hasMatches = true;
} }
@ -115,53 +215,53 @@ class Board extends FlameGame {
} while (hasMatches); } while (hasMatches);
} }
// Future<void> handleTileSwipe(Tile tile, Vector2 delta) async { int _calculateScore(int matchLength) {
// if (animating) return; if (matchLength == 3) {
return 50;
} else if (matchLength == 4) {
return 400;
} else if (matchLength == 5) {
return 200;
}
return 0;
}
// int row = tile.row; // Moving elements
// int col = tile.col;
// Tile? targetTile;
// if (delta.x.abs() > delta.y.abs()) { void selectTile(Tile tile) {
// if (delta.x > 0 && col < cols - 1) { if (selectedTile == null) {
// targetTile = tiles[row][col + 1]; selectedTile = tile;
// } else if (delta.x < 0 && col > 0) { tile.select();
// targetTile = tiles[row][col - 1]; } else {
// } if (_isNeighbor(selectedTile!, tile) || selectedTile!.isMagicCube) {
// } else { } else {
// if (delta.y > 0 && row < rows - 1) { selectedTile?.deselect();
// targetTile = tiles[row + 1][col]; selectedTile = tile;
// } else if (delta.y < 0 && row > 0) { tile.select();
// targetTile = tiles[row - 1][col]; }
// } }
// } }
// if (targetTile != null) { bool _isNeighbor(Tile tile1, Tile tile2) {
// animating = true; return (tile1.row == tile2.row && (tile1.col - tile2.col).abs() == 1) ||
// lastMovedTile = tile; (tile1.col == tile2.col && (tile1.row - tile2.row).abs() == 1);
// swapTiles(tile, targetTile, true); }
// await Future.delayed(const Duration(milliseconds: 300));
// if (!checkMatches()) {
// swapTiles(tile, targetTile, true);
// } else {
// swapNotifier.incrementMoveCount();
// }
// selectedRow = null;
// selectedCol = null;
// animating = false;
// }
// }
Future<void> handleTileSwipe(Tile tile, Vector2 delta) async { Future<void> handleTileSwipe(Tile tile, Vector2 delta) async {
if (animating) return; if (!isGridInitialized || animating) return;
Tile? targetTile; Tile? targetTile;
if (tile.isMagicCube) { if (tile.isMagicCube) {
targetTile = _getTileBySwipeDirection(tile, delta); targetTile = _getTileBySwipeDirection(tile, delta);
if (targetTile != null) { if (targetTile != null) {
_removeAllOfType(targetTile.spriteIndex); _removeAllOfType(targetTile.spriteIndex);
_animateRemoveTile(tile);
isSoundPlaying = await MainDB.instance.getSoundEnabled();
if (isSoundPlaying) {
AudioManager.playExplosionSound();
}
tiles[tile.row][tile.col] = null;
} }
return; return;
} }
@ -174,11 +274,11 @@ class Board extends FlameGame {
swapTiles(tile, targetTile, true); swapTiles(tile, targetTile, true);
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
bool matchesFound = await checkMatches();
if (!checkMatches()) { if (!matchesFound) {
swapTiles(tile, targetTile, true); swapTiles(tile, targetTile, true);
} else { } else {
swapNotifier.incrementMoveCount(); GameModeManager.movesLeft--;
} }
selectedRow = null; selectedRow = null;
selectedCol = null; selectedCol = null;
@ -207,28 +307,7 @@ class Board extends FlameGame {
return targetTile; return targetTile;
} }
void _removeAllOfType(int spriteIndex) {
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (tiles[row][col]?.spriteIndex == spriteIndex) {
_animateRemoveTile(tiles[row][col]!);
tiles[row][col] = null;
}
}
}
_applyGravity();
_fillEmptySpaces();
}
bool _isAdjacent(int row1, int col1, int row2, int col2) {
return (row1 == row2 && (col1 - col2).abs() == 1) ||
(col1 == col2 && (row1 - row2).abs() == 1);
}
void swapTiles(Tile tile1, Tile tile2, bool animate) { void swapTiles(Tile tile1, Tile tile2, bool animate) {
// final tempPosition1 = tile1.position.clone();
// final tempPosition2 = tile2.position.clone();
final tempRow1 = tile1.row; final tempRow1 = tile1.row;
final tempCol1 = tile1.col; final tempCol1 = tile1.col;
final tempRow2 = tile2.row; final tempRow2 = tile2.row;
@ -252,7 +331,49 @@ class Board extends FlameGame {
tile1.deselect(); tile1.deselect();
} }
bool checkMatches({bool simulate = false}) { // Hint button
Future<Tile?> findHint() async {
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
Tile? tile = tiles[row][col];
if (col < cols - 1 && await _canSwap(row, col, row, col + 1)) {
return tile;
}
if (row < rows - 1 && await _canSwap(row, col, row + 1, col)) {
return tile;
}
}
}
return null;
}
Future<bool> _canSwap(int row1, int col1, int row2, int col2) async {
Tile tempTile1 = tiles[row1][col1]!;
Tile tempTile2 = tiles[row2][col2]!;
tiles[row1][col1] = tempTile2;
tiles[row2][col2] = tempTile1;
bool matchFound = await checkMatches(simulate: true);
tiles[row1][col1] = tempTile1;
tiles[row2][col2] = tempTile2;
return matchFound;
}
void showHint() async {
Tile? hintTile = await findHint();
if (hintTile != null) {
hintTile.select();
}
}
// Match checks
Future<bool> checkMatches({bool simulate = false}) async {
if (simulate) { if (simulate) {
for (int row = 0; row < rows; row++) { for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) { for (int col = 0; col < cols; col++) {
@ -279,7 +400,10 @@ class Board extends FlameGame {
for (final match in matches) { for (final match in matches) {
points += _removeMatchedElements(match[0], match[1]); points += _removeMatchedElements(match[0], match[1]);
} }
AudioManager.playSelectSound(); isSoundPlaying = await MainDB.instance.getSoundEnabled();
if (isSoundPlaying) {
AudioManager.playSelectSound();
}
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
_applyGravity(); _applyGravity();
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
@ -290,7 +414,7 @@ class Board extends FlameGame {
}); });
}); });
}); });
swapNotifier.incrementScore(points); score += points;
return true; return true;
} }
@ -302,46 +426,81 @@ class Board extends FlameGame {
final value = tiles[row][col]?.spriteIndex; final value = tiles[row][col]?.spriteIndex;
int count = 1; int count = 1;
for (int i = col + 1; i < cols && tiles[row][i]?.spriteIndex == value; i++) for (int i = col + 1;
i < cols && tiles[row][i]?.spriteIndex == value;
i++) {
count++; count++;
for (int i = col - 1; i >= 0 && tiles[row][i]?.spriteIndex == value; i--) }
for (int i = col - 1; i >= 0 && tiles[row][i]?.spriteIndex == value; i--) {
count++; count++;
}
if (count >= 3) return true; if (count >= 3) return true;
count = 1; count = 1;
for (int i = row + 1; i < rows && tiles[i][col]?.spriteIndex == value; i++) for (int i = row + 1;
i < rows && tiles[i][col]?.spriteIndex == value;
i++) {
count++; count++;
for (int i = row - 1; i >= 0 && tiles[i][col]?.spriteIndex == value; i--) }
for (int i = row - 1; i >= 0 && tiles[i][col]?.spriteIndex == value; i--) {
count++; count++;
}
return count >= 3; return count >= 3;
} }
// Removing items
void _removeAllOfType(int spriteIndex) {
int removedCount = 0;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (tiles[row][col]?.spriteIndex == spriteIndex) {
_animateRemoveTile(tiles[row][col]!);
tiles[row][col] = null;
removedCount++;
}
}
}
score += removedCount * 100;
Future.delayed(const Duration(milliseconds: 300), () {
_applyGravity();
Future.delayed(const Duration(milliseconds: 300), () {
_fillEmptySpaces();
});
});
}
int _removeMatchedElements(int row, int col) { int _removeMatchedElements(int row, int col) {
int score = 0; int score = 0;
final int? value = tiles[row][col]?.spriteIndex; final int? value = tiles[row][col]?.spriteIndex;
bool bombTriggered = false; bool specialTriggered = false;
Tile? tileToTransformIntoBomb = null; Tile? tileToTransformIntoSpecial;
int left = col; int left = col;
while (left > 0 && tiles[row][left - 1]?.spriteIndex == value) left--; while (left > 0 && tiles[row][left - 1]?.spriteIndex == value) {
left--;
}
int right = col; int right = col;
while (right < cols - 1 && tiles[row][right + 1]?.spriteIndex == value) while (right < cols - 1 && tiles[row][right + 1]?.spriteIndex == value) {
right++; right++;
}
if (right - left + 1 >= 3) { if (right - left + 1 >= 3) {
score += _calculateScore(right - left + 1); score += _calculateScore(right - left + 1);
if (right - left + 1 >= 4) { if (right - left + 1 == 4) {
tileToTransformIntoBomb = lastMovedTile ?? lastMovedByGravity; tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity;
} else if (right - left + 1 >= 5) {
tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity;
} }
for (int i = left; i <= right; i++) { for (int i = left; i <= right; i++) {
if (tiles[row][i] != null) { if (tiles[row][i] != null) {
if (tiles[row][i]!.isBomb) { if (tiles[row][i]!.isBomb) {
bombTriggered = true; specialTriggered = true;
_triggerBomb(row, i); _triggerBomb(row, i);
} }
if (tiles[row][i] != tileToTransformIntoBomb) { if (tiles[row][i] != tileToTransformIntoSpecial) {
_animateRemoveTile(tiles[row][i]!); _animateRemoveTile(tiles[row][i]!);
tiles[row][i] = null; tiles[row][i] = null;
} }
@ -350,25 +509,30 @@ class Board extends FlameGame {
} }
int top = row; int top = row;
while (top > 0 && tiles[top - 1][col]?.spriteIndex == value) top--; while (top > 0 && tiles[top - 1][col]?.spriteIndex == value) {
top--;
}
int bottom = row; int bottom = row;
while (bottom < rows - 1 && tiles[bottom + 1][col]?.spriteIndex == value) while (bottom < rows - 1 && tiles[bottom + 1][col]?.spriteIndex == value) {
bottom++; bottom++;
}
if (bottom - top + 1 >= 3) { if (bottom - top + 1 >= 3) {
score += _calculateScore(bottom - top + 1); score += _calculateScore(bottom - top + 1);
if (bottom - top + 1 >= 4) { if (bottom - top + 1 == 4) {
tileToTransformIntoBomb = lastMovedTile ?? lastMovedByGravity; tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity;
} else if (bottom - top + 1 >= 5) {
tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity;
} }
for (int i = top; i <= bottom; i++) { for (int i = top; i <= bottom; i++) {
if (tiles[i][col] != null) { if (tiles[i][col] != null) {
if (tiles[i][col]!.isBomb) { if (tiles[i][col]!.isBomb) {
bombTriggered = true; specialTriggered = true;
_triggerBomb(i, col); _triggerBomb(i, col);
} }
if (tiles[i][col] != tileToTransformIntoBomb) { if (tiles[i][col] != tileToTransformIntoSpecial) {
_animateRemoveTile(tiles[i][col]!); _animateRemoveTile(tiles[i][col]!);
tiles[i][col] = null; tiles[i][col] = null;
} }
@ -376,19 +540,82 @@ class Board extends FlameGame {
} }
} }
if (bombTriggered) { if (tileToTransformIntoSpecial != null) {
_triggerBomb(row, col); if ((right - left + 1 >= 5) || (bottom - top + 1 >= 5)) {
_createMagicCube(
tileToTransformIntoSpecial.row, tileToTransformIntoSpecial.col);
} else {
_createBomb(
tileToTransformIntoSpecial.row, tileToTransformIntoSpecial.col);
}
} }
if (tileToTransformIntoBomb != null) { if (specialTriggered) {
_createBomb(tileToTransformIntoBomb.row, tileToTransformIntoBomb.col); _triggerBomb(row, col);
AudioManager.playFourElementsSound();
} }
return score; return score;
} }
void _triggerBomb(int row, int col) { void _animateRemoveTile(Tile tile) {
tile.add(ScaleEffect.to(
Vector2.zero(),
EffectController(
duration: 0.2,
curve: Curves.easeInBack,
),
onComplete: () => tile.removeFromParent(),
));
}
// The emergence of new elements
void _applyGravity() {
for (int col = 0; col < cols; col++) {
for (int row = rows - 1; row >= 0; row--) {
if (tiles[row][col] == null) {
for (int k = row - 1; k >= 0; k--) {
if (tiles[k][col] != null) {
tiles[row][col] = tiles[k][col]!;
tiles[k][col] = null;
tiles[row][col]!.row = row;
tiles[row][col]!.animateMoveTo(
Vector2(col * tileSize, row * tileSize + 200), () {});
lastMovedByGravity = tiles[row][col];
break;
}
}
}
}
}
}
void _fillEmptySpaces() {
for (int col = 0; col < cols; col++) {
for (int row = rows - 1; row >= 0; row--) {
if (tiles[row][col] == null) {
int spriteIndex = _randomElement();
var tile = Tile(
sprite: Tile.crystals[spriteIndex],
spriteIndex: spriteIndex,
size: Vector2.all(tileSize),
position: Vector2(col * tileSize, -tileSize),
row: row,
col: col,
onSwipe: handleTileSwipe,
);
tiles[row][col] = tile;
add(tile);
tile.animateMoveTo(
Vector2(col * tileSize, row * tileSize + 200), () {});
}
}
}
}
// Bomb
void _triggerBomb(int row, int col) async {
final tile = tiles[row][col]; final tile = tiles[row][col];
if (tile == null || !tile.isBomb) return; if (tile == null || !tile.isBomb) return;
@ -412,7 +639,10 @@ class Board extends FlameGame {
_animateRemoveTile(tile); _animateRemoveTile(tile);
tiles[row][col] = null; tiles[row][col] = null;
AudioManager.playExplosionSound(); isSoundPlaying = await MainDB.instance.getSoundEnabled();
if (isSoundPlaying) {
AudioManager.playExplosionSound();
}
} }
void _animateBombExplosion(Vector2 position) { void _animateBombExplosion(Vector2 position) {
@ -433,36 +663,11 @@ class Board extends FlameGame {
); );
} }
int _calculateScore(int matchLength) { void _createBomb(int row, int col) async {
if (matchLength == 3) { isSoundPlaying = await MainDB.instance.getSoundEnabled();
return 50; if (isSoundPlaying) {
} else if (matchLength == 4) { AudioManager.playFourElementsSound();
return 100;
} else if (matchLength == 5) {
return 200;
} }
return 0;
}
// void _animateRemoveTile(Tile tile) {
// tile.add(RemoveEffect(
// delay: 0.5,
// onComplete: () => remove(tile),
// ));
// }
void _animateRemoveTile(Tile tile) {
tile.add(ScaleEffect.to(
Vector2.zero(),
EffectController(
duration: 0.2,
curve: Curves.easeInBack,
),
onComplete: () => tile.removeFromParent(),
));
}
void _createBomb(int row, int col) {
final tile = tiles[row][col]; final tile = tiles[row][col];
if (tile != null) { if (tile != null) {
tile.isBomb = true; tile.isBomb = true;
@ -493,12 +698,6 @@ class Board extends FlameGame {
} }
} }
void _createMagicCube(int row, int col) {
var tile = tiles[row][col];
tile?.sprite = magicCubeSprite;
tile?.isMagicCube = true;
}
void explodeBomb(Tile bombTile) { void explodeBomb(Tile bombTile) {
final bombPosition = bombTile.position.clone(); final bombPosition = bombTile.position.clone();
final bombRow = bombTile.row; final bombRow = bombTile.row;
@ -521,259 +720,39 @@ class Board extends FlameGame {
tiles[bombRow][bombCol] = null; tiles[bombRow][bombCol] = null;
} }
void _applyGravity() { // Magic cube
for (int col = 0; col < cols; col++) {
for (int row = rows - 1; row >= 0; row--) { void _createMagicCube(int row, int col) async {
if (tiles[row][col] == null) { isSoundPlaying = await MainDB.instance.getSoundEnabled();
for (int k = row - 1; k >= 0; k--) { if (isSoundPlaying) {
if (tiles[k][col] != null) { AudioManager.playFourElementsSound();
tiles[row][col] = tiles[k][col]!;
tiles[k][col] = null;
tiles[row][col]!.row = row;
tiles[row][col]!.animateMoveTo(
Vector2(col * tileSize, row * tileSize), () {});
lastMovedByGravity = tiles[row][col];
break;
}
}
}
}
} }
var tile = tiles[row][col];
tile?.sprite = Tile.magicCubeSprite;
tile?.isMagicCube = true;
} }
void _fillEmptySpaces() { // Game over
for (int col = 0; col < cols; col++) {
for (int row = rows - 1; row >= 0; row--) { void _onGameOver() {
if (tiles[row][col] == null) { print("Game Over! Score: ${score}");
int spriteIndex = _randomElement(); showGameOverScreen();
var tile = Tile(
sprite: sprites[spriteIndex],
spriteIndex: spriteIndex,
size: Vector2.all(tileSize),
position: Vector2(col * tileSize, -tileSize),
row: row,
col: col,
// onTileTap: handleTileTap,
onSwipe: handleTileSwipe,
);
tiles[row][col] = tile;
add(tile);
tile.animateMoveTo(Vector2(col * tileSize, row * tileSize), () {});
}
}
}
} }
Tile? findHint() { void showGameOverScreen() {
for (int row = 0; row < rows; row++) { isGameOver = true;
for (int col = 0; col < cols; col++) { remove(_playerScore);
Tile? tile = tiles[row][col]; if (gameModeManager.currentMode) {
remove(_playerMoves);
if (col < cols - 1 && _canSwap(row, col, row, col + 1)) { } else {
return tile; remove(_remainingTime);
}
if (row < rows - 1 && _canSwap(row, col, row + 1, col)) {
return tile;
}
}
} }
return null;
}
bool _canSwap(int row1, int col1, int row2, int col2) { this.overlays.remove(PauseButton.id);
Tile tempTile1 = tiles[row1][col1]!; this.overlays.remove(RestartButton.id);
Tile tempTile2 = tiles[row2][col2]!; this.overlays.remove(HintButton.id);
MainDB.instance.addHighScore(score, () {
tiles[row1][col1] = tempTile2; this.overlays.add(GameOverMenu.id);
tiles[row2][col2] = tempTile1; });
bool matchFound = checkMatches(simulate: true);
tiles[row1][col1] = tempTile1;
tiles[row2][col2] = tempTile2;
return matchFound;
}
void showHint() {
Tile? hintTile = findHint();
if (hintTile != null) {
hintTile.select();
}
} }
} }
// void handleTileTap(Tile tappedTile) {
// if (animating) return;
// int row = tappedTile.row;
// int col = tappedTile.col;
// if (selectedRow == null || selectedCol == null) {
// tappedTile.select();
// selectedRow = row;
// selectedCol = col;
// } else {
// tiles[selectedRow!][selectedCol!]?.deselect();
// if (_isAdjacent(selectedRow!, selectedCol!, row, col)) {
// lastMovedTile = tiles[selectedRow!][selectedCol!];
// swapTiles(tiles[selectedRow!][selectedCol!]!, tiles[row][col]!, true);
// Future.delayed(const Duration(milliseconds: 300), () {
// if (!checkMatches()) {
// swapTiles(
// tiles[row][col]!, tiles[selectedRow!][selectedCol!]!, true);
// }
// selectedRow = null;
// selectedCol = null;
// });
// } else {
// tiles[selectedRow!][selectedCol!]?.deselect();
// tappedTile.select();
// selectedRow = row;
// selectedCol = col;
// }
// }
// }
// int _removeMatchedElements(int row, int col) {
// int score = 0;
// final int? value = tiles[row][col]?.spriteIndex;
// bool bombTriggered = false;
// Tile? tileToTransformIntoBomb = null;
// int left = col;
// while (left > 0 && tiles[row][left - 1]?.spriteIndex == value) left--;
// int right = col;
// while (right < cols - 1 && tiles[row][right + 1]?.spriteIndex == value)
// right++;
// if (right - left + 1 >= 3) {
// score += _calculateScore(right - left + 1);
// if (right - left + 1 >= 4 &&
// lastMovedTile != null &&
// lastMovedTile!.row == row &&
// lastMovedTile!.col >= left &&
// lastMovedTile!.col <= right) {
// tileToTransformIntoBomb = lastMovedTile;
// }
// for (int i = left; i <= right; i++) {
// if (tiles[row][i] != null) {
// if (tiles[row][i]!.isBomb) {
// bombTriggered = true;
// _triggerBomb(row, i);
// }
// if (tiles[row][i] != tileToTransformIntoBomb) {
// _animateRemoveTile(tiles[row][i]!);
// tiles[row][i] = null;
// }
// }
// }
// }
// int top = row;
// while (top > 0 && tiles[top - 1][col]?.spriteIndex == value) top--;
// int bottom = row;
// while (bottom < rows - 1 && tiles[bottom + 1][col]?.spriteIndex == value)
// bottom++;
// if (bottom - top + 1 >= 3) {
// score += _calculateScore(bottom - top + 1);
// if (bottom - top + 1 >= 4 &&
// lastMovedTile != null &&
// lastMovedTile!.col == col &&
// lastMovedTile!.row >= top &&
// lastMovedTile!.row <= bottom) {
// tileToTransformIntoBomb = lastMovedTile;
// }
// for (int i = top; i <= bottom; i++) {
// if (tiles[i][col] != null) {
// if (tiles[i][col]!.isBomb) {
// bombTriggered = true;
// _triggerBomb(i, col);
// }
// if (tiles[i][col] != tileToTransformIntoBomb) {
// _animateRemoveTile(tiles[i][col]!);
// tiles[i][col] = null;
// }
// }
// }
// }
// if (bombTriggered) {
// _triggerBomb(row, col);
// }
// if (tileToTransformIntoBomb != null) {
// _createBomb(tileToTransformIntoBomb.row, tileToTransformIntoBomb.col);
// }
// return score;
// }
// void explodeBomb(Tile bombTile) {
// final bombPosition = bombTile.position;
// final bombRow = bombTile.row;
// final bombCol = bombTile.col;
// for (int rowOffset = -1; rowOffset <= 1; rowOffset++) {
// for (int colOffset = -1; colOffset <= 1; colOffset++) {
// final row = bombRow + rowOffset;
// final col = bombCol + colOffset;
// if (row >= 0 && row < rows && col >= 0 && col < cols) {
// final tile = tiles[row][col];
// if (tile != null && tile != bombTile) {
// _animateRemoveTile(tile);
// tiles[row][col] = null;
// }
// }
// }
// }
// bombTile.add(RemoveEffect(
// delay: 0.5,
// onComplete: () => remove(bombTile),
// ));
// final explosion = CircleComponent(
// radius: tileSize / 2,
// paint: Paint()..color = Colors.orange.withOpacity(0.7),
// position: bombPosition,
// );
// add(explosion);
// explosion.add(ScaleEffect.to(
// Vector2.all(2),
// EffectController(duration: 0.5),
// onComplete: () => explosion.removeFromParent(),
// ));
// }
// void _applyGravity() {
// for (int col = 0; col < cols; col++) {
// for (int row = rows - 1; row >= 0; row--) {
// if (tiles[row][col] == null) {
// for (int k = row - 1; k >= 0; k--) {
// if (tiles[k][col] != null) {
// tiles[row][col] = tiles[k][col]!;
// tiles[k][col] = null;
// tiles[row][col]!.row = row;
// tiles[row][col]!.animateMoveTo(
// Vector2(col * tileSize, row * tileSize), () {});
// break;
// }
// }
// }
// }
// }
// }

View File

@ -0,0 +1,89 @@
import 'dart:async';
import 'dart:ui';
import 'package:match_magic/game/board.dart';
enum GameMode {
timeAttack,
levelProgression,
}
class GameModeManager {
final bool currentMode;
final VoidCallback onGameOver;
int remainingTime = 20;
int targetScore = 5000;
int currentLevel = 1;
static int movesLeft = 20;
Timer? _timer;
GameModeManager({
required this.currentMode,
required this.onGameOver,
});
void initializeMode() {
if (currentMode) {
_startLevelProgressionMode();
} else if (!currentMode) {
_startTimeAttackMode();
}
}
// Reset modes
void resetMode() {
_timer?.cancel();
remainingTime = 20;
currentLevel = 1;
movesLeft = 20;
}
// Time mode
void _startTimeAttackMode() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (remainingTime > 0) {
remainingTime--;
} else {
onGameOver();
_timer?.cancel();
}
});
}
// Levels mode
void _startLevelProgressionMode() {
movesLeft = 20; // Number of moves per level
}
// Time update
void updateTime(double dt) {}
// Game Completion Logic
bool isGameOverCondition() {
if (!currentMode) {
return remainingTime <= 0;
} else if (currentMode) {
return movesLeft <= 0;
}
return false;
}
// Moving to the next level
void nextLevel() {
if (Board.score >= targetScore) {
currentLevel++;
targetScore += 100;
movesLeft = 20;
}
}
// Updating the number of remaining moves
void updateMoves(int movesUsed) {
movesLeft -= movesUsed;
if (movesLeft <= 0 && Board.score < targetScore) {
onGameOver();
}
}
}

View File

@ -1,245 +0,0 @@
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:match_magic/game/sprite_loader.dart';
import 'package:match_magic/utilities/audio_manager.dart';
import 'package:provider/provider.dart';
import 'board.dart';
import 'swap_notifier.dart';
class MatchMagicGameScreen extends StatefulWidget {
const MatchMagicGameScreen({Key? key}) : super(key: key);
@override
_MatchMagicGameScreenState createState() => _MatchMagicGameScreenState();
}
class _MatchMagicGameScreenState extends State<MatchMagicGameScreen> {
late Future<List<Sprite>> _crystalsFuture;
late Future<Sprite> _magicCubeFuture;
Board? board;
@override
void initState() {
super.initState();
_crystalsFuture = SpriteLoader.loadCrystalSprites();
_magicCubeFuture = SpriteLoader.loadMagicCubeSprite();
AudioManager.load();
}
void _restartGame() {
context.read<SwapNotifier>().resetScore();
setState(() {
board = null;
_crystalsFuture = SpriteLoader.loadCrystalSprites();
_magicCubeFuture = SpriteLoader.loadMagicCubeSprite();
});
}
void _showSettingsDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Settings'),
content: const Text('The settings have not yet been implemented.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
_showExitConfirmationDialog();
},
child: const Text('Exit to Main Menu'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Close'),
),
],
);
},
);
}
void _showExitConfirmationDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Warning'),
content: const Text(
'You will lose all game progress. Are you sure you want to exit?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).popUntil((route) => route.isFirst);
context.read<SwapNotifier>().resetScore();
},
child: const Text('Yes'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: FutureBuilder<List<Sprite>>(
future: _crystalsFuture,
builder: (context, crystalSnapshot) {
if (crystalSnapshot.connectionState == ConnectionState.done) {
if (crystalSnapshot.hasError) {
return Center(child: Text('Error: ${crystalSnapshot.error}'));
} else if (!crystalSnapshot.hasData ||
crystalSnapshot.data == null) {
return const Center(child: Text('No crystal sprites found'));
} else {
final crystals = crystalSnapshot.data!;
return FutureBuilder<Sprite>(
future: _magicCubeFuture,
builder: (context, cubeSnapshot) {
if (cubeSnapshot.connectionState == ConnectionState.done) {
if (cubeSnapshot.hasError) {
return Center(
child: Text('Error: ${cubeSnapshot.error}'));
} else if (!cubeSnapshot.hasData ||
cubeSnapshot.data == null) {
return const Center(
child: Text('No magic cube sprite found'));
} else {
final magicCube = cubeSnapshot.data!;
board ??= Board(
sprites: crystals,
magicCubeSprite: magicCube,
swapNotifier: context.read<SwapNotifier>(),
);
return Stack(
children: [
Column(
children: [
const SizedBox(height: 50),
const ScoreDisplay(),
Expanded(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: GameWidget(game: board!),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _restartGame,
child: const Text(
'Restart',
style: TextStyle(color: Colors.black),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
board?.showHint();
},
child: const Text(
'Hint',
style: TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 50),
],
),
Positioned(
top: 16,
left: 16,
child: IconButton(
icon: const Icon(Icons.settings,
color: Colors.white),
onPressed: _showSettingsDialog,
),
),
],
);
}
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class ScoreDisplay extends StatelessWidget {
const ScoreDisplay({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<SwapNotifier>(
builder: (context, notifier, child) {
return Card(
color: Colors.blueGrey[900],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Score: ${notifier.score}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
);
},
),
Consumer<SwapNotifier>(
builder: (context, notifier, child) {
return Card(
color: Colors.blueGrey[900],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Moves: ${notifier.moveCount}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
);
},
),
],
),
);
}
}

View File

@ -1,15 +1,13 @@
import 'package:flame/components.dart'; // import 'package:flame/components.dart';
import 'package:flame/flame.dart';
class SpriteLoader { Future<void> loadImages() async {
static Future<List<Sprite>> loadCrystalSprites() async { await Flame.images.load('crystal1.png');
List<Sprite> sprites = []; await Flame.images.load('crystal2.png');
for (int i = 1; i <= 7; i++) { await Flame.images.load('crystal3.png');
sprites.add(await Sprite.load('crystal$i.png')); await Flame.images.load('crystal4.png');
} await Flame.images.load('crystal5.png');
return sprites; await Flame.images.load('crystal6.png');
} await Flame.images.load('crystal7.png');
await Flame.images.load('magic_cube.png');
static Future<Sprite> loadMagicCubeSprite() async {
return Sprite.load('magic_cube.png');
}
} }

View File

@ -1,54 +0,0 @@
import 'package:flutter/material.dart';
import 'tile.dart';
class SwapNotifier extends ChangeNotifier {
Tile? selectedTile;
int _score = 0;
int _moveCount = 0;
int get score => _score;
int get moveCount => _moveCount;
void resetScore() {
_score = 0;
_moveCount = 0;
selectedTile = null;
notifyListeners();
}
void incrementScore(int value) {
_score += value;
notifyListeners();
}
void incrementMoveCount() {
_moveCount += 1;
notifyListeners();
}
void selectTile(Tile tile) {
if (selectedTile == null) {
selectedTile = tile;
tile.select();
} else {
if (_isNeighbor(selectedTile!, tile) || selectedTile!.isMagicCube) {
notifyListeners();
} else {
selectedTile?.deselect();
selectedTile = tile;
tile.select();
notifyListeners();
}
}
}
bool _isNeighbor(Tile tile1, Tile tile2) {
return (tile1.row == tile2.row && (tile1.col - tile2.col).abs() == 1) ||
(tile1.col == tile2.col && (tile1.row - tile2.row).abs() == 1);
}
void clearSelectedTile() {
selectedTile = null;
notifyListeners();
}
}

View File

@ -1,6 +1,9 @@
import 'dart:math';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame/effects.dart'; import 'package:flame/effects.dart';
import 'package:flame/events.dart'; import 'package:flame/events.dart';
import 'package:flame/flame.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'board.dart'; import 'board.dart';
@ -26,8 +29,17 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
required this.col, required this.col,
// required this.onTileTap, // required this.onTileTap,
required this.onSwipe, required this.onSwipe,
}) : super(sprite: sprite, size: size, position: position); }) : super(
sprite: sprite,
size: size,
position: position,
) {
// final randomSpriteIndex = _random.nextInt(crystals.length);
// tile = isMagicCube ? crystals[randomSpriteIndex] : magicCube();
}
// static final Random _random = Random();
late Sprite tile;
// @override // @override
// bool onTapDown(TapDownEvent event) { // bool onTapDown(TapDownEvent event) {
// if (isAnimating || (parent is Board && (parent as Board).animating)) { // if (isAnimating || (parent is Board && (parent as Board).animating)) {
@ -37,6 +49,18 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
// return true; // return true;
// } // }
static List<Sprite> crystals = [
crystal1(),
crystal2(),
crystal3(),
crystal4(),
crystal5(),
crystal6(),
crystal7(),
];
static Sprite magicCubeSprite = magicCube();
@override @override
bool onDragStart(DragStartEvent event) { bool onDragStart(DragStartEvent event) {
if (isAnimating || (parent as Board).animating) { if (isAnimating || (parent as Board).animating) {
@ -123,3 +147,35 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks {
// )); // ));
// } // }
} }
Sprite crystal1() {
return Sprite(Flame.images.fromCache('crystal1.png'));
}
Sprite crystal2() {
return Sprite(Flame.images.fromCache('crystal2.png'));
}
Sprite crystal3() {
return Sprite(Flame.images.fromCache('crystal3.png'));
}
Sprite crystal4() {
return Sprite(Flame.images.fromCache('crystal4.png'));
}
Sprite crystal5() {
return Sprite(Flame.images.fromCache('crystal5.png'));
}
Sprite crystal6() {
return Sprite(Flame.images.fromCache('crystal6.png'));
}
Sprite crystal7() {
return Sprite(Flame.images.fromCache('crystal7.png'));
}
Sprite magicCube() {
return Sprite(Flame.images.fromCache('magic_cube.png'));
}

View File

@ -1,20 +1,25 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:match_magic/screens/main_menu.dart'; import 'package:match_magic/db/main_db.dart';
import 'package:provider/provider.dart'; import 'package:match_magic/screens/main_menu_screen.dart';
import 'game/match_magic_game.dart';
import 'game/swap_notifier.dart';
void main() { void main() async {
runApp( WidgetsFlutterBinding.ensureInitialized();
MultiProvider( await MainDB.instance.initMainDB();
providers: [ SystemChrome.setPreferredOrientations([
ChangeNotifierProvider(create: (_) => SwapNotifier()), DeviceOrientation.portraitUp,
], DeviceOrientation.portraitDown,
child: const MyApp(), ]).then(
(value) => runApp(
// MultiProvider(
// providers: [
// ChangeNotifierProvider(create: (_) => SwapNotifier()),
// ],
// child: const
MyApp(),
), ),
// )
); );
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -22,8 +27,8 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const MaterialApp( return MaterialApp(
home: MainMenu(), home: MainMenuScreen(),
); );
} }
} }

View File

@ -0,0 +1,16 @@
import 'package:shared_preferences/shared_preferences.dart';
class AppStateManager {
static const String _modeKey = 'mode';
// Installing the bubbles or balloons mod
static Future<bool> getMode() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool(_modeKey) ?? true;
}
static Future<void> setMode(bool mode) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool(_modeKey, mode);
}
}

View File

@ -0,0 +1,13 @@
import 'package:isar/isar.dart';
part 'high_score.g.dart';
@Collection()
class HighScore {
Id id = Isar.autoIncrement;
@enumerated
late int highScoreValue;
HighScore({required this.highScoreValue});
}

View File

@ -0,0 +1,355 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'high_score.dart';
// **************************************************************************
// IsarCollectionGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
extension GetHighScoreCollection on Isar {
IsarCollection<HighScore> get highScores => this.collection();
}
const HighScoreSchema = CollectionSchema(
name: r'HighScore',
id: 6847972554114346288,
properties: {
r'highScoreValue': PropertySchema(
id: 0,
name: r'highScoreValue',
type: IsarType.long,
)
},
estimateSize: _highScoreEstimateSize,
serialize: _highScoreSerialize,
deserialize: _highScoreDeserialize,
deserializeProp: _highScoreDeserializeProp,
idName: r'id',
indexes: {},
links: {},
embeddedSchemas: {},
getId: _highScoreGetId,
getLinks: _highScoreGetLinks,
attach: _highScoreAttach,
version: '3.1.0+1',
);
int _highScoreEstimateSize(
HighScore object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
return bytesCount;
}
void _highScoreSerialize(
HighScore object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeLong(offsets[0], object.highScoreValue);
}
HighScore _highScoreDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = HighScore(
highScoreValue: reader.readLong(offsets[0]),
);
object.id = id;
return object;
}
P _highScoreDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readLong(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Id _highScoreGetId(HighScore object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _highScoreGetLinks(HighScore object) {
return [];
}
void _highScoreAttach(IsarCollection<dynamic> col, Id id, HighScore object) {
object.id = id;
}
extension HighScoreQueryWhereSort
on QueryBuilder<HighScore, HighScore, QWhere> {
QueryBuilder<HighScore, HighScore, QAfterWhere> anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension HighScoreQueryWhere
on QueryBuilder<HighScore, HighScore, QWhereClause> {
QueryBuilder<HighScore, HighScore, QAfterWhereClause> idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
upper: id,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterWhereClause> idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<HighScore, HighScore, QAfterWhereClause> idGreaterThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<HighScore, HighScore, QAfterWhereClause> idLessThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<HighScore, HighScore, QAfterWhereClause> idBetween(
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
));
});
}
}
extension HighScoreQueryFilter
on QueryBuilder<HighScore, HighScore, QFilterCondition> {
QueryBuilder<HighScore, HighScore, QAfterFilterCondition>
highScoreValueEqualTo(int value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'highScoreValue',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition>
highScoreValueGreaterThan(
int value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'highScoreValue',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition>
highScoreValueLessThan(
int value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'highScoreValue',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition>
highScoreValueBetween(
int lower,
int upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'highScoreValue',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition> idEqualTo(
Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition> idGreaterThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition> idLessThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<HighScore, HighScore, QAfterFilterCondition> idBetween(
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
}
extension HighScoreQueryObject
on QueryBuilder<HighScore, HighScore, QFilterCondition> {}
extension HighScoreQueryLinks
on QueryBuilder<HighScore, HighScore, QFilterCondition> {}
extension HighScoreQuerySortBy on QueryBuilder<HighScore, HighScore, QSortBy> {
QueryBuilder<HighScore, HighScore, QAfterSortBy> sortByHighScoreValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'highScoreValue', Sort.asc);
});
}
QueryBuilder<HighScore, HighScore, QAfterSortBy> sortByHighScoreValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'highScoreValue', Sort.desc);
});
}
}
extension HighScoreQuerySortThenBy
on QueryBuilder<HighScore, HighScore, QSortThenBy> {
QueryBuilder<HighScore, HighScore, QAfterSortBy> thenByHighScoreValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'highScoreValue', Sort.asc);
});
}
QueryBuilder<HighScore, HighScore, QAfterSortBy> thenByHighScoreValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'highScoreValue', Sort.desc);
});
}
QueryBuilder<HighScore, HighScore, QAfterSortBy> thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<HighScore, HighScore, QAfterSortBy> thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
}
extension HighScoreQueryWhereDistinct
on QueryBuilder<HighScore, HighScore, QDistinct> {
QueryBuilder<HighScore, HighScore, QDistinct> distinctByHighScoreValue() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'highScoreValue');
});
}
}
extension HighScoreQueryProperty
on QueryBuilder<HighScore, HighScore, QQueryProperty> {
QueryBuilder<HighScore, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<HighScore, int, QQueryOperations> highScoreValueProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'highScoreValue');
});
}
}

View File

@ -0,0 +1,14 @@
import 'package:isar/isar.dart';
part 'settings.g.dart';
@Collection()
class Settings {
Settings({
required this.isSoundEnabled,
required this.isMusicEnabled,
});
Id id = 1;
late bool isSoundEnabled;
late bool isMusicEnabled;
}

View File

@ -0,0 +1,361 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'settings.dart';
// **************************************************************************
// IsarCollectionGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
extension GetSettingsCollection on Isar {
IsarCollection<Settings> get settings => this.collection();
}
const SettingsSchema = CollectionSchema(
name: r'Settings',
id: -8656046621518759136,
properties: {
r'isMusicEnabled': PropertySchema(
id: 0,
name: r'isMusicEnabled',
type: IsarType.bool,
),
r'isSoundEnabled': PropertySchema(
id: 1,
name: r'isSoundEnabled',
type: IsarType.bool,
)
},
estimateSize: _settingsEstimateSize,
serialize: _settingsSerialize,
deserialize: _settingsDeserialize,
deserializeProp: _settingsDeserializeProp,
idName: r'id',
indexes: {},
links: {},
embeddedSchemas: {},
getId: _settingsGetId,
getLinks: _settingsGetLinks,
attach: _settingsAttach,
version: '3.1.0+1',
);
int _settingsEstimateSize(
Settings object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
return bytesCount;
}
void _settingsSerialize(
Settings object,
IsarWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeBool(offsets[0], object.isMusicEnabled);
writer.writeBool(offsets[1], object.isSoundEnabled);
}
Settings _settingsDeserialize(
Id id,
IsarReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = Settings(
isMusicEnabled: reader.readBool(offsets[0]),
isSoundEnabled: reader.readBool(offsets[1]),
);
object.id = id;
return object;
}
P _settingsDeserializeProp<P>(
IsarReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readBool(offset)) as P;
case 1:
return (reader.readBool(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Id _settingsGetId(Settings object) {
return object.id;
}
List<IsarLinkBase<dynamic>> _settingsGetLinks(Settings object) {
return [];
}
void _settingsAttach(IsarCollection<dynamic> col, Id id, Settings object) {
object.id = id;
}
extension SettingsQueryWhereSort on QueryBuilder<Settings, Settings, QWhere> {
QueryBuilder<Settings, Settings, QAfterWhere> anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension SettingsQueryWhere on QueryBuilder<Settings, Settings, QWhereClause> {
QueryBuilder<Settings, Settings, QAfterWhereClause> idEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
upper: id,
));
});
}
QueryBuilder<Settings, Settings, QAfterWhereClause> idNotEqualTo(Id id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<Settings, Settings, QAfterWhereClause> idGreaterThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<Settings, Settings, QAfterWhereClause> idLessThan(Id id,
{bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<Settings, Settings, QAfterWhereClause> idBetween(
Id lowerId,
Id upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
));
});
}
}
extension SettingsQueryFilter
on QueryBuilder<Settings, Settings, QFilterCondition> {
QueryBuilder<Settings, Settings, QAfterFilterCondition> idEqualTo(Id value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> idGreaterThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> idLessThan(
Id value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> idBetween(
Id lower,
Id upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> isMusicEnabledEqualTo(
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isMusicEnabled',
value: value,
));
});
}
QueryBuilder<Settings, Settings, QAfterFilterCondition> isSoundEnabledEqualTo(
bool value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'isSoundEnabled',
value: value,
));
});
}
}
extension SettingsQueryObject
on QueryBuilder<Settings, Settings, QFilterCondition> {}
extension SettingsQueryLinks
on QueryBuilder<Settings, Settings, QFilterCondition> {}
extension SettingsQuerySortBy on QueryBuilder<Settings, Settings, QSortBy> {
QueryBuilder<Settings, Settings, QAfterSortBy> sortByIsMusicEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isMusicEnabled', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByIsMusicEnabledDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isMusicEnabled', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByIsSoundEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isSoundEnabled', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> sortByIsSoundEnabledDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isSoundEnabled', Sort.desc);
});
}
}
extension SettingsQuerySortThenBy
on QueryBuilder<Settings, Settings, QSortThenBy> {
QueryBuilder<Settings, Settings, QAfterSortBy> thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByIsMusicEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isMusicEnabled', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByIsMusicEnabledDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isMusicEnabled', Sort.desc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByIsSoundEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isSoundEnabled', Sort.asc);
});
}
QueryBuilder<Settings, Settings, QAfterSortBy> thenByIsSoundEnabledDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'isSoundEnabled', Sort.desc);
});
}
}
extension SettingsQueryWhereDistinct
on QueryBuilder<Settings, Settings, QDistinct> {
QueryBuilder<Settings, Settings, QDistinct> distinctByIsMusicEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isMusicEnabled');
});
}
QueryBuilder<Settings, Settings, QDistinct> distinctByIsSoundEnabled() {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'isSoundEnabled');
});
}
}
extension SettingsQueryProperty
on QueryBuilder<Settings, Settings, QQueryProperty> {
QueryBuilder<Settings, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<Settings, bool, QQueryOperations> isMusicEnabledProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isMusicEnabled');
});
}
QueryBuilder<Settings, bool, QQueryOperations> isSoundEnabledProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'isSoundEnabled');
});
}
}

View File

@ -0,0 +1,153 @@
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:match_magic/db/main_db.dart';
import 'package:match_magic/game/board.dart';
import 'package:match_magic/screens/main_menu_screen.dart';
import 'package:match_magic/widgets/gradient_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/hint_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/pause_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/restart_button.dart';
class GameOverMenu extends StatelessWidget {
static const String id = 'GameOverMenu';
final Board gameRef;
GameOverMenu({
super.key,
required this.gameRef,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFEBEBEB).withOpacity(0.6),
Color(0xFFBFBEC0).withOpacity(0.6),
],
),
),
),
Container(
padding: const EdgeInsets.symmetric(vertical: 70, horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Game over title.
const Padding(
padding: EdgeInsets.symmetric(vertical: 50.0),
child: Text(
'Game Over',
style: TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.w800,
color: Colors.black,
shadows: [
Shadow(
blurRadius: 20.0,
color: Colors.white,
offset: Offset(0, 0),
)
],
),
),
),
Stack(
children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 64),
child: Image.asset('assets/images/game_over_score.png')),
Positioned.fill(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 20),
Text(
'Score: ${Board.score}',
style: TextStyle(
fontSize: 24,
color: Colors.white,
),
),
SizedBox(height: 8),
FutureBuilder<int?>(
future: MainDB.instance.getHighScoreValue(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return CircularProgressIndicator(); //
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
return Text(
'High score: ${snapshot.data}',
style: TextStyle(
fontSize: 16, color: Colors.white),
);
} else {
return Text('No data');
}
},
),
],
),
),
],
),
Expanded(child: SizedBox()),
Row(
children: [
Expanded(
child: GradientButton(
onPressed: () async {
// AdManager.showInterstitialAd(context, () {});
// Pause
gameRef.overlays.remove(GameOverMenu.id);
gameRef.overlays.add(PauseButton.id);
gameRef.overlays.add(RestartButton.id);
gameRef.overlays.add(HintButton.id);
gameRef.newGame();
gameRef.resumeEngine();
},
buttonText: 'Play again'),
),
],
),
SizedBox(
height: 12,
),
// Exit button.
Row(
children: [
Expanded(
child: GradientButton(
onPressed: () {
FlameAudio.bgm.stop();
gameRef.overlays.remove(GameOverMenu.id);
gameRef.removed;
gameRef.resumeEngine();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => MainMenuScreen(),
),
);
},
buttonText: 'Exit'),
)
],
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,88 @@
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'package:match_magic/game/board.dart';
import 'package:match_magic/screens/game_over_screen.dart';
import 'package:match_magic/screens/pause_screen.dart';
import 'package:match_magic/utilities/audio_manager.dart';
import 'package:match_magic/widgets/overlays/game_overlay/hint_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/pause_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/restart_button.dart';
class GameScreen extends StatefulWidget {
GameScreen(this.gameMode);
final bool gameMode;
@override
_GameScreenState createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> {
Board? board;
@override
void initState() {
super.initState();
AudioManager.load();
// AdManager.loadBannerAd();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
GameWidget(
initialActiveOverlays: [
PauseButton.id,
HintButton.id,
RestartButton.id
],
overlayBuilderMap: {
PauseButton.id: (BuildContext context, Board gameRef) =>
PauseButton(
gameRef: gameRef,
),
HintButton.id: (BuildContext context, Board gameRef) =>
HintButton(
gameRef: gameRef,
),
RestartButton.id: (BuildContext context, Board gameRef) =>
RestartButton(
gameRef: gameRef,
),
PauseMenu.id: (BuildContext context, Board gameRef) =>
PauseMenu(
gameRef: gameRef,
),
GameOverMenu.id: (BuildContext context, Board gameRef) =>
GameOverMenu(
gameRef: gameRef,
)
},
game: Board(
context,
gameMode: widget.gameMode,
)),
// Display Ads
// if (AdManager.bannerAd != null)
// Align(
// alignment: Alignment.bottomCenter,
// child: Container(
// width: AdManager.bannerAd!.size.width.toDouble(),
// height: AdManager.bannerAd!.size.height.toDouble(),
// child: AdWidget(ad: AdManager.bannerAd!),
// ),
// ),
],
),
);
}
}

View File

@ -1,70 +0,0 @@
import 'package:flutter/material.dart';
import 'package:match_magic/game/match_magic_game.dart';
class MainMenu extends StatelessWidget {
const MainMenu({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.settings, color: Colors.white),
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Settings'),
content:
const Text('The settings have not yet been implemented.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Ok'),
),
],
),
);
},
),
backgroundColor: Colors.black,
),
backgroundColor: Colors.black,
body: Center(
child: Column(
children: [
const SizedBox(
height: 100,
),
const Text(
'Match Magic',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white),
),
const SizedBox(
height: 300,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const MatchMagicGameScreen()),
);
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 60.0, vertical: 16.0),
),
child: const Text('Play', style: TextStyle(fontSize: 16)),
),
],
),
),
);
}
}

View File

@ -0,0 +1,301 @@
// import 'package:flutter/material.dart';
// import 'package:match_magic/game/match_magic_game.dart';
// class MainMenu extends StatelessWidget {
// const MainMenu({Key? key}) : super(key: key);
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(
// leading: IconButton(
// icon: const Icon(Icons.settings, color: Colors.white),
// onPressed: () {
// showDialog(
// context: context,
// builder: (context) => AlertDialog(
// title: const Text('Settings'),
// content:
// const Text('The settings have not yet been implemented.'),
// actions: [
// TextButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// child: const Text('Ok'),
// ),
// ],
// ),
// );
// },
// ),
// backgroundColor: Colors.black,
// ),
// backgroundColor: Colors.black,
// body: Center(
// child: Column(
// children: [
// const SizedBox(
// height: 100,
// ),
// const Text(
// 'Match Magic',
// style: TextStyle(
// fontSize: 28,
// fontWeight: FontWeight.bold,
// color: Colors.white),
// ),
// const SizedBox(
// height: 300,
// ),
// ElevatedButton(
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const MatchMagicGameScreen()),
// );
// },
// style: ElevatedButton.styleFrom(
// padding: const EdgeInsets.symmetric(
// horizontal: 60.0, vertical: 16.0),
// ),
// child: const Text('Play', style: TextStyle(fontSize: 16)),
// ),
// ],
// ),
// ),
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:match_magic/models/app_state_manager.dart';
import 'package:match_magic/screens/game_screen.dart';
import 'package:match_magic/screens/options_screen.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/utilities/assets.dart';
class MainMenuScreen extends StatefulWidget {
@override
State<MainMenuScreen> createState() => _MainMenuScreenState();
}
class _MainMenuScreenState extends State<MainMenuScreen> {
bool islevelProgression = true;
@override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
// _loadBackground();
// _checkPremiumStatus();
_loadMode();
}
Future<void> _loadMode() async {
final mode = await AppStateManager.getMode();
setState(() {
islevelProgression = mode;
});
}
// Future<void> _loadBackground() async {
// final background = await MainDB.instance.getBackground();
// setState(() {
// backgroundImage = background;
// });
// }
// void _updateBackground(Background newBackground) {
// setState(() {
// backgroundImage = newBackground;
// });
// }
// // Premium check
// Future<void> _checkPremiumStatus() async {
// bool isPremium = await AppStateManager.isPremium();
// setState(() {
// _isPremium = isPremium;
// });
// }
Future<void> _refreshMainScreen() async {
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Container(
// decoration: BoxDecoration(
// image: DecorationImage(
// image: BackgroundImageProvider.getBackgroundImage(
// backgroundImage ?? Background.sky),
// fit: BoxFit.cover,
// ),
// ),
// ),
Container(
padding:
const EdgeInsets.only(left: 16, top: 32, right: 16, bottom: 70),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
TextButton(
onPressed: () async {
bool? refreshScreen = await showDialog(
context: context,
builder: (BuildContext context) => OptionsScreen(
// onUpdateBackground: (background) {
// setState(() {
// backgroundImage = background;
// });
// },
),
);
if (refreshScreen != null && refreshScreen == true) {
await _refreshMainScreen();
}
},
child: Text(
'Options',
style: AppStyles.subtitleTextStyle
.copyWith(color: AppStyles.accentColorTopBar),
)),
],
),
Expanded(
child: Container(),
),
Container(
height: 150,
child: Text(
'Match Magic',
style: TextStyle(
color: AppStyles.accentColorTopBar, fontSize: 30),
),
),
const SizedBox(height: 50),
Container(
height: 200,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Levels mode
Expanded(
child: GestureDetector(
onTap: () async {
setState(() {
islevelProgression = true;
});
await AppStateManager.setMode(true);
},
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius:
const BorderRadius.all(Radius.circular(20)),
border: islevelProgression
? Border.all(
color: AppStyles.accentColor,
width: 2,
)
: null,
),
child: const Align(
alignment: Alignment.bottomCenter,
child: Text(
'Levels',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
const SizedBox(
width: 16,
),
// Limited time mode
Expanded(
child: GestureDetector(
onTap: () async {
setState(() {
islevelProgression = false;
});
await AppStateManager.setMode(false);
},
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: const BorderRadius.all(
Radius.circular(20)),
border: islevelProgression
? null
: Border.all(
color: AppStyles.accentColor,
width: 2,
),
),
child: const Align(
alignment: Alignment.bottomCenter,
child: Text(
'Limited time',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
),
],
),
),
const SizedBox(height: 50),
// Play game
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) =>
GameScreen(islevelProgression)),
(route) => false);
},
child: Text('Play game'),
),
),
],
),
const SizedBox(height: 12),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,266 @@
import 'package:flutter/material.dart';
import 'package:match_magic/db/main_db.dart';
import 'package:match_magic/game/board.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/widgets/icon_widgets/arrow_left_icon.dart';
import 'package:match_magic/widgets/icon_widgets/music_icon.dart';
import 'package:match_magic/widgets/icon_widgets/star_icon.dart';
import 'package:match_magic/widgets/icon_widgets/volume_icon.dart';
import 'package:match_magic/widgets/toggle_switch.dart';
import 'package:package_info_plus/package_info_plus.dart';
class OptionsScreen extends StatefulWidget {
// final Function(Background) onUpdateBackground;
OptionsScreen();
@override
State<OptionsScreen> createState() => _OptionsScreenState();
}
class _OptionsScreenState extends State<OptionsScreen> {
bool isSound = true;
bool isMusic = true;
// Background? backgroundImage;
@override
void initState() {
super.initState();
fetchSoundAndMusicState();
// _loadBackground();
getVersionNumber();
// _checkPremiumStatus();
}
Future<String> getVersionNumber() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
// Future<void> _loadBackground() async {
// final background = await MainDB.instance.getBackground();
// setState(() {
// backgroundImage = background;
// });
// }
// Future<void> _checkPremiumStatus() async {
// bool isPremium = await AppStateManager.isPremium();
// setState(() {
// _isPremium = isPremium;
// });
// }
// Future<void> refreshOptionsScreen() async {
// await _checkPremiumStatus();
// setState(() {});
// }
Future<void> fetchSoundAndMusicState() async {
bool soundEnabled = await MainDB.instance.getSoundEnabled();
bool musicEnabled = await MainDB.instance.getMusicEnabled();
setState(() {
isSound = soundEnabled;
isMusic = musicEnabled;
});
}
// void updateBackground(Background newBackground) {
// setState(() {
// backgroundImage = newBackground;
// });
// widget.onUpdateBackground(newBackground);
// }
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Container(
// decoration: BoxDecoration(
// image: DecorationImage(
// image: BackgroundImageProvider.getBackgroundImage(
// backgroundImage ?? Background.sky),
// fit: BoxFit.cover,
// ),
// ),
// ),
Container(
padding:
const EdgeInsets.only(top: 38, left: 16, right: 16, bottom: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
IconButton(
onPressed: () {
Navigator.pop(context, true);
},
icon: ArrowLeftIcon()),
Text(
'Options',
style: AppStyles.subtitleTextStyle
.copyWith(color: AppStyles.accentColorTopBar),
),
],
),
const SizedBox(
height: 28,
),
Expanded(
child: ListView(
children: [
// Sound settings
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppStyles.mainBackground,
),
padding: EdgeInsets.only(
top: 12, bottom: 12, right: 12, left: 18),
child: Column(
children: [
const SizedBox(
height: 10,
),
const Row(
children: [
Text(
'Sound settings',
style: AppStyles.subtitleTextStyle,
),
],
),
const SizedBox(
height: 10,
),
Row(
children: [
VolumeIcon(),
const SizedBox(
width: 6,
),
const Text(
'Sound Effects',
style: TextStyle(fontSize: 16),
),
const Expanded(child: SizedBox()),
ToggleSwitch(
value: isSound,
onChanged: (value) async {
setState(() {
isSound = value;
});
await MainDB.instance
.saveSoundEnabled(value);
})
],
),
const SizedBox(
height: 10,
),
Row(
children: [
MusicIcon(),
const SizedBox(
width: 6,
),
const Text(
'Music',
style: TextStyle(fontSize: 16),
),
const Expanded(child: SizedBox()),
ToggleSwitch(
value: isMusic,
onChanged: (value) async {
setState(() {
isMusic = value;
});
await MainDB.instance
.saveMusicEnabled(value);
})
],
),
],
),
),
const SizedBox(
height: 12,
),
// High score
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppStyles.mainBackground,
),
child: ExpansionTile(
backgroundColor: AppStyles.mainBackground,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
title: Row(
children: [
StarIcon(),
SizedBox(
width: 6,
),
Text('High score'),
],
),
children: [
ListTile(
title: FutureBuilder<int?>(
future: MainDB.instance.getHighScoreValue(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
'${snapshot.data}',
style: TextStyle(fontSize: 16),
);
} else {
return Text('HighScore: 0');
}
},
),
),
],
),
),
const SizedBox(
height: 12,
),
],
),
),
// About
Container(
margin: EdgeInsets.only(top: 16),
child: Column(
children: [
Text('MatchMagic. Copyright 2024 Cypher Stack, LLC'),
FutureBuilder<String>(
future: getVersionNumber(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Version: ${snapshot.data}');
} else {
return Text('Version: loading...');
}
},
)
],
),
),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,206 @@
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:match_magic/db/main_db.dart';
import 'package:match_magic/game/board.dart';
import 'package:match_magic/screens/main_menu_screen.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/widgets/icon_widgets/volume_icon.dart';
import 'package:match_magic/widgets/overlays/game_overlay/hint_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/pause_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/restart_button.dart';
import 'package:match_magic/widgets/toggle_switch.dart';
class PauseMenu extends StatefulWidget {
static const String id = 'PauseMenu';
final Board gameRef;
const PauseMenu({super.key, required this.gameRef});
@override
State<PauseMenu> createState() => _PauseMenuState();
}
class _PauseMenuState extends State<PauseMenu> {
bool isMusic = true;
bool isSound = Board.isSoundPlaying;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 70, horizontal: 16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFEBEBEB).withOpacity(0.6),
Color(0xFFBFBEC0).withOpacity(0.6),
],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(
height: 122,
),
// Pause menu title.
Padding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: Text('Game Paused',
style: AppStyles.titleTextStyle.copyWith(
fontSize: 32, color: AppStyles.accentColorTopBar)),
),
// Sound settings
Card(
child: Container(
height: 56,
padding: const EdgeInsets.all(12),
child: Row(
children: [
const VolumeIcon(),
const SizedBox(
width: 6,
),
const Text(
'Sound settings',
style: TextStyle(),
),
const Expanded(child: SizedBox()),
ToggleSwitch(
value: isSound,
onChanged: (value) async {
setState(() {
isSound = value;
Board.isSoundPlaying = !Board.isSoundPlaying;
});
await MainDB.instance.saveSoundEnabled(value);
})
],
),
),
),
const SizedBox(
height: 6,
),
// Music
// Card(
// child: Container(
// height: 56,
// padding: const EdgeInsets.all(12),
// child: Row(
// children: [
// const MusicIcon(),
// const SizedBox(
// width: 6,
// ),
// const Text(
// 'Music',
// style: TextStyle(),
// ),
// const Expanded(child: SizedBox()),
// ToggleSwitch(
// value: isMusic,
// onChanged: (value) async {
// setState(() {
// isMusic = value;
// if (Board.isMusicPlaying) {
// FlameAudio.bgm.stop();
// } else {
// FlameAudio.bgm.play('.ogg');
// }
// Board.isMusicPlaying = !Board.isMusicPlaying;
// });
// await MainDB.instance.saveMusicEnabled(value);
// })
// ],
// ),
// ),
// ),
const Expanded(child: SizedBox()),
Row(
children: [
// Exit button
Expanded(
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
backgroundColor: AppStyles.mainBackground,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: const Text(
'Quit',
style: AppStyles.titleTextStyle,
),
content: const Text(
'Are you sure you want to leave the game? You progress will be lost.',
style: AppStyles.subtitleTextStyle,
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'CANCEL',
style: AppStyles.subtitleTextStyle.copyWith(
fontSize: 14, color: AppStyles.accentColor),
),
),
TextButton(
onPressed: () {
FlameAudio.bgm.stop();
widget.gameRef.overlays.remove(PauseMenu.id);
widget.gameRef.removed;
widget.gameRef.resumeEngine();
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => MainMenuScreen(),
),
);
},
child: Text(
'EXIT',
style: AppStyles.subtitleTextStyle.copyWith(
fontSize: 14, color: AppStyles.accentColor),
),
),
],
),
);
},
child: const Text('Exit'),
),
),
const SizedBox(
width: 16,
),
// Continue button
Expanded(
child: ElevatedButton(
onPressed: () async {
widget.gameRef.overlays.remove(PauseMenu.id);
widget.gameRef.overlays.add(PauseButton.id);
widget.gameRef.overlays.add(RestartButton.id);
widget.gameRef.overlays.add(HintButton.id);
widget.gameRef.resumeEngine();
print('${Board.isSoundPlaying}');
},
child: const Text('Continue'),
)),
],
),
],
),
);
}
}

43
lib/styles/styles.dart Normal file
View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class AppStyles {
// Fonts
static const TextStyle titleTextStyle = TextStyle(
fontFamily: 'MPLUSRounded1c-ExtraBold',
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppStyles.textColor,
);
static const TextStyle subtitleTextStyle = TextStyle(
fontFamily: 'MPLUSRounded1c-Medium',
fontSize: 16,
color: AppStyles.textColor,
);
// AppColor
static const Color primaryColor = Color(0xFF4124DF);
static const Color accentColor = Color(0xFFFF4FA3);
static const Color accentColorTopBar = Color(0xFF6014DC);
static const Color textColor = Colors.black;
static const Color linkColor = Color(0xFF3753E8);
static const Color mainBackground = Color(0xFFFFFFFF);
// Button
static const buttonGradient = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xBFFF4FA3), Color(0xBF3E24E0)],
);
static const buttonPrimaryDefaultText = Color(0xFFFBF8FF);
// ToggleSwitch
static const toggleSwitchBg = Color(0xFFFFC9ED);
static const toggleSwitchTextInactive = Color(0xFFFFFFFF);
static const toggleSwitchActiveBg = Color(0xFFFF4FA3);
static const toggleSwitchTextActive = Color(0xFFFFDCF3);
// TextField
static const textField = Color(0xFFFEF1FF);
}

28
lib/utilities/assets.dart Normal file
View File

@ -0,0 +1,28 @@
abstract class Assets {
static const svg = _SVG();
static const png = _PNG();
}
class _SVG {
const _SVG();
String get arrowLeft => "assets/svg/arrow-left.svg";
String get check => "assets/svg/check.svg";
String get checkCircle => "assets/svg/check-circle.svg";
String get chevronDown => "assets/svg/chevron-down.svg";
String get chevronRight => "assets/svg/chevron-right.svg";
String get clipboard => "assets/svg/clipboard.svg";
String get music => "assets/svg/music.svg";
String get pauseCircle => "assets/svg/pause-circle.svg";
String get star => "assets/svg/star.svg";
String get ticket => "assets/svg/ticket.svg";
String get volume => "assets/svg/volume.svg";
}
class _PNG {
const _PNG();
// String get logo => "assets/images/teeny_pop_logo.png";
// Background
// String get backgroundSky => "assets/images/background_sky.png";
}

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:match_magic/styles/styles.dart';
class GradientButton extends StatelessWidget {
final VoidCallback onPressed;
final String buttonText;
const GradientButton({
Key? key,
required this.onPressed,
required this.buttonText,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 40,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100)),
gradient: AppStyles.buttonGradient),
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
),
child: Text(
buttonText.toUpperCase(),
style: const TextStyle(
color: AppStyles.buttonPrimaryDefaultText,
fontWeight: FontWeight.w700),
),
),
);
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/utilities/assets.dart';
class ArrowLeftIcon extends StatelessWidget {
const ArrowLeftIcon({
Key? key,
this.width = 24,
this.height = 24,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.arrowLeft,
width: width,
height: height,
color: AppStyles.accentColorTopBar,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class CheckCircleIcon extends StatelessWidget {
const CheckCircleIcon({
Key? key,
this.width = 24,
this.height = 24,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.checkCircle,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class CheckIcon extends StatelessWidget {
const CheckIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.check,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class ChevronDownIcon extends StatelessWidget {
const ChevronDownIcon({
Key? key,
this.width = 24,
this.height = 24,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.chevronDown,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class ChevronRightIcon extends StatelessWidget {
const ChevronRightIcon({
Key? key,
this.width = 24,
this.height = 24,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.chevronRight,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/utilities/assets.dart';
class ClipboardIcon extends StatelessWidget {
const ClipboardIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.clipboard,
width: width,
height: height,
color: AppStyles.accentColor,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class MusicIcon extends StatelessWidget {
const MusicIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.music,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/styles/styles.dart';
import 'package:match_magic/utilities/assets.dart';
class PauseCircleIcon extends StatelessWidget {
const PauseCircleIcon({
Key? key,
this.width = 34,
this.height = 34,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.pauseCircle,
width: width,
height: height,
color: AppStyles.accentColorTopBar,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class StarIcon extends StatelessWidget {
const StarIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.star,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class TicketIcon extends StatelessWidget {
const TicketIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.ticket,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:match_magic/utilities/assets.dart';
class VolumeIcon extends StatelessWidget {
const VolumeIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.volume,
width: width,
height: height,
);
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:match_magic/game/board.dart';
class HintButton extends StatelessWidget {
final Board gameRef;
static const String id = 'HintButton';
const HintButton({Key? key, required this.gameRef}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 46,
left: 220,
child: ElevatedButton(
onPressed: () {
gameRef.showHint();
},
child: const Text(
'Hint',
style: TextStyle(color: Colors.black),
),
),
);
}
}

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:match_magic/game/board.dart';
import 'package:match_magic/screens/pause_screen.dart';
import 'package:match_magic/widgets/icon_widgets/pause_circle_icon.dart';
import 'package:match_magic/widgets/overlays/game_overlay/hint_button.dart';
import 'package:match_magic/widgets/overlays/game_overlay/restart_button.dart';
class PauseButton extends StatelessWidget {
final Board gameRef;
static const String id = 'PauseButton';
const PauseButton({Key? key, required this.gameRef}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
top: 46,
left: 10,
child: IconButton(
onPressed: () {
// AdManager.loadInterstitialAd();
gameRef.pauseEngine();
gameRef.overlays.add(PauseMenu.id);
gameRef.overlays.remove(PauseButton.id);
gameRef.overlays.remove(RestartButton.id);
gameRef.overlays.remove(HintButton.id);
},
icon: PauseCircleIcon(),
),
);
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:match_magic/game/board.dart';
class RestartButton extends StatelessWidget {
final Board gameRef;
static const String id = 'RestartButton';
const RestartButton({Key? key, required this.gameRef}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 46,
left: 80,
child: ElevatedButton(
onPressed: () {
gameRef.newGame();
},
child: const Text(
'Restart',
style: TextStyle(color: Colors.black),
),
),
);
}
}

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:match_magic/styles/styles.dart';
class RadioButton<T> extends StatelessWidget {
final T value;
final T? groupValue;
final ValueChanged<T?>? onChanged;
const RadioButton({
required this.value,
required this.groupValue,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Radio<T>(
value: value,
groupValue: groupValue,
onChanged: onChanged,
activeColor: AppStyles.accentColor,
visualDensity: const VisualDensity(
horizontal: VisualDensity.minimumDensity,
vertical: VisualDensity.minimumDensity,
),
),
],
);
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:match_magic/styles/styles.dart';
class ToggleSwitch extends StatelessWidget {
final ValueChanged<bool>? onChanged;
final bool value;
const ToggleSwitch({
Key? key,
required this.onChanged,
required this.value,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Switch(
value: value,
onChanged: onChanged,
activeColor: AppStyles.toggleSwitchTextActive,
activeTrackColor: AppStyles.toggleSwitchActiveBg,
inactiveThumbColor: AppStyles.toggleSwitchTextInactive,
inactiveTrackColor: AppStyles.toggleSwitchBg,
trackOutlineColor: MaterialStateProperty.all(Colors.white.withOpacity(0)),
);
}
}

View File

@ -7,9 +7,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h> #include <audioplayers_linux/audioplayers_linux_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
g_autoptr(FlPluginRegistrar) isar_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "IsarFlutterLibsPlugin");
isar_flutter_libs_plugin_register_with_registrar(isar_flutter_libs_registrar);
} }

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux audioplayers_linux
isar_flutter_libs
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -6,9 +6,15 @@ import FlutterMacOS
import Foundation import Foundation
import audioplayers_darwin import audioplayers_darwin
import isar_flutter_libs
import package_info_plus
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }

View File

@ -1,6 +1,30 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
url: "https://pub.dev"
source: hosted
version: "61.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
url: "https://pub.dev"
source: hosted
version: "5.13.0"
args:
dependency: transitive
description:
name: args
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -73,6 +97,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7"
url: "https://pub.dev"
source: hosted
version: "2.4.11"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe
url: "https://pub.dev"
source: hosted
version: "7.3.1"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
url: "https://pub.dev"
source: hosted
version: "8.9.2"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -81,6 +169,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -89,6 +185,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
version: "4.10.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -97,6 +201,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -113,6 +225,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.8" version: "1.0.8"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
dartx:
dependency: transitive
description:
name: dartx
sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -174,6 +302,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.2"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
url: "https://pub.dev"
source: hosted
version: "2.0.10+1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -184,6 +320,30 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -192,6 +352,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.2.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -200,6 +368,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
isar:
dependency: "direct main"
description:
name: isar
sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
isar_flutter_libs:
dependency: "direct main"
description:
name: isar_flutter_libs
sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
isar_generator:
dependency: "direct dev"
description:
name: isar_generator
sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -232,6 +448,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -256,6 +480,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.12.0" version: "1.12.0"
mime:
dependency: transitive
description:
name: mime
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -272,6 +504,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.0.1"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918
url: "https://pub.dev"
source: hosted
version: "8.0.2"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66
url: "https://pub.dev"
source: hosted
version: "3.0.1"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -280,8 +536,16 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
path_provider: path_parsing:
dependency: transitive dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
@ -328,6 +592,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -344,6 +616,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -352,11 +632,107 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.2" version: "6.1.2"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.3.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
url: "https://pub.dev"
source: hosted
version: "2.5.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -389,6 +765,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -421,6 +805,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.0" version: "0.7.0"
time:
dependency: transitive
description:
name: time
sha256: ad8e018a6c9db36cb917a031853a1aae49467a93e0d464683e029537d848c221
url: "https://pub.dev"
source: hosted
version: "2.1.4"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -437,6 +837,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.0" version: "4.5.0"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
url: "https://pub.dev"
source: hosted
version: "1.1.11+1"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -453,6 +877,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.1" version: "14.2.1"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web: web:
dependency: transitive dependency: transitive
description: description:
@ -461,6 +893,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
win32:
dependency: transitive
description:
name: win32
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
url: "https://pub.dev"
source: hosted
version: "5.5.4"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -469,6 +925,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
xxh3:
dependency: transitive
description:
name: xxh3
sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7
url: "https://pub.dev"
source: hosted
version: "1.0.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks: sdks:
dart: ">=3.4.3 <4.0.0" dart: ">=3.4.3 <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.22.0"

View File

@ -38,7 +38,13 @@ dependencies:
cupertino_icons: ^1.0.6 cupertino_icons: ^1.0.6
flame: ^1.18.0 flame: ^1.18.0
audioplayers: ^6.1.0 audioplayers: ^6.1.0
flutter_svg: ^2.0.9
flame_audio: ^2.10.3 flame_audio: ^2.10.3
isar: ^3.1.0+1
isar_flutter_libs: ^3.1.0+1
package_info_plus: ^8.0.2
path_provider: ^2.1.4
shared_preferences: ^2.3.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -50,6 +56,8 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^3.0.0 flutter_lints: ^3.0.0
isar_generator: ^3.1.0+1
build_runner: ^2.4.11
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
@ -64,17 +72,9 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/images/crystal1.png - assets/images/
- assets/images/crystal2.png - assets/svg/
- assets/images/crystal3.png - assets/audio/
- assets/images/crystal4.png
- assets/images/crystal5.png
- assets/images/crystal6.png
- assets/images/crystal7.png
- assets/images/magic_cube.png
- assets/audio/explosion.ogg
- assets/audio/four_elements.ogg
- assets/audio/select.ogg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see

View File

@ -7,8 +7,11 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h> #include <audioplayers_windows/audioplayers_windows_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar( AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
IsarFlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin"));
} }

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows audioplayers_windows
isar_flutter_libs
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST