- Add gridWidth and gridHeight properties to level configuration - Update GameGrid to accept custom dimensions instead of using constants - Modify GridComponent to calculate gem size based on grid dimensions - Update MatchThreeGame constructor to pass grid dimensions - Ensure proper scaling and positioning for variable grid sizes
199 lines
5.9 KiB
Dart
199 lines
5.9 KiB
Dart
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<MatchThreeGame> {
|
|
late GameGrid gameGrid;
|
|
final List<List<GemComponent?>> 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<void> 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(<GemComponent?>[]);
|
|
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<Gem> matches, [int combo = 0]) async {
|
|
final effects = <Future>[];
|
|
final toRemove = <GemComponent>[];
|
|
|
|
// 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 = <Future>[];
|
|
|
|
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<Gem> newGems) async {
|
|
final effects = <Future>[];
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|