diff --git a/assets/images/game_over_score.png b/assets/images/game_over_score.png new file mode 100644 index 0000000..e2cd7e5 Binary files /dev/null and b/assets/images/game_over_score.png differ diff --git a/assets/svg/arrow-left.svg b/assets/svg/arrow-left.svg new file mode 100644 index 0000000..339776e --- /dev/null +++ b/assets/svg/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/check-circle.svg b/assets/svg/check-circle.svg new file mode 100644 index 0000000..19abddf --- /dev/null +++ b/assets/svg/check-circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/check.svg b/assets/svg/check.svg new file mode 100644 index 0000000..5406b7e --- /dev/null +++ b/assets/svg/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/chevron-down.svg b/assets/svg/chevron-down.svg new file mode 100644 index 0000000..86bb99c --- /dev/null +++ b/assets/svg/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/chevron-right.svg b/assets/svg/chevron-right.svg new file mode 100644 index 0000000..e7ef342 --- /dev/null +++ b/assets/svg/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/clipboard.svg b/assets/svg/clipboard.svg new file mode 100644 index 0000000..bab777f --- /dev/null +++ b/assets/svg/clipboard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/music.svg b/assets/svg/music.svg new file mode 100644 index 0000000..1fb1c17 --- /dev/null +++ b/assets/svg/music.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/pause-circle.svg b/assets/svg/pause-circle.svg new file mode 100644 index 0000000..196453f --- /dev/null +++ b/assets/svg/pause-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/star.svg b/assets/svg/star.svg new file mode 100644 index 0000000..6558583 --- /dev/null +++ b/assets/svg/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/svg/ticket.svg b/assets/svg/ticket.svg new file mode 100644 index 0000000..06726cb --- /dev/null +++ b/assets/svg/ticket.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/svg/volume.svg b/assets/svg/volume.svg new file mode 100644 index 0000000..e8616ea --- /dev/null +++ b/assets/svg/volume.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/db/main_db.dart b/lib/db/main_db.dart new file mode 100644 index 0000000..db1e449 --- /dev/null +++ b/lib/db/main_db.dart @@ -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 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 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 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 getSoundEnabled() async { + final settings = await isar.settings.where().findFirst(); + return settings?.isSoundEnabled ?? true; + } + + Future getMusicEnabled() async { + final settings = await isar.settings.where().findFirst(); + return settings?.isMusicEnabled ?? true; + } + + Future 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 getHighScoreValue() async { + final highScore = await isar.highScores.where().findFirst(); + + return highScore?.highScoreValue; + } +} diff --git a/lib/game/board.dart b/lib/game/board.dart index 0f499be..c6ca576 100644 --- a/lib/game/board.dart +++ b/lib/game/board.dart @@ -3,40 +3,65 @@ import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/game.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/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 'package:flame/sprite.dart'; -import 'swap_notifier.dart'; class Board extends FlameGame { - final List sprites; - final SwapNotifier swapNotifier; - final Sprite magicCubeSprite; + BuildContext context; + final bool gameMode; + late GameModeManager gameModeManager; + static const int rows = 8; static const int cols = 8; late double tileSize; + Tile? selectedTile; List> tiles = []; int? selectedRow; int? selectedCol; bool animating = false; + bool isGridInitialized = false; Tile? lastMovedTile; Tile? lastMovedByGravity; + static int score = 0; + late TextComponent _playerScore; + late TextComponent _playerMoves; + late TextComponent _remainingTime; bool isFirstLaunch = true; - Board({ - required this.sprites, - required this.swapNotifier, - required this.magicCubeSprite, - }); + bool isGameOver = false; + static bool isSoundPlaying = true; + + Board( + this.context, { + required this.gameMode, + }) { + gameModeManager = GameModeManager( + currentMode: gameMode, + onGameOver: _onGameOver, + ); + } @override Future onLoad() async { super.onLoad(); - _resetGame(); + await loadImages(); + gameModeManager.initializeMode(); + + newGame(); } - void _resetGame() { + void newGame() { + isFirstLaunch = true; + resetGame(); + gameModeManager.initializeMode(); tiles.clear(); selectedRow = null; selectedCol = null; @@ -44,16 +69,82 @@ class Board extends FlameGame { tileSize = size.x / cols; _initializeGrid(isFirstLaunch); _removeInitialMatches(); + 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() { - isFirstLaunch = true; - swapNotifier.resetScore(); - _resetGame(); + void resetGame() { + isGameOver = false; + this.overlays.remove(GameOverMenu.id); + + score = 0; + gameModeManager.resetMode(); + selectedTile = null; + final List 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) { + int totalTiles = rows * cols; + int completedTiles = 0; for (int row = 0; row < rows; row++) { List rowTiles = []; for (int col = 0; col < cols; col++) { @@ -64,7 +155,7 @@ class Board extends FlameGame { : Vector2(col * tileSize, row * tileSize); var tile = Tile( - sprite: sprites[spriteIndex], + sprite: Tile.crystals[spriteIndex], spriteIndex: spriteIndex, size: Vector2.all(tileSize), position: initialPosition, @@ -80,22 +171,31 @@ class Board extends FlameGame { tile.add( MoveEffect.to( - Vector2(col * tileSize, row * tileSize), + Vector2(col * tileSize, row * tileSize + 200), EffectController( duration: 0.5, startDelay: delay, curve: Curves.bounceOut, ), + onComplete: () { + completedTiles++; + if (completedTiles == totalTiles) { + isGridInitialized = true; + } + }, ), ); } } tiles.add(rowTiles); } + if (!animate) { + isGridInitialized = true; + } } int _randomElement() { - return Random().nextInt(sprites.length); + return Random().nextInt(Tile.crystals.length); } void _removeInitialMatches() { @@ -106,7 +206,7 @@ class Board extends FlameGame { for (int col = 0; col < cols; col++) { if (_hasMatch(row, col)) { int spriteIndex = _randomElement(); - tiles[row][col]!.sprite = sprites[spriteIndex]; + tiles[row][col]!.sprite = Tile.crystals[spriteIndex]; tiles[row][col]!.spriteIndex = spriteIndex; hasMatches = true; } @@ -115,53 +215,53 @@ class Board extends FlameGame { } while (hasMatches); } - // Future handleTileSwipe(Tile tile, Vector2 delta) async { - // if (animating) return; + int _calculateScore(int matchLength) { + if (matchLength == 3) { + return 50; + } else if (matchLength == 4) { + return 400; + } else if (matchLength == 5) { + return 200; + } + return 0; + } - // int row = tile.row; - // int col = tile.col; - // Tile? targetTile; + // Moving elements - // if (delta.x.abs() > delta.y.abs()) { - // if (delta.x > 0 && col < cols - 1) { - // targetTile = tiles[row][col + 1]; - // } else if (delta.x < 0 && col > 0) { - // targetTile = tiles[row][col - 1]; - // } - // } else { - // if (delta.y > 0 && row < rows - 1) { - // targetTile = tiles[row + 1][col]; - // } else if (delta.y < 0 && row > 0) { - // targetTile = tiles[row - 1][col]; - // } - // } + void selectTile(Tile tile) { + if (selectedTile == null) { + selectedTile = tile; + tile.select(); + } else { + if (_isNeighbor(selectedTile!, tile) || selectedTile!.isMagicCube) { + } else { + selectedTile?.deselect(); + selectedTile = tile; + tile.select(); + } + } + } - // if (targetTile != null) { - // animating = true; - // lastMovedTile = tile; - // 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; - // } - // } + 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); + } Future handleTileSwipe(Tile tile, Vector2 delta) async { - if (animating) return; + if (!isGridInitialized || animating) return; Tile? targetTile; if (tile.isMagicCube) { targetTile = _getTileBySwipeDirection(tile, delta); if (targetTile != null) { _removeAllOfType(targetTile.spriteIndex); + _animateRemoveTile(tile); + isSoundPlaying = await MainDB.instance.getSoundEnabled(); + if (isSoundPlaying) { + AudioManager.playExplosionSound(); + } + + tiles[tile.row][tile.col] = null; } return; } @@ -174,11 +274,11 @@ class Board extends FlameGame { swapTiles(tile, targetTile, true); await Future.delayed(const Duration(milliseconds: 300)); - - if (!checkMatches()) { + bool matchesFound = await checkMatches(); + if (!matchesFound) { swapTiles(tile, targetTile, true); } else { - swapNotifier.incrementMoveCount(); + GameModeManager.movesLeft--; } selectedRow = null; selectedCol = null; @@ -207,28 +307,7 @@ class Board extends FlameGame { 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) { - // final tempPosition1 = tile1.position.clone(); - // final tempPosition2 = tile2.position.clone(); final tempRow1 = tile1.row; final tempCol1 = tile1.col; final tempRow2 = tile2.row; @@ -252,7 +331,49 @@ class Board extends FlameGame { tile1.deselect(); } - bool checkMatches({bool simulate = false}) { + // Hint button + + Future 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 _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 checkMatches({bool simulate = false}) async { if (simulate) { for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { @@ -279,7 +400,10 @@ class Board extends FlameGame { for (final match in matches) { points += _removeMatchedElements(match[0], match[1]); } - AudioManager.playSelectSound(); + isSoundPlaying = await MainDB.instance.getSoundEnabled(); + if (isSoundPlaying) { + AudioManager.playSelectSound(); + } Future.delayed(const Duration(milliseconds: 300), () { _applyGravity(); Future.delayed(const Duration(milliseconds: 300), () { @@ -290,7 +414,7 @@ class Board extends FlameGame { }); }); }); - swapNotifier.incrementScore(points); + score += points; return true; } @@ -302,46 +426,81 @@ class Board extends FlameGame { final value = tiles[row][col]?.spriteIndex; 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++; - 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++; + } if (count >= 3) return true; 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++; - 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++; + } 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 score = 0; final int? value = tiles[row][col]?.spriteIndex; - bool bombTriggered = false; - Tile? tileToTransformIntoBomb = null; + bool specialTriggered = false; + Tile? tileToTransformIntoSpecial; 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; - while (right < cols - 1 && tiles[row][right + 1]?.spriteIndex == value) + 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) { - tileToTransformIntoBomb = lastMovedTile ?? lastMovedByGravity; + if (right - left + 1 == 4) { + tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity; + } else if (right - left + 1 >= 5) { + tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity; } for (int i = left; i <= right; i++) { if (tiles[row][i] != null) { if (tiles[row][i]!.isBomb) { - bombTriggered = true; + specialTriggered = true; _triggerBomb(row, i); } - if (tiles[row][i] != tileToTransformIntoBomb) { + if (tiles[row][i] != tileToTransformIntoSpecial) { _animateRemoveTile(tiles[row][i]!); tiles[row][i] = null; } @@ -350,25 +509,30 @@ class Board extends FlameGame { } 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; - while (bottom < rows - 1 && tiles[bottom + 1][col]?.spriteIndex == value) + 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) { - tileToTransformIntoBomb = lastMovedTile ?? lastMovedByGravity; + if (bottom - top + 1 == 4) { + tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity; + } else if (bottom - top + 1 >= 5) { + tileToTransformIntoSpecial = lastMovedTile ?? lastMovedByGravity; } for (int i = top; i <= bottom; i++) { if (tiles[i][col] != null) { if (tiles[i][col]!.isBomb) { - bombTriggered = true; + specialTriggered = true; _triggerBomb(i, col); } - if (tiles[i][col] != tileToTransformIntoBomb) { + if (tiles[i][col] != tileToTransformIntoSpecial) { _animateRemoveTile(tiles[i][col]!); tiles[i][col] = null; } @@ -376,19 +540,82 @@ class Board extends FlameGame { } } - if (bombTriggered) { - _triggerBomb(row, col); + if (tileToTransformIntoSpecial != null) { + if ((right - left + 1 >= 5) || (bottom - top + 1 >= 5)) { + _createMagicCube( + tileToTransformIntoSpecial.row, tileToTransformIntoSpecial.col); + } else { + _createBomb( + tileToTransformIntoSpecial.row, tileToTransformIntoSpecial.col); + } } - if (tileToTransformIntoBomb != null) { - _createBomb(tileToTransformIntoBomb.row, tileToTransformIntoBomb.col); - AudioManager.playFourElementsSound(); + if (specialTriggered) { + _triggerBomb(row, col); } 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]; if (tile == null || !tile.isBomb) return; @@ -412,7 +639,10 @@ class Board extends FlameGame { _animateRemoveTile(tile); tiles[row][col] = null; - AudioManager.playExplosionSound(); + isSoundPlaying = await MainDB.instance.getSoundEnabled(); + if (isSoundPlaying) { + AudioManager.playExplosionSound(); + } } void _animateBombExplosion(Vector2 position) { @@ -433,36 +663,11 @@ class Board extends FlameGame { ); } - int _calculateScore(int matchLength) { - if (matchLength == 3) { - return 50; - } else if (matchLength == 4) { - return 100; - } else if (matchLength == 5) { - return 200; + void _createBomb(int row, int col) async { + isSoundPlaying = await MainDB.instance.getSoundEnabled(); + if (isSoundPlaying) { + AudioManager.playFourElementsSound(); } - 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]; if (tile != null) { 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) { final bombPosition = bombTile.position.clone(); final bombRow = bombTile.row; @@ -521,259 +720,39 @@ class Board extends FlameGame { tiles[bombRow][bombCol] = null; } - 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), () {}); - lastMovedByGravity = tiles[row][col]; - break; - } - } - } - } + // Magic cube + + void _createMagicCube(int row, int col) async { + isSoundPlaying = await MainDB.instance.getSoundEnabled(); + if (isSoundPlaying) { + AudioManager.playFourElementsSound(); } + var tile = tiles[row][col]; + tile?.sprite = Tile.magicCubeSprite; + tile?.isMagicCube = true; } - 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: 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), () {}); - } - } - } + // Game over + + void _onGameOver() { + print("Game Over! Score: ${score}"); + showGameOverScreen(); } - Tile? findHint() { - for (int row = 0; row < rows; row++) { - for (int col = 0; col < cols; col++) { - Tile? tile = tiles[row][col]; - - if (col < cols - 1 && _canSwap(row, col, row, col + 1)) { - return tile; - } - if (row < rows - 1 && _canSwap(row, col, row + 1, col)) { - return tile; - } - } + void showGameOverScreen() { + isGameOver = true; + remove(_playerScore); + if (gameModeManager.currentMode) { + remove(_playerMoves); + } else { + remove(_remainingTime); } - return null; - } - bool _canSwap(int row1, int col1, int row2, int col2) { - Tile tempTile1 = tiles[row1][col1]!; - Tile tempTile2 = tiles[row2][col2]!; - - tiles[row1][col1] = tempTile2; - 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(); - } + this.overlays.remove(PauseButton.id); + this.overlays.remove(RestartButton.id); + this.overlays.remove(HintButton.id); + MainDB.instance.addHighScore(score, () { + this.overlays.add(GameOverMenu.id); + }); } } - - - - // 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; - // } - // } - // } - // } - // } - // } \ No newline at end of file diff --git a/lib/game/game_mode_manager.dart b/lib/game/game_mode_manager.dart new file mode 100644 index 0000000..de37931 --- /dev/null +++ b/lib/game/game_mode_manager.dart @@ -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(); + } + } +} diff --git a/lib/game/match_magic_game.dart b/lib/game/match_magic_game.dart deleted file mode 100644 index 27fdfc9..0000000 --- a/lib/game/match_magic_game.dart +++ /dev/null @@ -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 { - late Future> _crystalsFuture; - late Future _magicCubeFuture; - Board? board; - - @override - void initState() { - super.initState(); - _crystalsFuture = SpriteLoader.loadCrystalSprites(); - _magicCubeFuture = SpriteLoader.loadMagicCubeSprite(); - AudioManager.load(); - } - - void _restartGame() { - context.read().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().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>( - 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( - 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(), - ); - - 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( - 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( - 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), - ), - ), - ); - }, - ), - ], - ), - ); - } -} diff --git a/lib/game/sprite_loader.dart b/lib/game/sprite_loader.dart index d1cb4ac..22568ca 100644 --- a/lib/game/sprite_loader.dart +++ b/lib/game/sprite_loader.dart @@ -1,15 +1,13 @@ -import 'package:flame/components.dart'; +// import 'package:flame/components.dart'; +import 'package:flame/flame.dart'; -class SpriteLoader { - static Future> loadCrystalSprites() async { - List sprites = []; - for (int i = 1; i <= 7; i++) { - sprites.add(await Sprite.load('crystal$i.png')); - } - return sprites; - } - - static Future loadMagicCubeSprite() async { - return Sprite.load('magic_cube.png'); - } +Future loadImages() async { + await Flame.images.load('crystal1.png'); + await Flame.images.load('crystal2.png'); + await Flame.images.load('crystal3.png'); + await Flame.images.load('crystal4.png'); + await Flame.images.load('crystal5.png'); + await Flame.images.load('crystal6.png'); + await Flame.images.load('crystal7.png'); + await Flame.images.load('magic_cube.png'); } diff --git a/lib/game/swap_notifier.dart b/lib/game/swap_notifier.dart deleted file mode 100644 index ad9c754..0000000 --- a/lib/game/swap_notifier.dart +++ /dev/null @@ -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(); - } -} diff --git a/lib/game/tile.dart b/lib/game/tile.dart index e607029..194d226 100644 --- a/lib/game/tile.dart +++ b/lib/game/tile.dart @@ -1,6 +1,9 @@ +import 'dart:math'; + import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/events.dart'; +import 'package:flame/flame.dart'; import 'package:flutter/material.dart'; import 'board.dart'; @@ -26,8 +29,17 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks { required this.col, // required this.onTileTap, 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 // bool onTapDown(TapDownEvent event) { // if (isAnimating || (parent is Board && (parent as Board).animating)) { @@ -37,6 +49,18 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks { // return true; // } + static List crystals = [ + crystal1(), + crystal2(), + crystal3(), + crystal4(), + crystal5(), + crystal6(), + crystal7(), + ]; + + static Sprite magicCubeSprite = magicCube(); + @override bool onDragStart(DragStartEvent event) { 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')); +} diff --git a/lib/main.dart b/lib/main.dart index ce89a71..ed044b0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,20 +1,25 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:match_magic/screens/main_menu.dart'; -import 'package:provider/provider.dart'; -import 'game/match_magic_game.dart'; -import 'game/swap_notifier.dart'; +import 'package:match_magic/db/main_db.dart'; +import 'package:match_magic/screens/main_menu_screen.dart'; -void main() { - runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider(create: (_) => SwapNotifier()), - ], - child: const MyApp(), +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await MainDB.instance.initMainDB(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]).then( + (value) => runApp( + // MultiProvider( + // providers: [ + // ChangeNotifierProvider(create: (_) => SwapNotifier()), + // ], + // child: const + MyApp(), ), + // ) ); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } class MyApp extends StatelessWidget { @@ -22,8 +27,8 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp( - home: MainMenu(), + return MaterialApp( + home: MainMenuScreen(), ); } } diff --git a/lib/models/app_state_manager.dart b/lib/models/app_state_manager.dart new file mode 100644 index 0000000..6e8d5a7 --- /dev/null +++ b/lib/models/app_state_manager.dart @@ -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 getMode() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getBool(_modeKey) ?? true; + } + + static Future setMode(bool mode) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setBool(_modeKey, mode); + } +} diff --git a/lib/models/isar/high_score.dart b/lib/models/isar/high_score.dart new file mode 100644 index 0000000..f8d548b --- /dev/null +++ b/lib/models/isar/high_score.dart @@ -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}); +} diff --git a/lib/models/isar/high_score.g.dart b/lib/models/isar/high_score.g.dart new file mode 100644 index 0000000..609f76f --- /dev/null +++ b/lib/models/isar/high_score.g.dart @@ -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 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 offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + return bytesCount; +} + +void _highScoreSerialize( + HighScore object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.highScoreValue); +} + +HighScore _highScoreDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = HighScore( + highScoreValue: reader.readLong(offsets[0]), + ); + object.id = id; + return object; +} + +P _highScoreDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> 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> _highScoreGetLinks(HighScore object) { + return []; +} + +void _highScoreAttach(IsarCollection col, Id id, HighScore object) { + object.id = id; +} + +extension HighScoreQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension HighScoreQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder 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 idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder 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 { + QueryBuilder + highScoreValueEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'highScoreValue', + value: value, + )); + }); + } + + QueryBuilder + highScoreValueGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'highScoreValue', + value: value, + )); + }); + } + + QueryBuilder + highScoreValueLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'highScoreValue', + value: value, + )); + }); + } + + QueryBuilder + 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 idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder 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 {} + +extension HighScoreQueryLinks + on QueryBuilder {} + +extension HighScoreQuerySortBy on QueryBuilder { + QueryBuilder sortByHighScoreValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'highScoreValue', Sort.asc); + }); + } + + QueryBuilder sortByHighScoreValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'highScoreValue', Sort.desc); + }); + } +} + +extension HighScoreQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByHighScoreValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'highScoreValue', Sort.asc); + }); + } + + QueryBuilder thenByHighScoreValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'highScoreValue', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } +} + +extension HighScoreQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByHighScoreValue() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'highScoreValue'); + }); + } +} + +extension HighScoreQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder highScoreValueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'highScoreValue'); + }); + } +} diff --git a/lib/models/isar/settings.dart b/lib/models/isar/settings.dart new file mode 100644 index 0000000..02739f8 --- /dev/null +++ b/lib/models/isar/settings.dart @@ -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; +} diff --git a/lib/models/isar/settings.g.dart b/lib/models/isar/settings.g.dart new file mode 100644 index 0000000..d2777c3 --- /dev/null +++ b/lib/models/isar/settings.g.dart @@ -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 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 offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + return bytesCount; +} + +void _settingsSerialize( + Settings object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeBool(offsets[0], object.isMusicEnabled); + writer.writeBool(offsets[1], object.isSoundEnabled); +} + +Settings _settingsDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = Settings( + isMusicEnabled: reader.readBool(offsets[0]), + isSoundEnabled: reader.readBool(offsets[1]), + ); + object.id = id; + return object; +} + +P _settingsDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> 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> _settingsGetLinks(Settings object) { + return []; +} + +void _settingsAttach(IsarCollection col, Id id, Settings object) { + object.id = id; +} + +extension SettingsQueryWhereSort on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension SettingsQueryWhere on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder 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 idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder 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 { + QueryBuilder idEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder 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 isMusicEnabledEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isMusicEnabled', + value: value, + )); + }); + } + + QueryBuilder isSoundEnabledEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isSoundEnabled', + value: value, + )); + }); + } +} + +extension SettingsQueryObject + on QueryBuilder {} + +extension SettingsQueryLinks + on QueryBuilder {} + +extension SettingsQuerySortBy on QueryBuilder { + QueryBuilder sortByIsMusicEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isMusicEnabled', Sort.asc); + }); + } + + QueryBuilder sortByIsMusicEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isMusicEnabled', Sort.desc); + }); + } + + QueryBuilder sortByIsSoundEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isSoundEnabled', Sort.asc); + }); + } + + QueryBuilder sortByIsSoundEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isSoundEnabled', Sort.desc); + }); + } +} + +extension SettingsQuerySortThenBy + on QueryBuilder { + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByIsMusicEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isMusicEnabled', Sort.asc); + }); + } + + QueryBuilder thenByIsMusicEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isMusicEnabled', Sort.desc); + }); + } + + QueryBuilder thenByIsSoundEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isSoundEnabled', Sort.asc); + }); + } + + QueryBuilder thenByIsSoundEnabledDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isSoundEnabled', Sort.desc); + }); + } +} + +extension SettingsQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByIsMusicEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isMusicEnabled'); + }); + } + + QueryBuilder distinctByIsSoundEnabled() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isSoundEnabled'); + }); + } +} + +extension SettingsQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder isMusicEnabledProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isMusicEnabled'); + }); + } + + QueryBuilder isSoundEnabledProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isSoundEnabled'); + }); + } +} diff --git a/lib/screens/game_over_screen.dart b/lib/screens/game_over_screen.dart new file mode 100644 index 0000000..b4b6543 --- /dev/null +++ b/lib/screens/game_over_screen.dart @@ -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( + 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'), + ) + ], + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/screens/game_screen.dart b/lib/screens/game_screen.dart new file mode 100644 index 0000000..57aa181 --- /dev/null +++ b/lib/screens/game_screen.dart @@ -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 { + 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!), + // ), + // ), + ], + ), + ); + } +} diff --git a/lib/screens/main_menu.dart b/lib/screens/main_menu.dart deleted file mode 100644 index 36bae61..0000000 --- a/lib/screens/main_menu.dart +++ /dev/null @@ -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)), - ), - ], - ), - ), - ); - } -} diff --git a/lib/screens/main_menu_screen.dart b/lib/screens/main_menu_screen.dart new file mode 100644 index 0000000..c9ef983 --- /dev/null +++ b/lib/screens/main_menu_screen.dart @@ -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 createState() => _MainMenuScreenState(); +} + +class _MainMenuScreenState extends State { + bool islevelProgression = true; + + @override + void initState() { + super.initState(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); + // _loadBackground(); + // _checkPremiumStatus(); + _loadMode(); + } + + Future _loadMode() async { + final mode = await AppStateManager.getMode(); + setState(() { + islevelProgression = mode; + }); + } + + // Future _loadBackground() async { + // final background = await MainDB.instance.getBackground(); + // setState(() { + // backgroundImage = background; + // }); + // } + + // void _updateBackground(Background newBackground) { + // setState(() { + // backgroundImage = newBackground; + // }); + // } + + // // Premium check + // Future _checkPremiumStatus() async { + // bool isPremium = await AppStateManager.isPremium(); + // setState(() { + // _isPremium = isPremium; + // }); + // } + + Future _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), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/options_screen.dart b/lib/screens/options_screen.dart new file mode 100644 index 0000000..9185126 --- /dev/null +++ b/lib/screens/options_screen.dart @@ -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 createState() => _OptionsScreenState(); +} + +class _OptionsScreenState extends State { + bool isSound = true; + bool isMusic = true; + // Background? backgroundImage; + + @override + void initState() { + super.initState(); + fetchSoundAndMusicState(); + // _loadBackground(); + getVersionNumber(); + // _checkPremiumStatus(); + } + + Future getVersionNumber() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + return packageInfo.version; + } + + // Future _loadBackground() async { + // final background = await MainDB.instance.getBackground(); + // setState(() { + // backgroundImage = background; + // }); + // } + + // Future _checkPremiumStatus() async { + // bool isPremium = await AppStateManager.isPremium(); + // setState(() { + // _isPremium = isPremium; + // }); + // } + + // Future refreshOptionsScreen() async { + // await _checkPremiumStatus(); + + // setState(() {}); + // } + + Future 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( + 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( + future: getVersionNumber(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text('Version: ${snapshot.data}'); + } else { + return Text('Version: loading...'); + } + }, + ) + ], + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/pause_screen.dart b/lib/screens/pause_screen.dart new file mode 100644 index 0000000..945e40c --- /dev/null +++ b/lib/screens/pause_screen.dart @@ -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 createState() => _PauseMenuState(); +} + +class _PauseMenuState extends State { + 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'), + )), + ], + ), + ], + ), + ); + } +} diff --git a/lib/styles/styles.dart b/lib/styles/styles.dart new file mode 100644 index 0000000..855657d --- /dev/null +++ b/lib/styles/styles.dart @@ -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); +} diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart new file mode 100644 index 0000000..e053497 --- /dev/null +++ b/lib/utilities/assets.dart @@ -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"; +} diff --git a/lib/widgets/gradient_button.dart b/lib/widgets/gradient_button.dart new file mode 100644 index 0000000..368f206 --- /dev/null +++ b/lib/widgets/gradient_button.dart @@ -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), + ), + ), + ); + } +} diff --git a/lib/widgets/icon_widgets/arrow_left_icon.dart b/lib/widgets/icon_widgets/arrow_left_icon.dart new file mode 100644 index 0000000..f3bd98c --- /dev/null +++ b/lib/widgets/icon_widgets/arrow_left_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/check_circle_icon.dart b/lib/widgets/icon_widgets/check_circle_icon.dart new file mode 100644 index 0000000..c672cc6 --- /dev/null +++ b/lib/widgets/icon_widgets/check_circle_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/check_icon.dart b/lib/widgets/icon_widgets/check_icon.dart new file mode 100644 index 0000000..c484080 --- /dev/null +++ b/lib/widgets/icon_widgets/check_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/chevron_down_icon.dart b/lib/widgets/icon_widgets/chevron_down_icon.dart new file mode 100644 index 0000000..51919e2 --- /dev/null +++ b/lib/widgets/icon_widgets/chevron_down_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/chevron_right_icon.dart b/lib/widgets/icon_widgets/chevron_right_icon.dart new file mode 100644 index 0000000..3f948f7 --- /dev/null +++ b/lib/widgets/icon_widgets/chevron_right_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/clipboard_icon.dart b/lib/widgets/icon_widgets/clipboard_icon.dart new file mode 100644 index 0000000..edec2a5 --- /dev/null +++ b/lib/widgets/icon_widgets/clipboard_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/music_icon.dart b/lib/widgets/icon_widgets/music_icon.dart new file mode 100644 index 0000000..46ef162 --- /dev/null +++ b/lib/widgets/icon_widgets/music_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/pause_circle_icon.dart b/lib/widgets/icon_widgets/pause_circle_icon.dart new file mode 100644 index 0000000..e14d24a --- /dev/null +++ b/lib/widgets/icon_widgets/pause_circle_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/star_icon.dart b/lib/widgets/icon_widgets/star_icon.dart new file mode 100644 index 0000000..a55c064 --- /dev/null +++ b/lib/widgets/icon_widgets/star_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/ticket_icon.dart b/lib/widgets/icon_widgets/ticket_icon.dart new file mode 100644 index 0000000..ecd133f --- /dev/null +++ b/lib/widgets/icon_widgets/ticket_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/icon_widgets/volume_icon.dart b/lib/widgets/icon_widgets/volume_icon.dart new file mode 100644 index 0000000..970c70b --- /dev/null +++ b/lib/widgets/icon_widgets/volume_icon.dart @@ -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, + ); + } +} diff --git a/lib/widgets/overlays/game_overlay/hint_button.dart b/lib/widgets/overlays/game_overlay/hint_button.dart new file mode 100644 index 0000000..20da7e4 --- /dev/null +++ b/lib/widgets/overlays/game_overlay/hint_button.dart @@ -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), + ), + ), + ); + } +} diff --git a/lib/widgets/overlays/game_overlay/pause_button.dart b/lib/widgets/overlays/game_overlay/pause_button.dart new file mode 100644 index 0000000..71f5428 --- /dev/null +++ b/lib/widgets/overlays/game_overlay/pause_button.dart @@ -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(), + ), + ); + } +} diff --git a/lib/widgets/overlays/game_overlay/restart_button.dart b/lib/widgets/overlays/game_overlay/restart_button.dart new file mode 100644 index 0000000..76d9ca2 --- /dev/null +++ b/lib/widgets/overlays/game_overlay/restart_button.dart @@ -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), + ), + ), + ); + } +} diff --git a/lib/widgets/radio_button.dart b/lib/widgets/radio_button.dart new file mode 100644 index 0000000..9b79ee6 --- /dev/null +++ b/lib/widgets/radio_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:match_magic/styles/styles.dart'; + +class RadioButton extends StatelessWidget { + final T value; + final T? groupValue; + final ValueChanged? onChanged; + + const RadioButton({ + required this.value, + required this.groupValue, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Radio( + value: value, + groupValue: groupValue, + onChanged: onChanged, + activeColor: AppStyles.accentColor, + visualDensity: const VisualDensity( + horizontal: VisualDensity.minimumDensity, + vertical: VisualDensity.minimumDensity, + ), + ), + ], + ); + } +} diff --git a/lib/widgets/toggle_switch.dart b/lib/widgets/toggle_switch.dart new file mode 100644 index 0000000..17547b2 --- /dev/null +++ b/lib/widgets/toggle_switch.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:match_magic/styles/styles.dart'; + +class ToggleSwitch extends StatelessWidget { + final ValueChanged? 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)), + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 1830e5c..7faaafd 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); 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); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index e9abb91..98e76e5 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + isar_flutter_libs ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a9f2f23..c272181 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,15 @@ import FlutterMacOS import Foundation import audioplayers_darwin +import isar_flutter_libs +import package_info_plus import path_provider_foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 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")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 284de48..f0f3a3d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,30 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile 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: dependency: transitive description: @@ -73,6 +97,70 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -81,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -89,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -97,6 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" crypto: dependency: transitive description: @@ -113,6 +225,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -174,6 +302,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: "direct dev" description: flutter @@ -184,6 +320,30 @@ packages: description: flutter source: sdk 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: dependency: transitive description: @@ -192,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -200,6 +368,54 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -232,6 +448,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -256,6 +480,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + url: "https://pub.dev" + source: hosted + version: "1.0.6" nested: dependency: transitive description: @@ -272,6 +504,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -280,8 +536,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" - path_provider: + path_parsing: dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_provider: + dependency: "direct main" description: name: path_provider sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 @@ -328,6 +592,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" platform: dependency: transitive description: @@ -344,6 +616,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" provider: dependency: "direct main" description: @@ -352,11 +632,107 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: flutter source: sdk 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: dependency: transitive description: @@ -389,6 +765,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -421,6 +805,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -437,6 +837,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -453,6 +877,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: @@ -461,6 +893,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -469,6 +925,30 @@ packages: url: "https://pub.dev" source: hosted 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: dart: ">=3.4.3 <4.0.0" flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index c992ecf..050314a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,7 +38,13 @@ dependencies: cupertino_icons: ^1.0.6 flame: ^1.18.0 audioplayers: ^6.1.0 + flutter_svg: ^2.0.9 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: flutter_test: @@ -50,6 +56,8 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. 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 # 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: assets: - - assets/images/crystal1.png - - assets/images/crystal2.png - - assets/images/crystal3.png - - 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 + - assets/images/ + - assets/svg/ + - assets/audio/ # An image asset can refer to one or more resolution-specific "variants", see diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 09e8e2c..ba7f138 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + IsarFlutterLibsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IsarFlutterLibsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 375535c..f310bcc 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows + isar_flutter_libs ) list(APPEND FLUTTER_FFI_PLUGIN_LIST