mirror of https://github.com/buggins/dlangui.git
Tetris example: refactoring
This commit is contained in:
parent
939f85cd93
commit
75cb2325cb
|
@ -141,22 +141,127 @@ enum TetrisAction : int {
|
||||||
FastDown,
|
FastDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum : int {
|
||||||
|
WALL = -1,
|
||||||
|
EMPTY = 0,
|
||||||
|
FIGURE1,
|
||||||
|
FIGURE2,
|
||||||
|
FIGURE3,
|
||||||
|
FIGURE4,
|
||||||
|
FIGURE5,
|
||||||
|
FIGURE6,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum : int {
|
||||||
|
ORIENTATION0,
|
||||||
|
ORIENTATION90,
|
||||||
|
ORIENTATION180,
|
||||||
|
ORIENTATION270
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint[6] _figureColors = [0xFF0000, 0xA0A000, 0xA000A0, 0x0000FF, 0x800000, 0x408000];
|
||||||
|
|
||||||
|
/// Figure type, orientation and position container
|
||||||
|
struct FigurePosition {
|
||||||
|
int index;
|
||||||
|
int orientation;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
this(int index, int orientation, int x, int y) {
|
||||||
|
this.index = index;
|
||||||
|
this.orientation = orientation;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
/// return rotated position CCW for angle=1, CW for angle=-1
|
||||||
|
FigurePosition rotate(int angle) {
|
||||||
|
int newOrientation = (orientation + 4 + angle) & 3;
|
||||||
|
return FigurePosition(index, newOrientation, x, y);
|
||||||
|
}
|
||||||
|
/// return moved position
|
||||||
|
FigurePosition move(int dx, int dy = 0) {
|
||||||
|
return FigurePosition(index, orientation, x + dx, y + dy);
|
||||||
|
}
|
||||||
|
/// return shape for figure orientation
|
||||||
|
@property FigureShape shape() const {
|
||||||
|
return FIGURES[index - 1].shapes[orientation];
|
||||||
|
}
|
||||||
|
/// return color for figure
|
||||||
|
@property uint color() const {
|
||||||
|
return _figureColors[index - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Cup content
|
||||||
|
|
||||||
|
Coordinates are relative to bottom left corner.
|
||||||
|
*/
|
||||||
|
struct Cup {
|
||||||
|
private int[] _cup;
|
||||||
|
private int _cols;
|
||||||
|
private int _rows;
|
||||||
|
/// returns number of columns
|
||||||
|
@property int cols() {
|
||||||
|
return _cols;
|
||||||
|
}
|
||||||
|
/// returns number of columns
|
||||||
|
@property int rows() {
|
||||||
|
return _rows;
|
||||||
|
}
|
||||||
|
/// inits empty cup of specified size
|
||||||
|
void init(int cols, int rows) {
|
||||||
|
_cols = cols;
|
||||||
|
_rows = rows;
|
||||||
|
_cup = new int[_cols * _rows];
|
||||||
|
for (int i = 0; i < _cup.length; i++)
|
||||||
|
_cup[i] = EMPTY;
|
||||||
|
}
|
||||||
|
/// returns cell content at specified position
|
||||||
|
int opIndex(int col, int row) {
|
||||||
|
if (col < 0 || row < 0 || col >= _cols || row >= _rows)
|
||||||
|
return WALL;
|
||||||
|
return _cup[row * _cols + col];
|
||||||
|
}
|
||||||
|
/// set cell value
|
||||||
|
void opIndexAssign(int value, int col, int row) {
|
||||||
|
if (col < 0 || row < 0 || col >= _cols || row >= _rows)
|
||||||
|
return; // ignore modification of cells outside cup
|
||||||
|
_cup[row * _cols + col] = value;
|
||||||
|
}
|
||||||
|
/// put figure at specified position
|
||||||
|
void putFigure(FigurePosition pos) {
|
||||||
|
FigureShape shape = pos.shape;
|
||||||
|
foreach(cell; shape.cells) {
|
||||||
|
this[pos.x + cell.dx, pos.y + cell.dy] = pos.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// check if all cells where figire is located are free
|
||||||
|
bool isPositionFree(in FigurePosition pos) {
|
||||||
|
FigureShape shape = pos.shape;
|
||||||
|
foreach(cell; shape.cells) {
|
||||||
|
int value = this[pos.x + cell.dx, pos.y + cell.dy];
|
||||||
|
if (value != 0) // occupied
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cup widget
|
||||||
class CupWidget : Widget {
|
class CupWidget : Widget {
|
||||||
/// cup columns count
|
/// cup columns count
|
||||||
int _cols;
|
int _cols;
|
||||||
/// cup rows count
|
/// cup rows count
|
||||||
int _rows;
|
int _rows;
|
||||||
int[] _cup;
|
/// cup data
|
||||||
/// current figure id
|
Cup _cup;
|
||||||
int _currentFigure;
|
|
||||||
/// current figure base point col
|
/// current figure index, orientation, position
|
||||||
int _currentFigureX;
|
FigurePosition _currentFigure;
|
||||||
/// current figure base point row
|
|
||||||
int _currentFigureY;
|
|
||||||
/// current figure base point row
|
|
||||||
int _currentFigureOrientation;
|
|
||||||
/// next figure id
|
/// next figure id
|
||||||
int _nextFigure;
|
FigurePosition _nextFigure;
|
||||||
|
|
||||||
/// Level 1..10
|
/// Level 1..10
|
||||||
int _level;
|
int _level;
|
||||||
/// Single cell movement duration for current level, in 1/10000000 of seconds
|
/// Single cell movement duration for current level, in 1/10000000 of seconds
|
||||||
|
@ -165,6 +270,7 @@ class CupWidget : Widget {
|
||||||
bool _fastDownFlag;
|
bool _fastDownFlag;
|
||||||
|
|
||||||
AnimationHelper _animation;
|
AnimationHelper _animation;
|
||||||
|
private PopupWidget _gameOverPopup;
|
||||||
|
|
||||||
/// Cup States
|
/// Cup States
|
||||||
enum CupState : int {
|
enum CupState : int {
|
||||||
|
@ -189,25 +295,6 @@ class CupWidget : Widget {
|
||||||
|
|
||||||
static const int RESERVED_ROWS = 5; // reserved for next figure
|
static const int RESERVED_ROWS = 5; // reserved for next figure
|
||||||
|
|
||||||
enum : int {
|
|
||||||
WALL = -1,
|
|
||||||
EMPTY = 0,
|
|
||||||
FIGURE1,
|
|
||||||
FIGURE2,
|
|
||||||
FIGURE3,
|
|
||||||
FIGURE4,
|
|
||||||
FIGURE5,
|
|
||||||
FIGURE6,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum : int {
|
|
||||||
ORIENTATION0,
|
|
||||||
ORIENTATION90,
|
|
||||||
ORIENTATION180,
|
|
||||||
ORIENTATION270
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint[6] _figureColors = [0xFF0000, 0xA0A000, 0xA000A0, 0x0000FF, 0x800000, 0x408000];
|
|
||||||
|
|
||||||
|
|
||||||
/// set difficulty level 1..10
|
/// set difficulty level 1..10
|
||||||
|
@ -256,41 +343,38 @@ class CupWidget : Widget {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rotate(int delta) {
|
bool rotate(int angle) {
|
||||||
int newOrientation = (_currentFigureOrientation + 4 + delta) & 3;
|
FigurePosition newpos = _currentFigure.rotate(angle);
|
||||||
if (isPositionFree(_currentFigure, newOrientation, _currentFigureX, _currentFigureY)) {
|
if (_cup.isPositionFree(newpos)) {
|
||||||
if (_state == CupState.FallingFigure) {
|
if (_state == CupState.FallingFigure) {
|
||||||
// special handling for fall animation
|
// special handling for fall animation
|
||||||
if (!isPositionFree(_currentFigure, newOrientation, _currentFigureX, _currentFigureY - 1)) {
|
if (!_cup.isPositionFree(newpos.move(0, -1))) {
|
||||||
if (isPositionFreeBelow())
|
if (isPositionFreeBelow())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_currentFigureOrientation = newOrientation;
|
_currentFigure = newpos;
|
||||||
return true;
|
return true;
|
||||||
} else if (isPositionFree(_currentFigure, newOrientation, _currentFigureX, _currentFigureY - 1)) {
|
} else if (_cup.isPositionFree(newpos.move(0, -1))) {
|
||||||
_currentFigureOrientation = newOrientation;
|
_currentFigure = newpos.move(0, -1);
|
||||||
_currentFigureY = _currentFigureY - 1;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool move(int deltaX) {
|
bool move(int deltaX) {
|
||||||
int newx = _currentFigureX + deltaX;
|
FigurePosition newpos = _currentFigure.move(deltaX);
|
||||||
if (isPositionFree(_currentFigure, _currentFigureOrientation, newx, _currentFigureY)) {
|
if (_cup.isPositionFree(newpos)) {
|
||||||
if (_state == CupState.FallingFigure && !isPositionFree(_currentFigure, _currentFigureOrientation, newx, _currentFigureY - 1)) {
|
if (_state == CupState.FallingFigure && !_cup.isPositionFree(newpos.move(0, -1))) {
|
||||||
if (isPositionFreeBelow())
|
if (isPositionFreeBelow())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_currentFigureX = newx;
|
_currentFigure = newpos;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PopupWidget _gameOverPopup;
|
|
||||||
|
|
||||||
protected void onAnimationFinished() {
|
protected void onAnimationFinished() {
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case CupState.NewFigure:
|
case CupState.NewFigure:
|
||||||
|
@ -300,14 +384,14 @@ class CupWidget : Widget {
|
||||||
break;
|
break;
|
||||||
case CupState.FallingFigure:
|
case CupState.FallingFigure:
|
||||||
if (isPositionFreeBelow()) {
|
if (isPositionFreeBelow()) {
|
||||||
_currentFigureY--;
|
_currentFigure.y--;
|
||||||
if (_fastDownFlag)
|
if (_fastDownFlag)
|
||||||
setCupState(CupState.FallingFigure, 10);
|
setCupState(CupState.FallingFigure, 10);
|
||||||
else
|
else
|
||||||
setCupState(CupState.HangingFigure, 75);
|
setCupState(CupState.HangingFigure, 75);
|
||||||
} else {
|
} else {
|
||||||
// At bottom of cup
|
// At bottom of cup
|
||||||
putFigure(_currentFigure, _currentFigureOrientation, _currentFigureX, _currentFigureY);
|
_cup.putFigure(_currentFigure);
|
||||||
_fastDownFlag = false;
|
_fastDownFlag = false;
|
||||||
if (!dropNextFigure()) {
|
if (!dropNextFigure()) {
|
||||||
// Game Over
|
// Game Over
|
||||||
|
@ -339,37 +423,26 @@ class CupWidget : Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
void genNextFigure() {
|
void genNextFigure() {
|
||||||
_nextFigure = uniform(FIGURE1, FIGURE6 + 1);
|
_nextFigure.index = uniform(FIGURE1, FIGURE6 + 1);
|
||||||
|
_nextFigure.orientation = ORIENTATION0;
|
||||||
|
_nextFigure.x = _cols / 2;
|
||||||
|
_nextFigure.y = _rows - _nextFigure.shape.extent + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dropNextFigure() {
|
bool dropNextFigure() {
|
||||||
if (_nextFigure == 0)
|
if (_nextFigure.index == 0)
|
||||||
genNextFigure();
|
genNextFigure();
|
||||||
_currentFigure = _nextFigure;
|
_currentFigure = _nextFigure;
|
||||||
_currentFigureOrientation = ORIENTATION0;
|
_currentFigure.x = _cols / 2;
|
||||||
_currentFigureX = _cols / 2;
|
_currentFigure.y = _rows - 1 - _currentFigure.shape.y0;
|
||||||
_currentFigureY = _rows - 1 - FIGURES[_currentFigure - 1].shapes[_currentFigureOrientation].y0;
|
|
||||||
setCupState(CupState.NewFigure, 100, 255);
|
setCupState(CupState.NewFigure, 100, 255);
|
||||||
return isPositionFree();
|
return isPositionFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(int cols, int rows) {
|
void init(int cols, int rows) {
|
||||||
|
_cup.init(cols, rows);
|
||||||
_cols = cols;
|
_cols = cols;
|
||||||
_rows = rows;
|
_rows = rows;
|
||||||
_cup = new int[_cols * _rows];
|
|
||||||
for (int i = 0; i < _cup.length; i++)
|
|
||||||
_cup[i] = EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int cell(int col, int row) {
|
|
||||||
if (col < 0 || row < 0 || col >= _cols || row >= _rows)
|
|
||||||
return WALL;
|
|
||||||
return _cup[row * _cols + col];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setCell(int col, int row, int value) {
|
|
||||||
_cup[row * _cols + col] = value;
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Rect cellRect(Rect rc, int col, int row) {
|
protected Rect cellRect(Rect rc, int col, int row) {
|
||||||
|
@ -383,50 +456,12 @@ class CupWidget : Widget {
|
||||||
return Rect(x0, y0, x0 + dd, y0 + dd);
|
return Rect(x0, y0, x0 + dd, y0 + dd);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void drawCell(DrawBuf buf, Rect cellRc, uint color) {
|
|
||||||
cellRc.right--;
|
|
||||||
cellRc.bottom--;
|
|
||||||
int w = cellRc.width / 6;
|
|
||||||
buf.drawFrame(cellRc, color, Rect(w,w,w,w));
|
|
||||||
cellRc.shrink(w, w);
|
|
||||||
color = addAlpha(color, 0xC0);
|
|
||||||
buf.fillRect(cellRc, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void drawFigure(DrawBuf buf, Rect rc, int figureIndex, int orientation, int x, int y, int dy, uint alpha = 0) {
|
|
||||||
uint color = addAlpha(_figureColors[figureIndex - 1], alpha);
|
|
||||||
FigureShape shape = FIGURES[figureIndex - 1].shapes[orientation];
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
Rect cellRc = cellRect(rc, x + shape.cells[i].dx, y + shape.cells[i].dy);
|
|
||||||
cellRc.top += dy;
|
|
||||||
cellRc.bottom += dy;
|
|
||||||
drawCell(buf, cellRc, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool isPositionFree() {
|
protected bool isPositionFree() {
|
||||||
return isPositionFree(_currentFigure, _currentFigureOrientation, _currentFigureX, _currentFigureY);
|
return _cup.isPositionFree(_currentFigure);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool isPositionFreeBelow() {
|
protected bool isPositionFreeBelow() {
|
||||||
return isPositionFree(_currentFigure, _currentFigureOrientation, _currentFigureX, _currentFigureY - 1);
|
return _cup.isPositionFree(_currentFigure.move(0, -1));
|
||||||
}
|
|
||||||
|
|
||||||
protected bool isPositionFree(int figureIndex, int orientation, int x, int y) {
|
|
||||||
FigureShape shape = FIGURES[figureIndex - 1].shapes[orientation];
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
int value = cell(x + shape.cells[i].dx, y + shape.cells[i].dy);
|
|
||||||
if (value != 0) // occupied
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void putFigure(int figureIndex, int orientation, int x, int y) {
|
|
||||||
FigureShape shape = FIGURES[figureIndex - 1].shapes[orientation];
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
setCell(x + shape.cells[i].dx, y + shape.cells[i].dy, figureIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle keys
|
/// Handle keys
|
||||||
|
@ -448,6 +483,29 @@ class CupWidget : Widget {
|
||||||
return super.onKeyEvent(event);
|
return super.onKeyEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// draw cup cell
|
||||||
|
protected void drawCell(DrawBuf buf, Rect cellRc, uint color) {
|
||||||
|
cellRc.right--;
|
||||||
|
cellRc.bottom--;
|
||||||
|
int w = cellRc.width / 6;
|
||||||
|
buf.drawFrame(cellRc, color, Rect(w,w,w,w));
|
||||||
|
cellRc.shrink(w, w);
|
||||||
|
color = addAlpha(color, 0xC0);
|
||||||
|
buf.fillRect(cellRc, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// draw figure
|
||||||
|
protected void drawFigure(DrawBuf buf, Rect rc, FigurePosition figure, int dy, uint alpha = 0) {
|
||||||
|
uint color = addAlpha(_figureColors[figure.index - 1], alpha);
|
||||||
|
FigureShape shape = figure.shape;
|
||||||
|
foreach(cell; shape.cells) {
|
||||||
|
Rect cellRc = cellRect(rc, figure.x + cell.dx, figure.y + cell.dy);
|
||||||
|
cellRc.top += dy;
|
||||||
|
cellRc.bottom += dy;
|
||||||
|
drawCell(buf, cellRc, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw widget at its position to buffer
|
/// Draw widget at its position to buffer
|
||||||
override void onDraw(DrawBuf buf) {
|
override void onDraw(DrawBuf buf) {
|
||||||
super.onDraw(buf);
|
super.onDraw(buf);
|
||||||
|
@ -471,7 +529,7 @@ class CupWidget : Widget {
|
||||||
for (int row = 0; row < _rows; row++) {
|
for (int row = 0; row < _rows; row++) {
|
||||||
for (int col = 0; col < _cols; col++) {
|
for (int col = 0; col < _cols; col++) {
|
||||||
|
|
||||||
int value = cell(col, row);
|
int value = _cup[col, row];
|
||||||
Rect cellRc = cellRect(rc, col, row);
|
Rect cellRc = cellRect(rc, col, row);
|
||||||
|
|
||||||
Point middle = cellRc.middle;
|
Point middle = cellRc.middle;
|
||||||
|
@ -490,19 +548,19 @@ class CupWidget : Widget {
|
||||||
if (_state == CupState.FallingFigure && isPositionFreeBelow()) {
|
if (_state == CupState.FallingFigure && isPositionFreeBelow()) {
|
||||||
dy = _animation.getProgress(topLeft.height);
|
dy = _animation.getProgress(topLeft.height);
|
||||||
}
|
}
|
||||||
drawFigure(buf, rc, _currentFigure, _currentFigureOrientation, _currentFigureX, _currentFigureY, dy, 0);
|
drawFigure(buf, rc, _currentFigure, dy, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_nextFigure != 0) {
|
if (_nextFigure.index != 0) {
|
||||||
auto shape = FIGURES[_nextFigure - 1].shapes[0];
|
//auto shape = _nextFigure.shape;
|
||||||
uint nextFigureAlpha = 0;
|
uint nextFigureAlpha = 0;
|
||||||
if (_state == CupState.NewFigure) {
|
if (_state == CupState.NewFigure) {
|
||||||
nextFigureAlpha = _animation.progress;
|
nextFigureAlpha = _animation.progress;
|
||||||
drawFigure(buf, rc, _currentFigure, _currentFigureOrientation, _currentFigureX, _currentFigureY, 0, 255 - nextFigureAlpha);
|
drawFigure(buf, rc, _currentFigure, 0, 255 - nextFigureAlpha);
|
||||||
}
|
}
|
||||||
if (_state != CupState.GameOver) {
|
if (_state != CupState.GameOver) {
|
||||||
drawFigure(buf, rc, _nextFigure, ORIENTATION0, _cols / 2, _rows - shape.extent + 1, 0, blendAlpha(0xA0, nextFigureAlpha));
|
drawFigure(buf, rc, _nextFigure, 0, blendAlpha(0xA0, nextFigureAlpha));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue