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 |