Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • android/org.benoitharrault.jeweled
1 result
Select Git revision
Show changes
Showing
with 1287 additions and 358 deletions
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:jeweled/models/settings_global.dart';
import 'package:jeweled/utils/tools.dart';
part 'settings_global_state.dart';
class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault()));
void setValues({
int? colorsTheme,
int? graphicTheme,
}) {
emit(
GlobalSettingsState(
settings: GlobalSettings(
colorsTheme: colorsTheme ?? state.settings.colorsTheme,
graphicTheme: graphicTheme ?? state.settings.graphicTheme,
),
),
);
}
int getParameterValue(String code) {
switch (code) {
case 'colorsTheme':
return GlobalSettings.getColorsThemeValueFromUnsafe(state.settings.colorsTheme);
case 'graphicTheme':
return GlobalSettings.getGraphicThemeValueFromUnsafe(state.settings.graphicTheme);
}
return 0;
}
void setParameterValue(String code, int value) {
printlog('GlobalSettingsCubit.setParameterValue');
printlog('code: $code / value: $value');
int colorsTheme = code == 'colorsTheme' ? value : getParameterValue('colorsTheme');
int graphicTheme = code == 'graphicTheme' ? value : getParameterValue('graphicTheme');
setValues(
colorsTheme: colorsTheme,
graphicTheme: graphicTheme,
);
}
@override
GlobalSettingsState? fromJson(Map<String, dynamic> json) {
int colorsTheme = json['colorsTheme'] as int;
int graphicTheme = json['graphicTheme'] as int;
return GlobalSettingsState(
settings: GlobalSettings(
colorsTheme: colorsTheme,
graphicTheme: graphicTheme,
),
);
}
@override
Map<String, dynamic>? toJson(GlobalSettingsState state) {
return <String, dynamic>{
'colorsTheme': state.settings.colorsTheme,
'graphicTheme': state.settings.graphicTheme,
};
}
}
part of 'settings_global_cubit.dart';
@immutable
class GlobalSettingsState extends Equatable {
const GlobalSettingsState({
required this.settings,
});
final GlobalSettings settings;
@override
List<dynamic> get props => <dynamic>[
settings,
];
Map<String, dynamic> get values => <String, dynamic>{
'settings': settings,
};
}
......@@ -8,8 +8,9 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:jeweled/config/theme.dart';
import 'package:jeweled/cubit/settings_cubit.dart';
import 'package:jeweled/cubit/game_cubit.dart';
import 'package:jeweled/cubit/settings_game_cubit.dart';
import 'package:jeweled/cubit/settings_global_cubit.dart';
import 'package:jeweled/ui/skeleton.dart';
void main() async {
......@@ -44,7 +45,8 @@ class MyApp extends StatelessWidget {
return MultiBlocProvider(
providers: [
BlocProvider<GameCubit>(create: (context) => GameCubit()),
BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()),
],
child: MaterialApp(
title: 'Jeweled',
......
......@@ -8,6 +8,11 @@ class CellLocation {
});
factory CellLocation.go(int row, int col) {
return new CellLocation(col: col, row: row);
return CellLocation(col: col, row: row);
}
@override
String toString() {
return 'CellLocation(col: $col, row: $row)';
}
}
import 'dart:math';
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/models/game_board.dart';
import 'package:jeweled/models/game_cell.dart';
import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/models/game_settings.dart';
import 'package:jeweled/models/settings_game.dart';
import 'package:jeweled/models/settings_global.dart';
import 'package:jeweled/utils/color_theme.dart';
import 'package:jeweled/utils/tools.dart';
class Game {
final GameBoard board;
final GameSettings settings;
final GameSettings gameSettings;
final GlobalSettings globalSettings;
List<int> shuffledColors = [];
bool isRunning = false;
bool isFinished = false;
int availableBlocksCount = 0;
......@@ -16,7 +22,9 @@ class Game {
Game({
required this.board,
required this.settings,
required this.gameSettings,
required this.globalSettings,
required this.shuffledColors,
this.isRunning = false,
this.isFinished = false,
this.availableBlocksCount = 0,
......@@ -27,63 +35,88 @@ class Game {
factory Game.createNull() {
return Game(
board: GameBoard.createNull(),
settings: GameSettings.createDefault(),
gameSettings: GameSettings.createDefault(),
globalSettings: GlobalSettings.createDefault(),
shuffledColors: shuffleColors(DefaultGlobalSettings.defaultColorsThemeValue),
);
}
factory Game.createNew({GameSettings? gameSettings}) {
GameSettings settings = gameSettings ?? GameSettings.createDefault();
factory Game.createNew({
GameSettings? gameSettings,
GlobalSettings? globalSettings,
}) {
GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
return Game(
board: GameBoard.createRandom(settings),
settings: settings,
board: GameBoard.createRandom(newGameSettings),
gameSettings: newGameSettings,
globalSettings: newGlobalSettings,
shuffledColors: shuffleColors(newGlobalSettings.colorsTheme),
isRunning: true,
);
}
static List<int> shuffleColors(final int colorsTheme) {
List<int> values =
List<int>.generate(ColorTheme.getColorsCount(colorsTheme), (i) => i + 1);
values.shuffle();
return values;
}
void shuffleColorsAgain(final int colorsTheme) {
shuffledColors = shuffleColors(colorsTheme);
}
GameCell getCell(CellLocation cellLocation) {
return this.board.cells[cellLocation.row][cellLocation.col];
return board.cells[cellLocation.row][cellLocation.col];
}
int? getCellValue(CellLocation cellLocation) {
return this.getCell(cellLocation).value;
return getCell(cellLocation).value;
}
int? getCellValueShuffled(CellLocation cellLocation) {
final int? value = getCell(cellLocation).value;
return value != null ? shuffledColors[value - 1] : null;
}
void updateCellValue(CellLocation locationToUpdate, int? value) {
this.board.cells[locationToUpdate.row][locationToUpdate.col].value = value;
board.cells[locationToUpdate.row][locationToUpdate.col].value = value;
}
void increaseMovesCount() {
this.movesCount += 1;
movesCount += 1;
}
void increaseScore(int? count) {
this.score += (count ?? 0);
score += (count ?? 0);
}
void updateAvailableBlocksCount() {
this.availableBlocksCount = this.getAvailableBlocks(this).length;
availableBlocksCount = getAvailableBlocks(this).length;
}
void updateGameIsRunning(bool gameIsRunning) {
this.isRunning = gameIsRunning;
isRunning = gameIsRunning;
}
void updateGameIsFinished(bool gameIsFinished) {
this.isFinished = gameIsFinished;
isFinished = gameIsFinished;
}
List<CellLocation> getSiblingCells(
final CellLocation referenceCellLocation,
List<CellLocation> siblingCells,
) {
final int boardSizeHorizontal = this.settings.boardSize;
final int boardSizeVertical = this.settings.boardSize;
final int boardSizeHorizontal = gameSettings.boardSize;
final int boardSizeVertical = gameSettings.boardSize;
final int? referenceValue = this.getCellValue(referenceCellLocation);
final int? referenceValue = getCellValue(referenceCellLocation);
for (var deltaRow = -1; deltaRow <= 1; deltaRow++) {
for (var deltaCol = -1; deltaCol <= 1; deltaCol++) {
for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
if (deltaCol == 0 || deltaRow == 0) {
final int candidateRow = referenceCellLocation.row + deltaRow;
final int candidateCol = referenceCellLocation.col + deltaCol;
......@@ -92,9 +125,9 @@ class Game {
(candidateCol >= 0 && candidateCol < boardSizeHorizontal)) {
final candidateLocation = CellLocation.go(candidateRow, candidateCol);
if (this.getCellValue(candidateLocation) == referenceValue) {
if (getCellValue(candidateLocation) == referenceValue) {
bool alreadyFound = false;
for (var index = 0; index < siblingCells.length; index++) {
for (int index = 0; index < siblingCells.length; index++) {
if ((siblingCells[index].row == candidateRow) &&
(siblingCells[index].col == candidateCol)) {
alreadyFound = true;
......@@ -114,25 +147,25 @@ class Game {
}
List<List<CellLocation>> getAvailableBlocks(final Game game) {
final int boardSizeHorizontal = game.settings.boardSize;
final int boardSizeVertical = game.settings.boardSize;
final int boardSizeHorizontal = game.gameSettings.boardSize;
final int boardSizeVertical = game.gameSettings.boardSize;
final List<List<CellLocation>> blocks = [];
for (var row = 0; row < boardSizeVertical; row++) {
for (var col = 0; col < boardSizeHorizontal; col++) {
for (int row = 0; row < boardSizeVertical; row++) {
for (int col = 0; col < boardSizeHorizontal; col++) {
final CellLocation cellLocation = CellLocation.go(row, col);
if (game.getCellValue(cellLocation) != null) {
// if current cell not already in a found block
bool alreadyFound = false;
blocks.forEach((List<CellLocation> foundBlock) {
foundBlock.forEach((CellLocation foundBlockCell) {
for (List<CellLocation> foundBlock in blocks) {
for (CellLocation foundBlockCell in foundBlock) {
if ((foundBlockCell.row == row) && (foundBlockCell.col == col)) {
alreadyFound = true;
}
});
});
}
}
if (!alreadyFound) {
final List<CellLocation> block = game.getSiblingCells(cellLocation, []);
if (block.length >= 3) {
......@@ -147,14 +180,14 @@ class Game {
}
bool hasAtLeastOneAvailableBlock() {
final int boardSizeHorizontal = this.settings.boardSize;
final int boardSizeVertical = this.settings.boardSize;
final int boardSizeHorizontal = gameSettings.boardSize;
final int boardSizeVertical = gameSettings.boardSize;
for (var row = 0; row < boardSizeVertical; row++) {
for (var col = 0; col < boardSizeHorizontal; col++) {
for (int row = 0; row < boardSizeVertical; row++) {
for (int col = 0; col < boardSizeHorizontal; col++) {
final CellLocation cellLocation = CellLocation.go(row, col);
if (this.getCellValue(cellLocation) != null) {
final List<CellLocation> block = this.getSiblingCells(cellLocation, []);
if (getCellValue(cellLocation) != null) {
final List<CellLocation> block = getSiblingCells(cellLocation, []);
if (block.length >= 3) {
// found one block => ok, not locked
return true;
......@@ -163,15 +196,15 @@ class Game {
}
}
print('Board is locked!');
printlog('Board is locked!');
return false;
}
bool isInBoard(CellLocation cell) {
if (cell.row > 0 &&
cell.row < this.settings.boardSize &&
cell.row < gameSettings.boardSize &&
cell.col > 0 &&
cell.col < this.settings.boardSize) {
cell.col < gameSettings.boardSize) {
return true;
}
return false;
......@@ -184,30 +217,35 @@ class Game {
// build a list of values to pick one
final List<int> values = [];
// All eligible values
final int maxValue = this.settings.colorsCount;
// All eligible values (twice)
final int maxValue = gameSettings.colorsCount;
for (int i = 1; i <= maxValue; i++) {
values.add(i);
values.add(i);
}
// Add values of current col
for (int r = 0; r <= this.settings.boardSize; r++) {
if (this.isInBoard(CellLocation.go(r, col))) {
final int? value = this.getCellValue(CellLocation.go(r, col));
// Add values of current col (twice)
for (int r = 0; r <= gameSettings.boardSize; r++) {
if (isInBoard(CellLocation.go(r, col))) {
final int? value = getCellValue(CellLocation.go(r, col));
if (value != null) {
values.add(value);
values.add(value);
}
}
}
// Add values of sibling cols
// Add values of sibling cols (twice for top rows)
for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
final int c = col + deltaCol;
for (int r = 0; r < this.settings.boardSize; r++) {
if (this.isInBoard(CellLocation.go(r, c))) {
final int? value = this.getCellValue(CellLocation.go(r, c));
for (int r = 0; r < gameSettings.boardSize; r++) {
if (isInBoard(CellLocation.go(r, c))) {
final int? value = getCellValue(CellLocation.go(r, c));
if (value != null) {
values.add(value);
if (row < gameSettings.boardSize / 3) {
values.add(value);
}
}
}
}
......@@ -218,8 +256,8 @@ class Game {
final int c = col + deltaCol;
for (int deltaRow = -2; deltaRow <= 2; deltaRow++) {
final int r = row + deltaRow;
if (this.isInBoard(CellLocation.go(r, c))) {
final int? value = this.getCellValue(CellLocation.go(r, c));
if (isInBoard(CellLocation.go(r, c))) {
final int? value = getCellValue(CellLocation.go(r, c));
if (value != null) {
values.add(value);
}
......@@ -232,35 +270,40 @@ class Game {
}
void dump() {
print('');
print('## Current game dump:');
print('');
this.settings.dump();
print('');
this.board.dump();
print('');
print('Game: ');
print(' isRunning: ' + isRunning.toString());
print(' isFinished: ' + isFinished.toString());
print(' movesCount: ' + movesCount.toString());
print(' score: ' + score.toString());
print(' availableBlocksCount: ' + availableBlocksCount.toString());
print('');
}
printlog('');
printlog('## Current game dump:');
printlog('');
gameSettings.dump();
globalSettings.dump();
printlog('');
board.dump();
printlog('');
printlog('Game: ');
printlog(' isRunning: $isRunning');
printlog(' isFinished: $isFinished');
printlog(' movesCount: $movesCount');
printlog(' score: $score');
printlog(' availableBlocksCount: $availableBlocksCount');
printlog(' shuffledColors: $shuffledColors');
printlog('');
}
@override
String toString() {
return 'Game(' + this.toJson().toString() + ')';
return 'Game(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'board': this.board.toJson(),
'settings': this.settings.toJson(),
'isRunning': this.isRunning,
'isFinished': this.isFinished,
'availableBlocksCount': this.availableBlocksCount,
'movesCount': this.movesCount,
'score': this.score,
'board': board.toJson(),
'gameSettings': gameSettings.toJson(),
'globalSettings': globalSettings.toJson(),
'shuffledColors': shuffledColors,
'isRunning': isRunning,
'isFinished': isFinished,
'availableBlocksCount': availableBlocksCount,
'movesCount': movesCount,
'score': score,
};
}
}
import 'dart:math';
import 'package:jeweled/models/game_cell.dart';
import 'package:jeweled/models/game_settings.dart';
import 'package:jeweled/models/settings_game.dart';
import 'package:jeweled/utils/tools.dart';
class GameBoard {
final List<List<GameCell>> cells;
......@@ -19,12 +20,12 @@ class GameBoard {
final int boardSizeVertical = gameSettings.boardSize;
final int maxValue = gameSettings.colorsCount;
final rand = new Random();
final rand = Random();
List<List<GameCell>> cells = [];
for (var rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
for (int rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) {
List<GameCell> row = [];
for (var colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
for (int colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) {
int value = 1 + rand.nextInt(maxValue);
row.add(GameCell(value));
}
......@@ -38,33 +39,34 @@ class GameBoard {
void dump() {
String horizontalRule = '----';
cells[0].forEach((element) {
for (int i = 0; i < cells[0].length; i++) {
horizontalRule += '-';
});
}
print('Board:');
print(horizontalRule);
printlog('Board:');
printlog(horizontalRule);
for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
String row = '| ';
for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row += cells[rowIndex][colIndex].value.toString();
}
row += ' |';
print(row);
printlog(row);
}
print(horizontalRule);
printlog(horizontalRule);
}
@override
String toString() {
return 'Board(' + this.toJson().toString() + ')';
return 'Board(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'cells': this.cells.toString(),
'cells': cells.toString(),
};
}
}
......@@ -5,13 +5,14 @@ class GameCell {
this.value,
);
@override
String toString() {
return 'Cell(' + this.toJson().toString() + ')';
return 'Cell(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'value': this.value,
'value': value,
};
}
}
import 'package:jeweled/config/default_game_settings.dart';
import 'package:jeweled/utils/tools.dart';
class GameSettings {
final int boardSize;
......@@ -33,19 +34,20 @@ class GameSettings {
}
void dump() {
print('Settings: ');
print(' boardSize: ' + boardSize.toString());
print(' colorsCount: ' + colorsCount.toString());
printlog('Settings: ');
printlog(' boardSize: $boardSize');
printlog(' colorsCount: $colorsCount');
}
@override
String toString() {
return 'GameSettings(' + this.toJson().toString() + ')';
return 'GameSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'boardSize': this.boardSize,
'colorsCount': this.colorsCount,
'boardSize': boardSize,
'colorsCount': colorsCount,
};
}
}
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/utils/tools.dart';
class GlobalSettings {
final int colorsTheme;
final int graphicTheme;
GlobalSettings({
required this.colorsTheme,
required this.graphicTheme,
});
static int getColorsThemeValueFromUnsafe(int colorsTheme) {
if (DefaultGlobalSettings.allowedColorsThemeValues.contains(colorsTheme)) {
return colorsTheme;
}
return DefaultGlobalSettings.defaultColorsThemeValue;
}
static int getGraphicThemeValueFromUnsafe(int graphicTheme) {
if (DefaultGlobalSettings.allowedGraphicThemeValues.contains(graphicTheme)) {
return graphicTheme;
}
return DefaultGlobalSettings.defaultGraphicThemeValue;
}
factory GlobalSettings.createDefault() {
return GlobalSettings(
colorsTheme: DefaultGlobalSettings.defaultColorsThemeValue,
graphicTheme: DefaultGlobalSettings.defaultGraphicThemeValue,
);
}
void dump() {
printlog('Settings: ');
printlog(' colorsTheme: $colorsTheme');
printlog(' graphicTheme: $graphicTheme');
}
@override
String toString() {
return 'GlobalSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'colorsTheme': colorsTheme,
'graphicTheme': graphicTheme,
};
}
}
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:jeweled/models/game_cell.dart';
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/models/game.dart';
import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/utils/color_extensions.dart';
import 'package:jeweled/utils/color_theme.dart';
class GameBoardPainter extends CustomPainter {
const GameBoardPainter(this.currentGame);
const GameBoardPainter({
required this.game,
required this.animations,
});
final Game currentGame;
static const drawTextValue = false;
final Game game;
final List<List<Animation<double>?>> animations;
@override
void paint(Canvas canvas, Size size) {
final int sizeHorizontal = currentGame.settings.boardSize;
final int sizeVertical = currentGame.settings.boardSize;
int graphicTheme = game.globalSettings.graphicTheme;
final List cells = currentGame.board.cells;
final double cellSize = size.width / max(sizeHorizontal, sizeVertical);
final double canvasSize = min(size.width, size.height);
// background
for (var row = 0; row < sizeVertical; row++) {
final double y = cellSize * row;
for (var col = 0; col < sizeHorizontal; col++) {
final double x = cellSize * col;
const double cellBorderWidth = 2;
const double boardBorderWidth = 3;
final GameCell cell = cells[row][col];
final int colorCode = ColorTheme.getColorCode(cell.value);
switch (graphicTheme) {
case DefaultGlobalSettings.graphicThemeSolidBackground:
drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
);
break;
case DefaultGlobalSettings.graphicThemeGradientAndBorder:
drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
borderWidth: cellBorderWidth,
gradientFrom: -10,
gradientTo: 10,
);
break;
case DefaultGlobalSettings.graphicThemeEmojis:
drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
contentStrings: DefaultGlobalSettings.graphicThemeContentEmojiStrings,
);
break;
case DefaultGlobalSettings.graphicThemePatterns:
drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
contentStrings: DefaultGlobalSettings.graphicThemeContentPatternStrings,
);
break;
final cellPaintBackground = Paint();
cellPaintBackground.color = Color(colorCode);
cellPaintBackground.style = PaintingStyle.fill;
default:
}
final Rect cellBackground =
Rect.fromPoints(Offset(x - 1, y - 1), Offset(x + cellSize + 2, y + cellSize + 2));
// board borders
final boardPaintBorder = Paint();
boardPaintBorder.color = ColorTheme.getBorderColor();
boardPaintBorder.strokeWidth = boardBorderWidth;
boardPaintBorder.strokeCap = StrokeCap.round;
const Offset boardTopLeft = Offset(0, 0);
final Offset boardTopRight = Offset(canvasSize, 0);
final Offset boardBottomLeft = Offset(0, canvasSize);
final Offset boardBottomRight = Offset(canvasSize, canvasSize);
canvas.drawLine(boardTopLeft, boardTopRight, boardPaintBorder);
canvas.drawLine(boardTopRight, boardBottomRight, boardPaintBorder);
canvas.drawLine(boardBottomRight, boardBottomLeft, boardPaintBorder);
canvas.drawLine(boardBottomLeft, boardTopLeft, boardPaintBorder);
}
canvas.drawRect(cellBackground, cellPaintBackground);
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
// draw value on cell
if (drawTextValue) {
final textPainter = TextPainter(
text: TextSpan(
text: cell.value.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 15,
),
),
textDirection: TextDirection.ltr,
)..layout(
minWidth: 0,
maxWidth: size.width,
void drawBoard({
required Canvas canvas,
required double canvasSize,
required Game game,
double overlapping = 1,
int gradientFrom = 0,
int gradientTo = 0,
double borderWidth = 0,
List<String>? contentStrings,
}) {
final int cellsCountHorizontal = game.gameSettings.boardSize;
final int cellsCountVertical = game.gameSettings.boardSize;
final int colorsTheme = game.globalSettings.colorsTheme;
final double size = canvasSize / max(cellsCountHorizontal, cellsCountVertical);
for (int row = 0; row < cellsCountVertical; row++) {
final double yOrigin = size * row;
for (int col = 0; col < cellsCountHorizontal; col++) {
final double x = size * col;
final CellLocation cellLocation = CellLocation.go(row, col);
final int? cellValue = game.getCellValueShuffled(cellLocation);
if (cellValue != null) {
final Animation<double>? translation = animations[row][col];
final double y = yOrigin + (translation?.value ?? 0) * size;
drawCell(
canvas: canvas,
x: x,
y: y,
cellSize: size,
cellValue: cellValue,
colorsTheme: colorsTheme,
overlapping: overlapping,
gradientFrom: gradientFrom,
gradientTo: gradientTo,
borderWidth: borderWidth,
contentStrings: contentStrings,
);
textPainter.paint(canvas, Offset(x + 4, y + 2));
}
}
}
// borders
const double borderSize = 4;
final cellPaintBorder = Paint();
cellPaintBorder.color = ColorTheme.getBorderColor();
cellPaintBorder.strokeWidth = borderSize;
cellPaintBorder.strokeCap = StrokeCap.round;
for (var row = 0; row < sizeVertical; row++) {
final double y = cellSize * row;
for (var col = 0; col < sizeHorizontal; col++) {
final double x = cellSize * col;
final GameCell cell = cells[row][col];
final int? cellValue = cell.value;
// top border
if ((row == 0) ||
(row > 1 &&
cellValue != currentGame.getCellValue(CellLocation.go(row - 1, col)))) {
final Offset borderStart = Offset(x, y);
final Offset borderStop = Offset(x + cellSize, y);
canvas.drawLine(borderStart, borderStop, cellPaintBorder);
}
// bottom border
if ((row == sizeVertical - 1) ||
((row + 1) < sizeVertical &&
cellValue != currentGame.getCellValue(CellLocation.go(row + 1, col)))) {
final Offset borderStart = Offset(x, y + cellSize);
final Offset borderStop = Offset(x + cellSize, y + cellSize);
canvas.drawLine(borderStart, borderStop, cellPaintBorder);
void drawCell({
required Canvas canvas,
required double x,
required double y,
required double cellSize,
required int cellValue,
required int colorsTheme,
required double overlapping,
required int gradientFrom,
required int gradientTo,
required double borderWidth,
required List<String>? contentStrings,
}) {
final Color baseColor = ColorTheme.getColor(cellValue, colorsTheme);
final paint = Paint();
// draw background
final Rect rect = Rect.fromLTWH(x, y, cellSize + overlapping, cellSize + overlapping);
// solid or gradient
if (gradientFrom == 0 && gradientTo == 0) {
paint.color = baseColor;
} else {
paint.shader = ui.Gradient.linear(
rect.topLeft,
rect.bottomCenter,
[
(gradientFrom < 0)
? baseColor.lighten(-gradientFrom)
: baseColor.darken(gradientFrom),
(gradientTo < 0) ? baseColor.lighten(-gradientTo) : baseColor.darken(gradientTo),
],
);
}
paint.style = PaintingStyle.fill;
canvas.drawRect(rect, paint);
// left border
if ((col == 0) ||
(col > 1 &&
cellValue != currentGame.getCellValue(CellLocation.go(row, col - 1)))) {
final Offset borderStart = Offset(x, y);
final Offset borderStop = Offset(x, y + cellSize);
canvas.drawLine(borderStart, borderStop, cellPaintBorder);
}
// draw border
if (borderWidth != 0) {
final Rect border = Rect.fromLTWH(x + borderWidth, y + borderWidth,
cellSize + overlapping - 2 * borderWidth, cellSize + overlapping - 2 * borderWidth);
// right border
if ((col == sizeHorizontal - 1) ||
((col + 1) < sizeHorizontal &&
cellValue != currentGame.getCellValue(CellLocation.go(row, col + 1)))) {
final Offset borderStart = Offset(x + cellSize, y);
final Offset borderStop = Offset(x + cellSize, y + cellSize);
canvas.drawLine(borderStart, borderStop, cellPaintBorder);
}
}
}
final paintBorder = Paint();
paintBorder.color = baseColor.darken(20);
paintBorder.style = PaintingStyle.stroke;
paintBorder.strokeWidth = borderWidth;
paintBorder.strokeJoin = StrokeJoin.round;
canvas.drawRect(border, paintBorder);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
// draw content
if (contentStrings != null) {
final int emojiIndex =
cellValue - 1 + game.shuffledColors[0] + 2 * game.shuffledColors[1];
final String text = contentStrings[emojiIndex % contentStrings.length];
final textSpan = TextSpan(
text: text,
style: TextStyle(
color: Colors.black,
fontSize: 4 + cellSize / 2,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
x + (cellSize - textPainter.width) * 0.5,
y + (cellSize - textPainter.height) * 0.5,
),
);
}
}
}
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:jeweled/config/default_game_settings.dart';
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/models/settings_game.dart';
import 'package:jeweled/models/settings_global.dart';
import 'package:jeweled/utils/color_extensions.dart';
import 'package:jeweled/utils/color_theme.dart';
import 'package:jeweled/utils/tools.dart';
class ParameterPainter extends CustomPainter {
const ParameterPainter({
required this.code,
required this.value,
required this.isSelected,
required this.gameSettings,
required this.globalSettings,
});
final String code;
final int value;
final bool isSelected;
final GameSettings gameSettings;
final GlobalSettings globalSettings;
@override
void paint(Canvas canvas, Size size) {
// force square
final double canvasSize = min(size.width, size.height);
const Color borderColorEnabled = Colors.blue;
const Color borderColorDisabled = Colors.white;
// "enabled/disabled" border
final paint = Paint();
paint.style = PaintingStyle.stroke;
paint.color = isSelected ? borderColorEnabled : borderColorDisabled;
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 20 / 100 * canvasSize;
canvas.drawRect(
Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint);
// content
switch (code) {
case 'colorsCount':
paintColorsCountParameterItem(value, canvas, canvasSize);
break;
case 'boardSize':
paintBoardSizeParameterItem(value, canvas, canvasSize);
break;
case 'colorsTheme':
paintColorsThemeParameterItem(value, canvas, canvasSize);
break;
case 'graphicTheme':
paintGraphicThemeParameterItem(value, canvas, canvasSize);
break;
default:
printlog('Unknown parameter: $code/$value');
paintUnknownParameterItem(value, canvas, canvasSize);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
// "unknown" parameter -> simple bock with text
void paintUnknownParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
paint.color = Colors.grey;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
final textSpan = TextSpan(
text: '?\n$value',
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size - textPainter.width) * 0.5,
(size - textPainter.height) * 0.5,
),
);
}
void paintBoardSizeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey;
int gridWidth = 1;
switch (value) {
case DefaultGameSettings.boardSizeValueSmall:
backgroundColor = Colors.green;
gridWidth = 2;
break;
case DefaultGameSettings.boardSizeValueMedium:
backgroundColor = Colors.orange;
gridWidth = 3;
break;
case DefaultGameSettings.boardSizeValueLarge:
backgroundColor = Colors.red;
gridWidth = 4;
break;
case DefaultGameSettings.boardSizeValueExtraLarge:
backgroundColor = Colors.purple;
gridWidth = 5;
break;
default:
printlog('Wrong value for boardSize parameter value: $value');
}
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
// Colored background
paint.color = backgroundColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
// Mini grid
final borderColor = Colors.grey.shade800;
final double cellSize = size / 7;
final double origin = (size - gridWidth * cellSize) / 2;
for (int row = 0; row < gridWidth; row++) {
for (int col = 0; col < gridWidth; col++) {
final Offset topLeft = Offset(origin + col * cellSize, origin + row * cellSize);
final Offset bottomRight = topLeft + Offset(cellSize, cellSize);
final squareColor =
Color(ColorTheme.getColorCode(col + row * gridWidth, globalSettings.colorsTheme));
paint.color = squareColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
paint.color = borderColor;
paint.style = PaintingStyle.stroke;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
}
}
}
void paintColorsCountParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey;
switch (value) {
case DefaultGameSettings.colorsCountValueLow:
backgroundColor = Colors.green;
break;
case DefaultGameSettings.colorsCountValueMedium:
backgroundColor = Colors.orange;
break;
case DefaultGameSettings.colorsCountValueHigh:
backgroundColor = Colors.red;
break;
case DefaultGameSettings.colorsCountValueVeryHigh:
backgroundColor = Colors.purple;
break;
default:
printlog('Wrong value for colorsCount parameter value: $value');
}
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
// Colored background
paint.color = backgroundColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
// Colors preview
const List<Offset> positions = [
Offset(0, 0),
Offset(1, 0),
Offset(2, 0),
Offset(2, 1),
Offset(2, 2),
Offset(1, 2),
Offset(0, 2),
Offset(0, 1),
];
final double padding = 4 / 100 * size;
final double margin = 3 / 100 * size;
final double width = ((size - 2 * padding) / 3) - 2 * margin;
for (int colorIndex = 0; colorIndex < value; colorIndex++) {
final Offset position = positions[colorIndex];
final Offset topLeft = Offset(padding + margin + position.dx * (width + 2 * margin),
padding + margin + position.dy * (width + 2 * margin));
final Offset bottomRight = topLeft + Offset(width, width);
final squareColor =
Color(ColorTheme.getColorCode(colorIndex, globalSettings.colorsTheme));
paint.color = squareColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
final borderColor = squareColor.darken(20);
paint.color = borderColor;
paint.style = PaintingStyle.stroke;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
}
// centered text value
final textSpan = TextSpan(
text: value.toString(),
style: TextStyle(
color: Colors.black,
fontSize: size / 4,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size - textPainter.width) * 0.5,
(size - textPainter.height) * 0.5,
),
);
}
void paintColorsThemeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey;
const int gridWidth = 4;
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
// Colored background
paint.color = backgroundColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
// Mini grid
final borderColor = Colors.grey.shade800;
final double cellSize = size / gridWidth;
final double origin = (size - gridWidth * cellSize) / 2;
for (int row = 0; row < gridWidth; row++) {
for (int col = 0; col < gridWidth; col++) {
final Offset topLeft = Offset(origin + col * cellSize, origin + row * cellSize);
final Offset bottomRight = topLeft + Offset(cellSize, cellSize);
final squareColor = Color(ColorTheme.getColorCode(col + row * gridWidth, value));
paint.color = squareColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
paint.color = borderColor;
paint.style = PaintingStyle.stroke;
canvas.drawRect(Rect.fromPoints(topLeft, bottomRight), paint);
}
}
}
void paintGraphicThemeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey;
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
// Colored background
paint.color = backgroundColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
// cells preview
const List<Offset> positions = [
Offset(0, 0),
Offset(1, 0),
Offset(2, 0),
Offset(2, 1),
Offset(2, 2),
Offset(1, 2),
Offset(0, 2),
Offset(0, 1),
];
final double padding = 5 / 100 * size;
final double width = (size - 2 * padding) / 3;
bool drawBorder = false;
bool gradientColor = false;
List<String> contentStrings = [];
switch (value) {
case DefaultGlobalSettings.graphicThemeSolidBackground:
break;
case DefaultGlobalSettings.graphicThemeGradientAndBorder:
drawBorder = true;
gradientColor = true;
break;
case DefaultGlobalSettings.graphicThemeEmojis:
contentStrings = DefaultGlobalSettings.graphicThemeContentEmojiStrings;
break;
case DefaultGlobalSettings.graphicThemePatterns:
contentStrings = DefaultGlobalSettings.graphicThemeContentPatternStrings;
break;
default:
printlog('Wrong value for colorsCount parameter value: $value');
}
for (int itemValue = 0; itemValue < positions.length; itemValue++) {
final Offset position = positions[itemValue];
final Offset topLeft =
Offset(padding + position.dx * width, padding + position.dy * width);
final Offset bottomRight = topLeft + Offset(width, width);
final Rect itemBox = Rect.fromPoints(topLeft, bottomRight);
final itemColor = ColorTheme.getColor(itemValue, globalSettings.colorsTheme);
paint.color = itemColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(itemBox, paint);
// gradient background
if (gradientColor) {
paint.shader = ui.Gradient.linear(itemBox.topLeft, itemBox.bottomCenter, [
itemColor.lighten(10),
itemColor.darken(10),
]);
paint.style = PaintingStyle.fill;
canvas.drawRect(itemBox, paint);
}
// cell border
if (drawBorder) {
final paintBorder = Paint();
paintBorder.color = itemColor.darken(20);
paintBorder.style = PaintingStyle.stroke;
paintBorder.strokeWidth = 2;
canvas.drawRect(itemBox, paintBorder);
}
// centered text value
if (contentStrings.isNotEmpty) {
final textSpan = TextSpan(
text: contentStrings[itemValue],
style: TextStyle(
color: Colors.black,
fontSize: width / 2,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
topLeft.dx + (width - textPainter.width) * 0.5,
topLeft.dy + (width - textPainter.height) * 0.5,
),
);
}
}
}
}
......@@ -9,9 +9,9 @@ class SkeletonScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: GlobalAppBar(),
appBar: const GlobalAppBar(),
extendBodyBehindAppBar: false,
body: ScreenGame(),
body: const ScreenGame(),
backgroundColor: Theme.of(context).colorScheme.background,
);
}
......
......@@ -16,7 +16,9 @@ class GameWidget extends StatelessWidget {
builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame;
return Column(
return Container(
padding: const EdgeInsets.all(4),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
......@@ -25,8 +27,11 @@ class GameWidget extends StatelessWidget {
const SizedBox(height: 2),
const GameBoard(),
const SizedBox(height: 2),
currentGame.isFinished ? const GameBottomButtonsWidget() : const SizedBox.shrink(),
currentGame.isFinished
? const GameBottomButtonsWidget()
: const SizedBox.shrink(),
],
),
);
},
);
......
......@@ -6,9 +6,95 @@ import 'package:jeweled/models/game.dart';
import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/ui/painters/game_board_painter.dart';
class GameBoard extends StatelessWidget {
class GameBoard extends StatefulWidget {
const GameBoard({super.key});
@override
State<GameBoard> createState() => _GameBoard();
}
class _GameBoard extends State<GameBoard> with TickerProviderStateMixin {
final int animationDuration = 500;
List<List<Animation<double>?>> animations = [];
void resetAnimations(int boardSize) {
animations = List.generate(
boardSize,
(i) => List.generate(
boardSize,
(i) => null,
),
);
}
void animateCells(List<CellLocation> cellsToRemove) {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
final Game currentGame = gameCubit.state.currentGame;
// "move down" cells
final controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: animationDuration),
)..addListener(() {
if (mounted) {
setState(() {});
}
});
if (mounted) {
setState(() {});
}
Animation<double> animation(int count) => Tween(
begin: 0.0,
end: count.toDouble(),
).animate(CurvedAnimation(
curve: Curves.bounceOut,
parent: controller,
))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
gameCubit.postAnimate();
resetAnimations(currentGame.gameSettings.boardSize);
setState(() {});
controller.dispose();
}
});
// Count translation length for each cell to move
final List<List<int>> stepsDownCounts = List.generate(
currentGame.gameSettings.boardSize,
(i) => List.generate(
currentGame.gameSettings.boardSize,
(i) => 0,
),
);
for (CellLocation cellToRemove in cellsToRemove) {
for (int i = 0; i < cellToRemove.row; i++) {
stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col] += 1;
}
}
// Build animation for each cell to move
bool needAnimation = false;
for (CellLocation cellToRemove in cellsToRemove) {
for (int i = 0; i < cellToRemove.row; i++) {
final int stepsCount = stepsDownCounts[(cellToRemove.row - i) - 1][cellToRemove.col];
animations[(cellToRemove.row - i) - 1][cellToRemove.col] = animation(stepsCount);
needAnimation = true;
}
}
controller.forward().orCancel;
if (!needAnimation) {
gameCubit.postAnimate();
}
}
@override
Widget build(BuildContext context) {
final double displayWidth = MediaQuery.of(context).size.width;
......@@ -18,20 +104,38 @@ class GameBoard extends StatelessWidget {
builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame;
if (animations.isEmpty) {
resetAnimations(currentGame.gameSettings.boardSize);
}
return GestureDetector(
onTapUp: (details) {
double xTap = details.localPosition.dx;
double yTap = details.localPosition.dy;
int col = xTap ~/ (displayWidth / currentGame.settings.boardSize);
int row = yTap ~/ (displayWidth / currentGame.settings.boardSize);
bool animationInProgress = false;
for (List<Animation<double>?> row in animations) {
for (Animation<double>? cell in row) {
if (cell != null) {
animationInProgress = true;
}
}
}
if (!animationInProgress) {
final double xTap = details.localPosition.dx;
final double yTap = details.localPosition.dy;
final int col = xTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
final int row = yTap ~/ (displayWidth / currentGame.gameSettings.boardSize);
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
gameCubit.tapOnCell(CellLocation.go(row, col));
animateCells(gameCubit.tapOnCell(CellLocation.go(row, col)));
}
},
child: CustomPaint(
size: Size(displayWidth, displayWidth),
willChange: false,
painter: GameBoardPainter(currentGame),
painter: GameBoardPainter(
game: currentGame,
animations: animations,
),
isComplex: true,
),
);
......
......@@ -15,12 +15,11 @@ class GameBottomButtonsWidget extends StatelessWidget {
image: AssetImage(decorationImageAssetName),
fit: BoxFit.fill,
),
onPressed: () => null,
onPressed: () {},
);
return Container(
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
return Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
......@@ -30,7 +29,7 @@ class GameBottomButtonsWidget extends StatelessWidget {
Column(
children: [
TextButton(
child: Image(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
......@@ -47,7 +46,6 @@ class GameBottomButtonsWidget extends StatelessWidget {
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:unicons/unicons.dart';
import 'package:jeweled/cubit/game_cubit.dart';
import 'package:jeweled/models/game.dart';
......@@ -55,6 +56,13 @@ class GameTopIndicatorWidget extends StatelessWidget {
color: availableBlocksCountTextColor,
),
),
TextButton(
child: const Icon(UniconsSolid.refresh),
onPressed: () {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
gameCubit.shuffleColors(currentGame.globalSettings.colorsTheme);
},
)
],
),
],
......
......@@ -18,13 +18,11 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
if (currentGame.isRunning) {
menuActions.add(TextButton(
child: Container(
child: Image(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
),
onPressed: () => null,
onPressed: () {},
onLongPress: () {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
gameCubit.quitGame();
......
......@@ -2,97 +2,120 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jeweled/config/default_game_settings.dart';
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/cubit/game_cubit.dart';
import 'package:jeweled/cubit/settings_cubit.dart';
import 'package:jeweled/models/game_settings.dart';
import 'package:jeweled/cubit/settings_game_cubit.dart';
import 'package:jeweled/cubit/settings_global_cubit.dart';
import 'package:jeweled/ui/painters/parameter_painter.dart';
class Parameters extends StatelessWidget {
const Parameters({super.key});
static const double separatorHeight = 2.0;
static const double blockMargin = 3.0;
static const double blockPadding = 2.0;
static const Color buttonBackgroundColor = Colors.white;
static const Color buttonBorderColorActive = Colors.blue;
static const Color buttonBorderColorInactive = Colors.white;
static const double buttonBorderWidth = 10.0;
static const double buttonBorderRadius = 8.0;
static const double buttonPadding = 0.0;
static const double buttonMargin = 0.0;
final double separatorHeight = 8.0;
@override
Widget build(BuildContext context) {
return BlocBuilder<SettingsCubit, SettingsState>(
builder: (BuildContext context, SettingsState settingsState) {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
final SettingsCubit settingsCubit = BlocProvider.of<SettingsCubit>(context);
final List<Widget> lines = [];
List<Widget> buildParametersLine({
required String code,
required bool isGlobal,
}) {
final List<Widget> parameterButtons = [];
DefaultGameSettings.availableParameters.forEach((code) {
final List<dynamic> availableValues = DefaultGameSettings.getAvailableValues(code);
final List<int> availableValues = isGlobal
? DefaultGlobalSettings.getAvailableValues(code)
: DefaultGameSettings.getAvailableValues(code);
if (availableValues.length > 1) {
final List<Widget> parameterButtons = [];
for (int value in availableValues) {
final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
builder: (BuildContext context, GameSettingsState gameSettingsState) {
return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
final GameSettingsCubit gameSettingsCubit =
BlocProvider.of<GameSettingsCubit>(context);
final GlobalSettingsCubit globalSettingsCubit =
BlocProvider.of<GlobalSettingsCubit>(context);
final dynamic currentValue = settingsCubit.getParameterValue(code);
final int currentValue = isGlobal
? globalSettingsCubit.getParameterValue(code)
: gameSettingsCubit.getParameterValue(code);
availableValues.forEach((value) {
final bool isActive = (value == currentValue);
final String imageAsset = code + '_' + value.toString();
final Widget parameterButton = TextButton(
final double displayWidth = MediaQuery.of(context).size.width;
final double itemWidth = displayWidth / availableValues.length - 25;
return TextButton(
child: Container(
margin: EdgeInsets.all(buttonMargin),
padding: EdgeInsets.all(buttonPadding),
decoration: BoxDecoration(
color: buttonBackgroundColor,
borderRadius: BorderRadius.circular(buttonBorderRadius),
border: Border.all(
color: isActive ? buttonBorderColorActive : buttonBorderColorInactive,
width: buttonBorderWidth,
margin: const EdgeInsets.all(0),
padding: const EdgeInsets.all(0),
child: CustomPaint(
size: Size(itemWidth, itemWidth),
willChange: false,
painter: ParameterPainter(
code: code,
value: value,
isSelected: isActive,
gameSettings: gameSettingsState.settings,
globalSettings: globalSettingsState.settings,
),
isComplex: true,
),
child: buildImageWidget(imageAsset),
),
onPressed: () => settingsCubit.setParameterValue(code, value),
onPressed: () => isGlobal
? globalSettingsCubit.setParameterValue(code, value)
: gameSettingsCubit.setParameterValue(code, value),
);
},
);
},
);
parameterButtons.add(parameterButton);
});
}
lines.add(Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: parameterButtons,
return parameterButtons;
}
@override
Widget build(BuildContext context) {
final List<Widget> lines = [];
// Game settings
for (String code in DefaultGameSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: false,
),
],
));
lines.add(SizedBox(height: separatorHeight));
}
});
return Container(
child: Column(
children: [
SizedBox(height: separatorHeight),
Column(
children: lines,
),
SizedBox(height: separatorHeight),
buildStartNewGameButton(gameCubit, settingsState.settings),
],
lines.add(SizedBox(height: separatorHeight));
lines.add(buildStartNewGameButton());
lines.add(SizedBox(height: separatorHeight));
// Global settings
for (String code in DefaultGlobalSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: true,
),
);
},
));
lines.add(SizedBox(height: separatorHeight));
}
return Column(
children: lines,
);
}
static Image buildImageWidget(String imageAssetCode) {
return Image(
image: AssetImage('assets/icons/' + imageAssetCode + '.png'),
image: AssetImage('assets/icons/$imageAssetCode.png'),
fit: BoxFit.fill,
);
}
......@@ -108,18 +131,27 @@ class Parameters extends StatelessWidget {
children: [
TextButton(
child: buildImageContainerWidget('placeholder'),
onPressed: () => null,
onPressed: () {},
),
],
);
}
static Container buildStartNewGameButton(GameCubit gameCubit, GameSettings settings) {
static Widget buildStartNewGameButton() {
const double blockMargin = 3.0;
const double blockPadding = 2.0;
return BlocBuilder<GameSettingsCubit, GameSettingsState>(
builder: (BuildContext context, GameSettingsState gameSettingsState) {
return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
final GameCubit gameCubit = BlocProvider.of<GameCubit>(context);
return Container(
margin: EdgeInsets.all(blockMargin),
padding: EdgeInsets.all(blockPadding),
margin: const EdgeInsets.all(blockMargin),
padding: const EdgeInsets.all(blockPadding),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
......@@ -128,7 +160,10 @@ class Parameters extends StatelessWidget {
children: [
TextButton(
child: buildImageContainerWidget('button_start'),
onPressed: () => gameCubit.startNewGame(settings),
onPressed: () => gameCubit.startNewGame(
gameSettings: gameSettingsState.settings,
globalSettings: globalSettingsState.settings,
),
),
],
),
......@@ -138,5 +173,9 @@ class Parameters extends StatelessWidget {
],
),
);
},
);
},
);
}
}
import 'package:flutter/material.dart';
class ColorTheme {
static Map<String, List<int>> itemColors = {
'default': [
0xffffff,
0xe63a3f,
0x708cfd,
0x359c35,
0xffce2c,
0xff6f43,
0xa13cb1,
0x38ffff,
0xf2739d,
static Map<int, List<int>> itemColors = {
// legacy
// 0:[0x9D9D9D,0xFFFFFF,0xBE2633,0xE06F8B,0x493C2B,0xA46422,0xEB8931,0xF7E26B,0x2F484E,0x44891A,0xA3CE27,0x1B2632,0x005784,0x31A2F2,0xB2DCEF,],
// https://lospec.com/palette-list/gothic-bit
1: [
0x0e0e12,
0x1a1a24,
0x333346,
0x535373,
0x8080a4,
0xa6a6bf,
0xc1c1d2,
0xe6e6ec,
],
// https://lospec.com/palette-list/sweethope
2: [
0x615e85,
0x9c8dc2,
0xd9a3cd,
0xebc3a7,
0xe0e0dc,
0xa3d1af,
0x90b4de,
0x717fb0,
],
// https://lospec.com/palette-list/nostalgic-dreams
3: [
0xd9af80,
0xb07972,
0x524352,
0x686887,
0x7f9bb0,
0xbfd4b0,
0x90b870,
0x628c70,
],
// https://lospec.com/palette-list/arjibi8
4: [
0x8bc7bf,
0x5796a1,
0x524bb3,
0x471b6e,
0x702782,
0xb0455a,
0xde8b6f,
0xebd694,
],
// https://lospec.com/palette-list/kotomasho-8
// 5:[0x40263e,0x5979a6,0x84c2a3,0xefe8c3,0xefefef,0xcbc7d6,0xd06060,0x773971,],
// https://lospec.com/palette-list/desatur8
// 6:[0xf0f0eb,0xffff8f,0x7be098,0x849ad8,0xe8b382,0xd8828e,0xa776c1,0x545155,],
// https://lospec.com/palette-list/purplemorning8
// 7:[0x211d38,0x2e2a4f,0x3b405e,0x60556e,0x9a6278,0xc7786f,0xcfa98a,0xcdd4a5,],
// https://lospec.com/palette-list/cold-war-8
// 8:[0x332422,0xc95b40,0xff9b5e,0xfcdf76,0x4c2f7f,0x3a66ad,0x39cec2,0xfafff9,],
// https://lospec.com/palette-list/low-8
// 9:[0x111323,0x374566,0x50785d,0x8497b3,0xe8dcd8,0xcfb463,0xb35447,0x692e4b,],
};
static int defaultItemColor = 0x808080;
static int getColorCode(int? value) {
const skin = 'default';
static int getColorsCount(final int skin) {
if (itemColors.containsKey(skin) && null != itemColors[skin]) {
List<int> skinColors = itemColors[skin] ?? [];
return skinColors.length;
}
return 0;
}
static int getColorCode(int? value, final int skin) {
if (value != null && itemColors.containsKey(skin) && null != itemColors[skin]) {
List<int> skinColors = itemColors[skin] ?? [];
if (skinColors.length > value) {
return (skinColors[value]) | 0xFF000000;
}
return (skinColors[value % getColorsCount(skin)]) | 0xFF000000;
}
return defaultItemColor | 0xFF000000;
}
static Color getColor(int? value, final int skin) {
return Color(getColorCode(value, skin));
}
static Color getBorderColor() {
return Colors.black;
return Colors.grey;
}
}
import 'package:flutter/foundation.dart';
void printlog(String message) {
if (!kReleaseMode) {
debugPrint(message);
}
}