-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: initial crossword structure * rebase fixes * feat: Initial Crossword board * rebase fixes * tests * fix lint
- Loading branch information
1 parent
65d05d9
commit fafc849
Showing
17 changed files
with
978 additions
and
24 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export 'section_component.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flame/components.dart'; | ||
import 'package:flame/sprite.dart'; | ||
import 'package:flutter/material.dart' hide Axis; | ||
import 'package:game_domain/game_domain.dart'; | ||
import 'package:io_crossword/crossword/crossword.dart'; | ||
|
||
class SectionComponent extends PositionComponent | ||
with HasGameRef<CrosswordGame> { | ||
SectionComponent({ | ||
required this.index, | ||
super.key, | ||
}); | ||
|
||
final (int, int) index; | ||
|
||
late StreamSubscription<CrosswordState> _subscription; | ||
|
||
@override | ||
FutureOr<void> onLoad() async { | ||
await super.onLoad(); | ||
|
||
final boardSection = gameRef.state.sections[index]; | ||
if (boardSection != null) { | ||
_loadBoardSection(boardSection); | ||
} else { | ||
_subscription = gameRef.bloc.stream.listen((state) { | ||
if (state is CrosswordLoaded) { | ||
final boardSection = state.sections[index]; | ||
if (boardSection != null) { | ||
_subscription.cancel(); | ||
_loadBoardSection(boardSection); | ||
} | ||
} | ||
}); | ||
|
||
gameRef.bloc.add( | ||
BoardSectionRequested(index), | ||
); | ||
} | ||
} | ||
|
||
void _loadBoardSection(BoardSection section) { | ||
final spriteBatch = SpriteBatch(gameRef.lettersSprite); | ||
|
||
final sectionPosition = Vector2( | ||
(index.$1 * gameRef.sectionSize).toDouble(), | ||
(index.$2 * gameRef.sectionSize).toDouble(), | ||
); | ||
|
||
for (var i = 0; i < section.words.length; i++) { | ||
final word = section.words[i]; | ||
|
||
final wordCharacters = word.answer.toUpperCase().characters; | ||
|
||
for (var c = 0; c < wordCharacters.length; c++) { | ||
final char = wordCharacters.elementAt(c); | ||
final charIndex = char.codeUnitAt(0) - 65; | ||
final rect = Rect.fromLTWH( | ||
(charIndex * CrosswordGame.cellSize).toDouble(), | ||
0, | ||
CrosswordGame.cellSize.toDouble(), | ||
CrosswordGame.cellSize.toDouble(), | ||
); | ||
|
||
final x = word.axis == Axis.horizontal | ||
? word.position.x + c | ||
: word.position.x; | ||
|
||
final y = | ||
word.axis == Axis.vertical ? word.position.y + c : word.position.y; | ||
final offset = sectionPosition + | ||
Vector2( | ||
x * CrosswordGame.cellSize.toDouble(), | ||
y * CrosswordGame.cellSize.toDouble(), | ||
); | ||
|
||
spriteBatch.add( | ||
source: rect, | ||
offset: offset, | ||
); | ||
} | ||
} | ||
|
||
add(SpriteBatchComponent(spriteBatch: spriteBatch)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,140 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flame/camera.dart'; | ||
import 'package:flame/components.dart'; | ||
import 'package:flame/events.dart'; | ||
import 'package:flame/extensions.dart'; | ||
import 'package:flame/game.dart'; | ||
import 'package:io_crossword/crossword/crossword.dart'; | ||
|
||
class CrosswordGame extends FlameGame with PanDetector { | ||
CrosswordGame(this.bloc); | ||
|
||
static const cellSize = 40; | ||
|
||
final CrosswordBloc bloc; | ||
|
||
late final Size totalArea; | ||
late final int sectionSize; | ||
|
||
late final Image lettersSprite; | ||
|
||
var _visibleSections = <(double, double)>[]; | ||
|
||
CrosswordLoaded get state { | ||
final state = bloc.state; | ||
if (state is! CrosswordLoaded) { | ||
throw ArgumentError('Cannot load game without a loaded state.'); | ||
} | ||
return state; | ||
} | ||
|
||
@override | ||
FutureOr<void> onLoad() async { | ||
await super.onLoad(); | ||
|
||
// TODO(erickzanardo): Use the assets cubit instead | ||
lettersSprite = await images.load('letters.png'); | ||
|
||
sectionSize = state.sectionSize; | ||
|
||
totalArea = Size( | ||
(state.width * cellSize).toDouble(), | ||
(state.height * cellSize).toDouble(), | ||
); | ||
|
||
camera | ||
..priority = 1 | ||
..viewport = (MaxViewport()..anchor = Anchor.topLeft) | ||
..viewfinder.position = Vector2( | ||
totalArea.width / 2, | ||
totalArea.height / 2, | ||
); | ||
|
||
_updateVisibleSections(); | ||
} | ||
|
||
void _updateVisibleSections() { | ||
final visibleViewport = camera.visibleWorldRect; | ||
|
||
final viewportMiddle = visibleViewport.size / 2; | ||
|
||
final horizontalSectionsVisibleInViewport = | ||
(visibleViewport.width / sectionSize).ceilToDouble(); | ||
final verticalSectionsVisibleInViewport = | ||
(visibleViewport.height / sectionSize).ceilToDouble(); | ||
|
||
final cameraPosition = | ||
camera.viewfinder.position + viewportMiddle.toVector2(); | ||
|
||
final startSection = Vector2( | ||
((cameraPosition.x - viewportMiddle.width) ~/ sectionSize).toDouble(), | ||
((cameraPosition.y - viewportMiddle.height) ~/ sectionSize) | ||
.toDouble(), | ||
) - | ||
Vector2( | ||
horizontalSectionsVisibleInViewport, | ||
verticalSectionsVisibleInViewport, | ||
); | ||
|
||
final endSection = Vector2( | ||
((cameraPosition.x + viewportMiddle.width) ~/ sectionSize).toDouble(), | ||
((cameraPosition.y + viewportMiddle.height) ~/ sectionSize) | ||
.toDouble(), | ||
) + | ||
Vector2( | ||
horizontalSectionsVisibleInViewport, | ||
verticalSectionsVisibleInViewport, | ||
); | ||
|
||
final currentVisibleSections = <(double, double)>[]; | ||
for (var x = startSection.x; x <= endSection.x; x++) { | ||
for (var y = startSection.y; y <= endSection.y; y++) { | ||
final sectionIndex = (x, y); | ||
currentVisibleSections.add(sectionIndex); | ||
} | ||
} | ||
|
||
final sectionsToRemove = | ||
_visibleSections.toSet().difference(currentVisibleSections.toSet()); | ||
final sectionsToAdd = | ||
currentVisibleSections.toSet().difference(_visibleSections.toSet()); | ||
|
||
if (sectionsToRemove.isNotEmpty) { | ||
for (final sectionIndex in sectionsToRemove) { | ||
findByKeyName('section-$sectionIndex')?.removeFromParent(); | ||
} | ||
} | ||
|
||
if (sectionsToAdd.isNotEmpty) { | ||
for (final sectionIndex in sectionsToAdd) { | ||
world.add( | ||
SectionComponent( | ||
key: ComponentKey.named('section-$sectionIndex'), | ||
index: ( | ||
sectionIndex.$1.toInt(), | ||
sectionIndex.$2.toInt(), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
_visibleSections = currentVisibleSections; | ||
} | ||
|
||
var _distanceMoved = 0.0; | ||
|
||
@override | ||
void onPanUpdate(DragUpdateInfo info) { | ||
super.onPanUpdate(info); | ||
|
||
_distanceMoved += info.delta.global.length; | ||
camera.viewfinder.position -= info.delta.global; | ||
|
||
class CrosswordGame extends FlameGame {} | ||
if (_distanceMoved >= 280) { | ||
_distanceMoved = 0; | ||
_updateVisibleSections(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export 'components/components.dart'; | ||
export 'crossword_game.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.