Stefan Schuermans commited on 2019-07-15 19:04:46
Showing 5 changed files, with 131 additions and 16 deletions.
| ... | ... |
@@ -0,0 +1 @@ |
| 1 |
+2000 |
| ... | ... |
@@ -0,0 +1 @@ |
| 1 |
+gameover |
| ... | ... |
@@ -0,0 +1 @@ |
| 1 |
+yeah |
| ... | ... |
@@ -40,14 +40,18 @@ Tetris::Tetris(const std::string &name, Mgrs &mgrs, const Directory &dirBase): |
| 40 | 40 |
m_fileDelay(dirBase.getFile("delay")),
|
| 41 | 41 |
m_fileDropDelay(dirBase.getFile("dropDelay")),
|
| 42 | 42 |
m_fileBlinkDelay(dirBase.getFile("blinkDelay")),
|
| 43 |
+ m_fileGameOverDelay(dirBase.getFile("gameOverDelay")),
|
|
| 43 | 44 |
m_fileStartSound(dirBase.getFile("startSound")),
|
| 45 |
+ m_fileRowCompleteSound(dirBase.getFile("rowCompleteSound")),
|
|
| 46 |
+ m_fileGameOverSound(dirBase.getFile("gameOverSound")),
|
|
| 44 | 47 |
m_stoneColor(), |
| 45 | 48 |
m_delay(c_delayDescr.default_), |
| 46 | 49 |
m_dropDelay(c_dropDelayDescr.default_), |
| 47 | 50 |
m_blinkDelay(c_blinkDelayDescr.default_), |
| 51 |
+ m_gameOverDelay(c_gameOverDelayDescr.default_), |
|
| 48 | 52 |
m_pConn(NULL), |
| 49 | 53 |
m_stone(-1), m_rot(-1), m_posX(-1), m_posY(-1), |
| 50 |
- m_dropping(false), m_blinking(0), |
|
| 54 |
+ m_dropping(false), m_blinking(0), m_completed(0), m_gameOver(0), |
|
| 51 | 55 |
m_field(), m_rowsBlink() |
| 52 | 56 |
{
|
| 53 | 57 |
// open operator connection interfaces for player |
| ... | ... |
@@ -86,9 +90,15 @@ bool Tetris::updateConfigGame() |
| 86 | 90 |
if (valueUpdate(m_fileBlinkDelay, c_blinkDelayDescr, m_blinkDelay)) {
|
| 87 | 91 |
ret = true; |
| 88 | 92 |
} |
| 93 |
+ if (valueUpdate(m_fileGameOverDelay, c_gameOverDelayDescr, |
|
| 94 |
+ m_gameOverDelay)) {
|
|
| 95 |
+ ret = true; |
|
| 96 |
+ } |
|
| 89 | 97 |
|
| 90 | 98 |
// sound name file was modified -> re-read sound name, no other update needed |
| 91 | 99 |
soundUpdate(m_fileStartSound); |
| 100 |
+ soundUpdate(m_fileRowCompleteSound); |
|
| 101 |
+ soundUpdate(m_fileGameOverSound); |
|
| 92 | 102 |
|
| 93 | 103 |
return ret; |
| 94 | 104 |
} |
| ... | ... |
@@ -151,8 +161,8 @@ void Tetris::opConnRecvKey(OpConn *pConn, char key) |
| 151 | 161 |
} |
| 152 | 162 |
|
| 153 | 163 |
/** normal keys for controlling game, |
| 154 |
- deactivated if dropping stone or rows blinking */ |
|
| 155 |
- if (m_dropping || m_blinking > 0) {
|
|
| 164 |
+ deactivated if dropping stone, rows blinking or end of game */ |
|
| 165 |
+ if (m_dropping || m_blinking > 0 || m_gameOver > 0) {
|
|
| 156 | 166 |
return; |
| 157 | 167 |
} |
| 158 | 168 |
|
| ... | ... |
@@ -254,6 +264,7 @@ void Tetris::reinitialize() |
| 254 | 264 |
valueFromFile(m_fileDelay, c_delayDescr, m_delay); |
| 255 | 265 |
valueFromFile(m_fileDropDelay, c_dropDelayDescr, m_dropDelay); |
| 256 | 266 |
valueFromFile(m_fileBlinkDelay, c_blinkDelayDescr, m_blinkDelay); |
| 267 |
+ valueFromFile(m_fileGameOverDelay, c_gameOverDelayDescr, m_gameOverDelay); |
|
| 257 | 268 |
|
| 258 | 269 |
// initialize field: empty |
| 259 | 270 |
m_field.clear(); |
| ... | ... |
@@ -263,6 +274,11 @@ void Tetris::reinitialize() |
| 263 | 274 |
m_rowsBlink.clear(); |
| 264 | 275 |
m_rowsBlink.resize(m_height, false); |
| 265 | 276 |
|
| 277 |
+ // no rows blinking or completed, game not over yet |
|
| 278 |
+ m_blinking = 0; |
|
| 279 |
+ m_completed = 0; |
|
| 280 |
+ m_gameOver = 0; |
|
| 281 |
+ |
|
| 266 | 282 |
// start with new stone |
| 267 | 283 |
newStone(); |
| 268 | 284 |
|
| ... | ... |
@@ -300,9 +316,43 @@ void Tetris::redraw() |
| 300 | 316 |
/// process next time step of game |
| 301 | 317 |
void Tetris::timeStep() |
| 302 | 318 |
{
|
| 319 |
+ // count time at end of game |
|
| 320 |
+ if (m_gameOver > 0) {
|
|
| 321 |
+ timeGameOver(); |
|
| 303 | 322 |
// blinking of completed rows |
| 304 |
- if (m_blinking > 0) {
|
|
| 323 |
+ } else if (m_blinking > 0) {
|
|
| 324 |
+ timeBlinkRows(); |
|
| 325 |
+ // falling stone |
|
| 326 |
+ } else {
|
|
| 327 |
+ timeStone(); |
|
| 328 |
+ } |
|
| 329 |
+ |
|
| 330 |
+ // request next time step |
|
| 331 |
+ planTimeStep(); |
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+/// count time at end of game |
|
| 335 |
+void Tetris::timeGameOver() |
|
| 336 |
+{
|
|
| 337 |
+ // count time |
|
| 338 |
+ ++m_gameOver; |
|
| 305 | 339 |
|
| 340 |
+ // terminate game after some time |
|
| 341 |
+ if (m_gameOver >= 8) {
|
|
| 342 |
+ // close operator connection |
|
| 343 |
+ if (m_pConn) {
|
|
| 344 |
+ forgetOpConn(m_pConn); // remove from requests (if it was in) |
|
| 345 |
+ m_pConn->close(); |
|
| 346 |
+ m_pConn = NULL; |
|
| 347 |
+ } |
|
| 348 |
+ // deactivate game |
|
| 349 |
+ deactivate(); |
|
| 350 |
+ } |
|
| 351 |
+} |
|
| 352 |
+ |
|
| 353 |
+/// blink completed rows |
|
| 354 |
+void Tetris::timeBlinkRows() |
|
| 355 |
+{
|
|
| 306 | 356 |
// blink rows |
| 307 | 357 |
++m_blinking; |
| 308 | 358 |
|
| ... | ... |
@@ -333,10 +383,11 @@ void Tetris::timeStep() |
| 333 | 383 |
|
| 334 | 384 |
// redraw image and send frame |
| 335 | 385 |
redraw(); |
| 386 |
+} |
|
| 336 | 387 |
|
| 337 |
- // falling stone |
|
| 338 |
- } else {
|
|
| 339 |
- |
|
| 388 |
+/// falling stone |
|
| 389 |
+void Tetris::timeStone() |
|
| 390 |
+{
|
|
| 340 | 391 |
// stone can move down by one pixel |
| 341 | 392 |
if (checkStoneFit(m_stone, m_rot, m_posY + 1, m_posX)) {
|
| 342 | 393 |
// move stone down by one pixel |
| ... | ... |
@@ -350,19 +401,27 @@ void Tetris::timeStep() |
| 350 | 401 |
// add stone permanently to field at current position |
| 351 | 402 |
freezeStone(m_stone, m_rot, m_posY, m_posX); |
| 352 | 403 |
drawStone(m_stone, m_rot, m_posY, m_posX); // TODO: frozen color |
| 404 |
+ |
|
| 405 |
+ // overflow of game field -> game over |
|
| 406 |
+ if (checkStoneOverflow(m_stone, m_rot, m_posY, m_posX)) {
|
|
| 407 |
+ // unset stone |
|
| 408 |
+ m_stone = -1; |
|
| 409 |
+ // game over |
|
| 410 |
+ m_gameOver = 1; |
|
| 411 |
+ playOpConnSound(m_pConn, m_fileGameOverSound); |
|
| 412 |
+ } |
|
| 413 |
+ |
|
| 414 |
+ // no overflow |
|
| 415 |
+ else {
|
|
| 353 | 416 |
// no current stone any more |
| 354 | 417 |
m_stone = -1; |
| 355 | 418 |
// check for completed rows, (afterwards: new stone) |
| 356 | 419 |
checkComplete(); |
| 357 | 420 |
} |
| 421 |
+ } |
|
| 358 | 422 |
|
| 359 | 423 |
// send updated image buffer as frame |
| 360 | 424 |
sendFrame(); |
| 361 |
- |
|
| 362 |
- } // falling stone |
|
| 363 |
- |
|
| 364 |
- // request next time step |
|
| 365 |
- planTimeStep(); |
|
| 366 | 425 |
} |
| 367 | 426 |
|
| 368 | 427 |
/// check for completed rows to disappear (new stone afterwards) |
| ... | ... |
@@ -391,6 +450,7 @@ void Tetris::checkComplete() |
| 391 | 450 |
// start blinking (start with rows visible, last bit == 0) |
| 392 | 451 |
else {
|
| 393 | 452 |
m_blinking = 2; |
| 453 |
+ playOpConnSound(m_pConn, m_fileRowCompleteSound); |
|
| 394 | 454 |
} |
| 395 | 455 |
} |
| 396 | 456 |
|
| ... | ... |
@@ -420,12 +480,17 @@ void Tetris::planTimeStep() |
| 420 | 480 |
|
| 421 | 481 |
// compute interval based on game state |
| 422 | 482 |
int interval_ms = m_delay; |
| 423 |
- if (m_dropping) {
|
|
| 424 |
- interval_ms = m_dropDelay; |
|
| 483 |
+ int speedup = m_completed; |
|
| 484 |
+ if (m_gameOver > 0) {
|
|
| 485 |
+ interval_ms = m_gameOverDelay; |
|
| 486 |
+ speedup = 0; |
|
| 425 | 487 |
} else if (m_blinking > 0) {
|
| 426 | 488 |
interval_ms = m_blinkDelay; |
| 489 |
+ } else if (m_dropping) {
|
|
| 490 |
+ interval_ms = m_dropDelay; |
|
| 427 | 491 |
} |
| 428 |
- float interval = 1e-3f * interval_ms; |
|
| 492 |
+ float scale = 0.3f + 0.7f * expf(-0.3f * speedup); |
|
| 493 |
+ float interval = 1e-3f * interval_ms * scale; |
|
| 429 | 494 |
|
| 430 | 495 |
// request next time call |
| 431 | 496 |
Time stepTime; |
| ... | ... |
@@ -471,6 +536,29 @@ bool Tetris::checkStoneFit(int stone, int rot, int y, int x) const |
| 471 | 536 |
return true; |
| 472 | 537 |
} |
| 473 | 538 |
|
| 539 |
+/// check if stone overflow game field |
|
| 540 |
+bool Tetris::checkStoneOverflow(int stone, int rot, int y, int x) const |
|
| 541 |
+{
|
|
| 542 |
+ // get rotation of stone |
|
| 543 |
+ RotStone const *rotStone = getRotStone(stone, rot); |
|
| 544 |
+ if (! rotStone) {
|
|
| 545 |
+ return true; // invalid stone or rotation -> overflow |
|
| 546 |
+ } |
|
| 547 |
+ |
|
| 548 |
+ // check pixels for overflow |
|
| 549 |
+ for (int p = 0; p < c_pixelCnt; ++p) {
|
|
| 550 |
+ int py = y + rotStone->pixels[p].y; |
|
| 551 |
+ int px = x + rotStone->pixels[p].x; |
|
| 552 |
+ if (! checkLimitInt(py, 0, m_height -1) || |
|
| 553 |
+ ! checkLimitInt(px, 0, m_width - 1)) {
|
|
| 554 |
+ return true; // outside field (including top) -> overflow |
|
| 555 |
+ } |
|
| 556 |
+ } |
|
| 557 |
+ |
|
| 558 |
+ // all checks passed -> no overflow |
|
| 559 |
+ return false; |
|
| 560 |
+} |
|
| 561 |
+ |
|
| 474 | 562 |
/// freeze stone to field at position |
| 475 | 563 |
void Tetris::freezeStone(int stone, int rot, int y, int x) |
| 476 | 564 |
{
|
| ... | ... |
@@ -592,5 +680,8 @@ Tetris::ValueDescr const Tetris::c_dropDelayDescr = { 100, 50, 250 };
|
| 592 | 680 |
/// descriptor for delay value during blinking of disappearing rows |
| 593 | 681 |
Tetris::ValueDescr const Tetris::c_blinkDelayDescr = { 50, 50, 250 };
|
| 594 | 682 |
|
| 683 |
+/// descriptor for delay value at end of game |
|
| 684 |
+Tetris::ValueDescr const Tetris::c_gameOverDelayDescr = { 2000, 100, 5000 };
|
|
| 685 |
+ |
|
| 595 | 686 |
} // namespace Blinker |
| 596 | 687 |
|
| ... | ... |
@@ -121,6 +121,15 @@ protected: |
| 121 | 121 |
/// process next time step of game |
| 122 | 122 |
virtual void timeStep(); |
| 123 | 123 |
|
| 124 |
+ /// count time at end of game |
|
| 125 |
+ void timeGameOver(); |
|
| 126 |
+ |
|
| 127 |
+ /// blink completed rows |
|
| 128 |
+ void timeBlinkRows(); |
|
| 129 |
+ |
|
| 130 |
+ /// falling stone |
|
| 131 |
+ void timeStone(); |
|
| 132 |
+ |
|
| 124 | 133 |
/// check for completed rows to disappear (new stone afterwards) |
| 125 | 134 |
void checkComplete(); |
| 126 | 135 |
|
| ... | ... |
@@ -136,6 +145,9 @@ protected: |
| 136 | 145 |
/// check if stone fits at position |
| 137 | 146 |
bool checkStoneFit(int stone, int rot, int y, int x) const; |
| 138 | 147 |
|
| 148 |
+ /// check if stone overflow game field |
|
| 149 |
+ bool checkStoneOverflow(int stone, int rot, int y, int x) const; |
|
| 150 |
+ |
|
| 139 | 151 |
/// freeze stone to field at position |
| 140 | 152 |
void freezeStone(int stone, int rot, int y, int x); |
| 141 | 153 |
|
| ... | ... |
@@ -165,17 +177,24 @@ protected: |
| 165 | 177 |
/// descriptor for delay value during blinking of disappearing rows |
| 166 | 178 |
static ValueDescr const c_blinkDelayDescr; |
| 167 | 179 |
|
| 180 |
+ /// descriptor for delay value at end of game |
|
| 181 |
+ static ValueDescr const c_gameOverDelayDescr; |
|
| 182 |
+ |
|
| 168 | 183 |
ColorFile m_fileStoneColor; ///< color file for stone color |
| 169 | 184 |
UIntFile m_fileDelay; ///< file for initial delay in ms per frame |
| 170 | 185 |
UIntFile m_fileDropDelay; ///< file for delay while dropping (ms/frame) |
| 171 | 186 |
UIntFile m_fileBlinkDelay; ///< file for delay while blinking (ms/frame) |
| 187 |
+ UIntFile m_fileGameOverDelay; ///< file for delay at end (ms/frame) |
|
| 172 | 188 |
|
| 173 | 189 |
NameFile m_fileStartSound; ///< "start game" sound name file |
| 190 |
+ NameFile m_fileRowCompleteSound; ///< "row complete" sound name file |
|
| 191 |
+ NameFile m_fileGameOverSound; ///< "game over" sound name file |
|
| 174 | 192 |
|
| 175 | 193 |
ColorData m_stoneColor; ///< stone color |
| 176 | 194 |
unsigned int m_delay; ///< initial delay in ms per frame |
| 177 | 195 |
unsigned int m_dropDelay; ///< delay while dropping in ms per frame |
| 178 |
- unsigned int m_blinkDelay; ///< delay while rwos blinking in ms per frame |
|
| 196 |
+ unsigned int m_blinkDelay; ///< delay while rows blinking in ms per frame |
|
| 197 |
+ unsigned int m_gameOverDelay; ///< delay at end of game in ms per frame |
|
| 179 | 198 |
|
| 180 | 199 |
OpConn *m_pConn; ///< operator connection of player (or NULL) |
| 181 | 200 |
|
| ... | ... |
@@ -185,6 +204,8 @@ protected: |
| 185 | 204 |
int m_posY; ///< y position of current stone |
| 186 | 205 |
bool m_dropping; ///< whether currently dropping a stone |
| 187 | 206 |
int m_blinking; ///< step of blinking: 0 not blinking, lsb == 0 -> visible |
| 207 |
+ int m_completed; ///< number of overall completed rows |
|
| 208 |
+ int m_gameOver; ///< counter at end of game: 0 means game is running |
|
| 188 | 209 |
|
| 189 | 210 |
/// tetris field (y * m_width + x), -1 for free, >= 0 for pixel from stone |
| 190 | 211 |
std::vector<int> m_field; |
| 191 | 212 |