From 9ecf924a8c4f381a0bcb44842d025371b093ab53 Mon Sep 17 00:00:00 2001 From: alexvasl Date: Fri, 16 Aug 2024 19:09:24 +0300 Subject: [PATCH] Adding bombs and magic cubes. --- lib/game/board.dart | 188 ++++++++++++++++++++++++++++----- lib/game/match_magic_game.dart | 28 +++-- lib/game/tile.dart | 17 ++- lib/main.dart | 5 +- 4 files changed, 191 insertions(+), 47 deletions(-) diff --git a/lib/game/board.dart b/lib/game/board.dart index e7812e7..85d003a 100644 --- a/lib/game/board.dart +++ b/lib/game/board.dart @@ -2,9 +2,9 @@ import 'dart:math'; import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/game.dart'; +import 'package:flutter/material.dart'; import 'tile.dart'; import 'package:flame/sprite.dart'; -import 'package:provider/provider.dart'; import 'swap_notifier.dart'; class Board extends FlameGame { @@ -17,6 +17,7 @@ class Board extends FlameGame { int? selectedRow; int? selectedCol; bool animating = false; + Tile? lastMovedTile; Board({required this.sprites, required this.swapNotifier}); @@ -64,7 +65,7 @@ class Board extends FlameGame { } int _randomElement() { - return Random().nextInt(7); + return Random().nextInt(sprites.length); } void _removeInitialMatches() { @@ -97,11 +98,12 @@ class Board extends FlameGame { } else { tiles[selectedRow!][selectedCol!]?.deselect(); if (_isAdjacent(selectedRow!, selectedCol!, row, col)) { - swapTiles(tiles[selectedRow!]![selectedCol!]!, tiles[row]![col]!, true); + 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); + tiles[row][col]!, tiles[selectedRow!][selectedCol!]!, true); } selectedRow = null; selectedCol = null; @@ -138,12 +140,13 @@ class Board extends FlameGame { if (targetTile != null) { animating = true; + lastMovedTile = tile; swapTiles(tile, targetTile, true); await Future.delayed(const Duration(milliseconds: 300)); if (!checkMatches()) { - swapTiles(tile, targetTile!, true); + swapTiles(tile, targetTile, true); } animating = false; @@ -213,59 +216,89 @@ class Board extends FlameGame { } bool _hasMatch(int row, int col) { - final value = tiles[row]?[col]?.spriteIndex; + 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; } int _removeMatchedElements(int row, int col) { int score = 0; - final value = tiles[row]?[col]?.spriteIndex; + final int? value = tiles[row][col]?.spriteIndex; + bool transformToBomb = false; 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) { int matchLength = right - left + 1; score += _calculateScore(matchLength); + + if (matchLength == 4 && + lastMovedTile != null && + lastMovedTile!.row == row && + lastMovedTile!.col == col) { + transformToBomb = true; + } + for (int i = left; i <= right; i++) { - if (tiles[row]?[i] != null) { - _animateRemoveTile(tiles[row]![i]!); - tiles[row]![i] = null; + if (tiles[row][i] != null && + (!transformToBomb || tiles[row][i] != lastMovedTile)) { + _animateRemoveTile(tiles[row][i]!); + tiles[row][i] = null; } } } 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) { int matchLength = bottom - top + 1; score += _calculateScore(matchLength); + + if (matchLength == 4 && + lastMovedTile != null && + lastMovedTile!.row == row && + lastMovedTile!.col == col) { + transformToBomb = true; + } + for (int i = top; i <= bottom; i++) { - if (tiles[i]?[col] != null) { - _animateRemoveTile(tiles[i]![col]!); - tiles[i]![col] = null; + if (tiles[i][col] != null && + (!transformToBomb || tiles[i][col] != lastMovedTile)) { + _animateRemoveTile(tiles[i][col]!); + tiles[i][col] = null; } } } + if (transformToBomb && lastMovedTile != null) { + _createBomb(lastMovedTile!.row, lastMovedTile!.col); + } else { + if (tiles[row][col] != null) { + _animateRemoveTile(tiles[row][col]!); + tiles[row][col] = null; + } + } + return score; } @@ -274,6 +307,8 @@ class Board extends FlameGame { return 50; } else if (matchLength == 4) { return 100; + } else if (matchLength == 5) { + return 200; } return 0; } @@ -284,16 +319,113 @@ class Board extends FlameGame { }); } + void _createBomb(int row, int col) { + final tile = tiles[row][col]!; + tile.isBomb = true; + + tile.add( + OpacityEffect.to( + 0.5, + EffectController( + duration: 0.5, + infinite: true, + reverseDuration: 0.5, + ), + ), + ); + + tile.add( + ColorEffect( + Colors.orange, + EffectController( + duration: 0.5, + infinite: true, + reverseDuration: 0.5, + ), + opacityFrom: 0.5, + opacityTo: 1.0, + ), + ); + } + + void _createMagicCube(int row, int col) { + final tile = tiles[row][col]!; + tile.isMagicCube = true; + + tile.add( + OpacityEffect.to( + 0.5, + EffectController( + duration: 0.5, + infinite: true, + reverseDuration: 0.5, + ), + ), + ); + + tile.add( + ColorEffect( + Colors.purple, + EffectController( + duration: 0.5, + infinite: true, + reverseDuration: 0.5, + ), + opacityFrom: 0.5, + opacityTo: 1.0, + ), + ); + } + + 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) { + 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( + 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; } @@ -306,7 +438,7 @@ class Board extends FlameGame { void _fillEmptySpaces() { for (int col = 0; col < cols; col++) { for (int row = rows - 1; row >= 0; row--) { - if (tiles[row]?[col] == null) { + if (tiles[row][col] == null) { int spriteIndex = _randomElement(); var tile = Tile( sprite: sprites[spriteIndex], diff --git a/lib/game/match_magic_game.dart b/lib/game/match_magic_game.dart index 1808f47..6b531f7 100644 --- a/lib/game/match_magic_game.dart +++ b/lib/game/match_magic_game.dart @@ -2,7 +2,6 @@ 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/main.dart'; import 'package:provider/provider.dart'; import 'board.dart'; import 'swap_notifier.dart'; @@ -48,6 +47,9 @@ class _MatchMagicGameScreenState extends State { sprites: sprites, swapNotifier: context.read()); return Column( children: [ + SizedBox( + height: 50, + ), const ScoreDisplay(), Expanded( child: Center( @@ -59,8 +61,14 @@ class _MatchMagicGameScreenState extends State { ), ElevatedButton( onPressed: _restartGame, - child: const Text('Restart'), + child: const Text( + 'Restart', + style: TextStyle(color: Colors.black), + ), ), + SizedBox( + height: 50, + ) ], ); } @@ -72,19 +80,6 @@ class _MatchMagicGameScreenState extends State { } } -class MatchMagicGame extends StatelessWidget { - final List sprites; - - const MatchMagicGame({required this.sprites, Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return GameWidget( - game: Board(sprites: sprites, swapNotifier: context.read()), - ); - } -} - class ScoreDisplay extends StatelessWidget { const ScoreDisplay({Key? key}) : super(key: key); @@ -96,7 +91,8 @@ class ScoreDisplay extends StatelessWidget { builder: (context, notifier, child) { return Text( 'Score: ${notifier.score}', - style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white), ); }, ), diff --git a/lib/game/tile.dart b/lib/game/tile.dart index 89de470..27b5b99 100644 --- a/lib/game/tile.dart +++ b/lib/game/tile.dart @@ -2,6 +2,7 @@ import 'package:flame/components.dart'; import 'package:flame/effects.dart'; import 'package:flame/events.dart'; import 'package:flutter/material.dart'; +import 'board.dart'; class Tile extends SpriteComponent with TapCallbacks, DragCallbacks { int row; @@ -10,6 +11,8 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks { final void Function(Tile) onTileTap; final void Function(Tile, Vector2) onSwipe; bool isSelected = false; + bool isBomb = false; + bool isMagicCube = false; Vector2? startDragPosition; Tile({ @@ -25,18 +28,30 @@ class Tile extends SpriteComponent with TapCallbacks, DragCallbacks { @override bool onTapDown(TapDownEvent event) { - onTileTap(this); + if (parent is Board && (parent as Board).animating) { + return false; + } + if (isBomb) { + (parent as Board).explodeBomb(this); + } else { + onTileTap(this); + } return true; } @override bool onDragStart(DragStartEvent event) { + super.onDragStart(event); + if (parent is Board && (parent as Board).animating) { + return false; + } startDragPosition = event.localPosition; return true; } @override bool onDragEnd(DragEndEvent event) { + super.onDragEnd(event); if (startDragPosition != null) { final delta = event.velocity; onSwipe(this, delta); diff --git a/lib/main.dart b/lib/main.dart index c727e92..a3ec77c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ -import 'package:flame/sprite.dart'; import 'package:flutter/material.dart'; -import 'package:match_magic/game/sprite_loader.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'game/match_magic_game.dart'; import 'game/swap_notifier.dart'; @@ -14,6 +13,7 @@ void main() { child: const MyApp(), ), ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } class MyApp extends StatelessWidget { @@ -27,6 +27,7 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, ), home: const Scaffold( + backgroundColor: Colors.black, body: MatchMagicGameScreen(), ), );