Tetris example: refactoring

This commit is contained in:
Vadim Lopatin 2014-12-25 15:39:40 +03:00
parent 75cb2325cb
commit 1bab98e87e
1 changed files with 106 additions and 82 deletions

View File

@ -190,6 +190,14 @@ struct FigurePosition {
@property uint color() const { @property uint color() const {
return _figureColors[index - 1]; return _figureColors[index - 1];
} }
/// return true if figure index is not initialized
@property empty() const {
return index == 0;
}
/// clears content
void reset() {
index = 0;
}
} }
/** /**
@ -201,6 +209,15 @@ struct Cup {
private int[] _cup; private int[] _cup;
private int _cols; private int _cols;
private int _rows; private int _rows;
private FigurePosition _currentFigure;
/// current figure index, orientation, position
@property ref FigurePosition currentFigure() { return _currentFigure; }
private FigurePosition _nextFigure;
/// next figure
@property ref FigurePosition nextFigure() { return _nextFigure; }
/// returns number of columns /// returns number of columns
@property int cols() { @property int cols() {
return _cols; return _cols;
@ -229,14 +246,15 @@ struct Cup {
return; // ignore modification of cells outside cup return; // ignore modification of cells outside cup
_cup[row * _cols + col] = value; _cup[row * _cols + col] = value;
} }
/// put figure at specified position /// put current figure into cup at current position and orientation
void putFigure(FigurePosition pos) { void putFigure() {
FigureShape shape = pos.shape; FigureShape shape = _currentFigure.shape;
foreach(cell; shape.cells) { foreach(cell; shape.cells) {
this[pos.x + cell.dx, pos.y + cell.dy] = pos.index; this[_currentFigure.x + cell.dx, _currentFigure.y + cell.dy] = _currentFigure.index;
} }
} }
/// check if all cells where figire is located are free
/// check if all cells where specified figure is located are free
bool isPositionFree(in FigurePosition pos) { bool isPositionFree(in FigurePosition pos) {
FigureShape shape = pos.shape; FigureShape shape = pos.shape;
foreach(cell; shape.cells) { foreach(cell; shape.cells) {
@ -246,6 +264,67 @@ struct Cup {
} }
return true; return true;
} }
/// check if all cells where current figire is located are free
bool isPositionFree() {
return isPositionFree(_currentFigure);
}
/// check if all cells where current figire is located are free
bool isPositionFreeBelow() {
return isPositionFree(_currentFigure.move(0, -1));
}
/// try to rotate current figure, returns true if figure rotated
bool rotate(int angle, bool falling) {
FigurePosition newpos = _currentFigure.rotate(angle);
if (isPositionFree(newpos)) {
if (falling) {
// special handling for fall animation
if (!isPositionFree(newpos.move(0, -1))) {
if (isPositionFreeBelow())
return false;
}
}
_currentFigure = newpos;
return true;
} else if (isPositionFree(newpos.move(0, -1))) {
_currentFigure = newpos.move(0, -1);
return true;
}
return false;
}
/// try to move current figure, returns true if figure rotated
bool move(int deltaX, int deltaY, bool falling) {
FigurePosition newpos = _currentFigure.move(deltaX, deltaY);
if (isPositionFree(newpos)) {
if (falling && !isPositionFree(newpos.move(0, -1))) {
if (isPositionFreeBelow())
return false;
}
_currentFigure = newpos;
return true;
}
return false;
}
void genNextFigure() {
_nextFigure.index = uniform(FIGURE1, FIGURE6 + 1);
_nextFigure.orientation = ORIENTATION0;
_nextFigure.x = _cols / 2;
_nextFigure.y = _rows - _nextFigure.shape.extent + 1;
}
bool dropNextFigure() {
if (_nextFigure.empty)
genNextFigure();
_currentFigure = _nextFigure;
_currentFigure.x = _cols / 2;
_currentFigure.y = _rows - 1 - _currentFigure.shape.y0;
return isPositionFree();
}
} }
/// Cup widget /// Cup widget
@ -257,10 +336,6 @@ class CupWidget : Widget {
/// cup data /// cup data
Cup _cup; Cup _cup;
/// current figure index, orientation, position
FigurePosition _currentFigure;
/// next figure id
FigurePosition _nextFigure;
/// Level 1..10 /// Level 1..10
int _level; int _level;
@ -343,62 +418,32 @@ class CupWidget : Widget {
return true; return true;
} }
bool rotate(int angle) {
FigurePosition newpos = _currentFigure.rotate(angle);
if (_cup.isPositionFree(newpos)) {
if (_state == CupState.FallingFigure) {
// special handling for fall animation
if (!_cup.isPositionFree(newpos.move(0, -1))) {
if (isPositionFreeBelow())
return false;
}
}
_currentFigure = newpos;
return true;
} else if (_cup.isPositionFree(newpos.move(0, -1))) {
_currentFigure = newpos.move(0, -1);
return true;
}
return false;
}
bool move(int deltaX) {
FigurePosition newpos = _currentFigure.move(deltaX);
if (_cup.isPositionFree(newpos)) {
if (_state == CupState.FallingFigure && !_cup.isPositionFree(newpos.move(0, -1))) {
if (isPositionFreeBelow())
return false;
}
_currentFigure = newpos;
return true;
}
return false;
}
protected void onAnimationFinished() { protected void onAnimationFinished() {
switch (_state) { switch (_state) {
case CupState.NewFigure: case CupState.NewFigure:
_fastDownFlag = false; _fastDownFlag = false;
genNextFigure(); _cup.genNextFigure();
setCupState(CupState.HangingFigure, 75); setCupState(CupState.HangingFigure, 75);
break; break;
case CupState.FallingFigure: case CupState.FallingFigure:
if (isPositionFreeBelow()) { if (_cup.isPositionFreeBelow()) {
_currentFigure.y--; _cup.move(0, -1, false);
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
_cup.putFigure(_currentFigure); _cup.putFigure();
_fastDownFlag = false; _fastDownFlag = false;
if (!dropNextFigure()) { if (!_cup.dropNextFigure()) {
// Game Over // Game Over
setCupState(CupState.GameOver); setCupState(CupState.GameOver);
Widget popupWidget = new TextWidget("popup", "Game Over!"d); Widget popupWidget = new TextWidget("popup", "Game Over!"d);
popupWidget.padding(Rect(30, 30, 30, 30)).backgroundImageId("popup_background").alpha(0x40).fontWeight(800).fontSize(30); popupWidget.padding(Rect(30, 30, 30, 30)).backgroundImageId("popup_background").alpha(0x40).fontWeight(800).fontSize(30);
_gameOverPopup = window.showPopup(popupWidget, this); _gameOverPopup = window.showPopup(popupWidget, this);
} else {
setCupState(CupState.NewFigure, 100, 255);
} }
} }
break; break;
@ -422,23 +467,6 @@ class CupWidget : Widget {
} }
} }
void genNextFigure() {
_nextFigure.index = uniform(FIGURE1, FIGURE6 + 1);
_nextFigure.orientation = ORIENTATION0;
_nextFigure.x = _cols / 2;
_nextFigure.y = _rows - _nextFigure.shape.extent + 1;
}
bool dropNextFigure() {
if (_nextFigure.index == 0)
genNextFigure();
_currentFigure = _nextFigure;
_currentFigure.x = _cols / 2;
_currentFigure.y = _rows - 1 - _currentFigure.shape.y0;
setCupState(CupState.NewFigure, 100, 255);
return isPositionFree();
}
void init(int cols, int rows) { void init(int cols, int rows) {
_cup.init(cols, rows); _cup.init(cols, rows);
_cols = cols; _cols = cols;
@ -456,14 +484,6 @@ class CupWidget : Widget {
return Rect(x0, y0, x0 + dd, y0 + dd); return Rect(x0, y0, x0 + dd, y0 + dd);
} }
protected bool isPositionFree() {
return _cup.isPositionFree(_currentFigure);
}
protected bool isPositionFreeBelow() {
return _cup.isPositionFree(_currentFigure.move(0, -1));
}
/// Handle keys /// Handle keys
override bool onKeyEvent(KeyEvent event) { override bool onKeyEvent(KeyEvent event) {
if (event.action == KeyAction.KeyDown && _state == CupState.GameOver) { if (event.action == KeyAction.KeyDown && _state == CupState.GameOver) {
@ -545,22 +565,21 @@ class CupWidget : Widget {
// draw current figure falling // draw current figure falling
if (_state == CupState.FallingFigure || _state == CupState.HangingFigure) { if (_state == CupState.FallingFigure || _state == CupState.HangingFigure) {
int dy = 0; int dy = 0;
if (_state == CupState.FallingFigure && isPositionFreeBelow()) { if (falling && _cup.isPositionFreeBelow())
dy = _animation.getProgress(topLeft.height); dy = _animation.getProgress(topLeft.height);
} drawFigure(buf, rc, _cup.currentFigure, dy, 0);
drawFigure(buf, rc, _currentFigure, dy, 0);
} }
// draw next figure
if (_nextFigure.index != 0) { if (!_cup._nextFigure.empty) {
//auto shape = _nextFigure.shape; //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, 0, 255 - nextFigureAlpha); drawFigure(buf, rc, _cup.currentFigure, 0, 255 - nextFigureAlpha);
} }
if (_state != CupState.GameOver) { if (_state != CupState.GameOver) {
drawFigure(buf, rc, _nextFigure, 0, blendAlpha(0xA0, nextFigureAlpha)); drawFigure(buf, rc, _cup.nextFigure, 0, blendAlpha(0xA0, nextFigureAlpha));
} }
} }
@ -571,17 +590,21 @@ class CupWidget : Widget {
measuredContent(parentWidth, parentHeight, 350, 550); measuredContent(parentWidth, parentHeight, 350, 550);
} }
@property bool falling() {
return _state == CupState.FallingFigure;
}
/// override to handle specific actions /// override to handle specific actions
override bool handleAction(const Action a) { override bool handleAction(const Action a) {
switch (a.id) { switch (a.id) {
case TetrisAction.MoveLeft: case TetrisAction.MoveLeft:
move(-1); _cup.move(-1, 0, falling);
return true; return true;
case TetrisAction.MoveRight: case TetrisAction.MoveRight:
move(1); _cup.move(1, 0, falling);
return true; return true;
case TetrisAction.RotateCCW: case TetrisAction.RotateCCW:
rotate(1); _cup.rotate(1, falling);
return true; return true;
case TetrisAction.FastDown: case TetrisAction.FastDown:
handleFastDown(true); handleFastDown(true);
@ -596,7 +619,8 @@ class CupWidget : Widget {
void newGame() { void newGame() {
setLevel(1); setLevel(1);
init(_cols, _rows); init(_cols, _rows);
dropNextFigure(); _cup.dropNextFigure();
setCupState(CupState.NewFigure, 100, 255);
if (window && _gameOverPopup) { if (window && _gameOverPopup) {
window.removePopup(_gameOverPopup); window.removePopup(_gameOverPopup);
_gameOverPopup = null; _gameOverPopup = null;