import 'package:flame/components.dart'; import 'package:match_three/bloc/game_bloc.dart'; import 'package:match_three/game/systems/physics_system.dart'; import '../models/grid.dart'; import '../models/gem.dart'; import '../match_three_game.dart'; import 'gem_component.dart'; import '../../utils/constants.dart'; class GridComponent extends PositionComponent with HasGameReference { late GameGrid gameGrid; final List> gemComponents = []; final MatchThreeGame game; final int levelId; bool canInterract = false; double gemSize = GameConstants.gemSize; final int gridWidth; // Default grid width final int gridHeight; // Default grid height GridComponent(this.game, this.levelId, this.gridHeight, this.gridWidth) : super(); @override Future onLoad() async { // Calculate dynamic gem size based on available screen space // For now, use a reasonable default size that will be updated when we have screen dimensions gemSize = GameConstants.calculateGemSize(gridWidth, gridHeight, 600.0, 800.0); game.gameBloc.add(StartLevel(levelId)); } void _createGemComponents() { // Clear existing components for (final row in gemComponents) { for (final gemComponent in row) { gemComponent?.removeFromParent(); } row.clear(); } gemComponents.clear(); for (final child in children) { if (child is GemComponent) { child.removeFromParent(); } } for (int row = 0; row < gridHeight; row++) { gemComponents.add([]); for (int col = 0; col < gridWidth; col++) { final gem = gameGrid.getGem(row, col); if (gem != null) { final gemComponent = GemComponent(gem: gem, gridComponent: this); gemComponent.position = Vector2( col * gemSize + GameConstants.gridPadding, row * gemSize + GameConstants.gridPadding, ); gemComponents[row].add(gemComponent); add(gemComponent); } else { gemComponents[row].add(null); } } } } void updateGrid(GameState state) async { // Work only with level-based states (including Free Play) if (state is! GameLevelPlaying) { return; } canInterract = false; print("Update event with state ${state.runtimeType.toString()}"); // Handle level-based states if (state is GameLevelSwap) { await _swapGems( state.grid, state.gem1, state.gem2, ); } if (state is GameLevelMatch) { await _animateMatch(state.grid, state.matches); } if (state is GameLevelDrop) { await _animateDrop(state.grid); } if (state is GameLevelNewGems) { await _animateNewGemsFall(state.grid, state.gems); } if (state is GameLevelIdle) { canInterract = true; } // Update grid and create components for all GameLevelPlaying states gameGrid = state.grid; gameGrid.printGrid(); _createGemComponents(); await Future.delayed(const Duration(milliseconds: 100)); print("Updated state ${state.runtimeType.toString()}"); state.done(); } GemComponent? _findGemComponent(Gem gem) { return gemComponents[gem.row][gem.col]; } _swapGems(GameGrid newGrid, Gem gem1, Gem gem2) async { final gemComp1 = _findGemComponent(gem1); final gemComp2 = _findGemComponent(gem2); if (gemComp1 == null || gemComp2 == null) { print("!! Gem not found"); return; } await PhysicsSystem.animateSwap(gemComp1, gemComp2); } _animateMatch(GameGrid newGrid, List matches, [int combo = 0]) async { final effects = []; final toRemove = []; // Animate explosion for (final gem in matches) { final gemComponent = _findGemComponent(gem); if (gemComponent != null) { toRemove.add(gemComponent); effects.add(gemComponent.animateMatch()); if (combo > 0) { gemComponent.animateCombo(); } } } await Future.wait(effects); for (final gemComponent in toRemove) { gemComponent.removeFromParent(); } } _animateDrop(GameGrid newGrid) async { final effects = []; for (int col = gridWidth - 1; col >= 0; col--) { for (int row = gridHeight - 1; row >= 0; row--) { final gem = gameGrid.getGem(row, col); // if gem is removed - animate fall effect from top if (gem == null) { int fallToRow = row; while (row >= 0) { final gemAbove = gameGrid.getGem(row, col); final gemComponent = gemComponents[row][col]; if (gemAbove != null) { final targetPosition = Vector2( col * gemSize + GameConstants.gridPadding, fallToRow * gemSize + GameConstants.gridPadding, ); print( "@@ Fall $row, $col -> $fallToRow, $col ${gemComponent == null ? "NULL" : "OK"}"); effects.add(gemComponent?.animateFall(targetPosition)); fallToRow--; } row--; } } } } await Future.wait(effects); } _animateNewGemsFall(GameGrid newGrid, List newGems) async { final effects = []; for (final gem in newGems) { // Create gem component from gem and animate falling effect to proper location final gemComponent = GemComponent(gem: gem, gridComponent: this); final targetPosition = Vector2( gem.col * gemSize + GameConstants.gridPadding, gem.row * gemSize + GameConstants.gridPadding, ); gemComponent.position.x = targetPosition.x; gemComponent.position.y = -gemSize; add(gemComponent); effects.add(gemComponent.animateFall(targetPosition)); } await Future.wait(effects); } void onGemTapped(Gem gem) { if (canInterract) { game.selectGem(gem); } } }