Stefan Schuermans commited on 2019-06-15 21:28:51
Showing 2 changed files, with 95 additions and 5 deletions.
... | ... |
@@ -3,6 +3,7 @@ |
3 | 3 |
Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html |
4 | 4 |
a blinkenarea.org project */ |
5 | 5 |
|
6 |
+#include <cmath> |
|
6 | 7 |
#include <stdlib.h> |
7 | 8 |
#include <string> |
8 | 9 |
#include <vector> |
... | ... |
@@ -43,9 +44,10 @@ Pong::Pong(const std::string &name, Mgrs &mgrs, const Directory &dirBase): |
43 | 44 |
m_filePadColor(dirBase.getFile("padColor")), |
44 | 45 |
m_fileComputerColor(dirBase.getFile("computerColor")), |
45 | 46 |
m_ballColor(), m_lineColor(), m_padColor(), m_computerColor(), |
47 |
+ m_pConnLeft(NULL), m_pConnRight(NULL), |
|
46 | 48 |
m_ballPosX(-1), m_ballPosY(-1), m_ballDirX(0), m_ballDirY(0), |
47 | 49 |
m_padSize(0), m_leftPosY(0), m_rightPosY(0), m_leftDelay(0), m_rightDelay(0), |
48 |
- m_pConnLeft(NULL), m_pConnRight(NULL) |
|
50 |
+ m_goalDelay(0), m_bounceCnt(0), m_scoreLeft(0), m_scoreRight(0) |
|
49 | 51 |
{ |
50 | 52 |
// open operator connection interfaces for left and right player |
51 | 53 |
m_mgrs.m_opMgr.open(m_name + OpConnSuffixLeft, this); |
... | ... |
@@ -226,8 +228,10 @@ void Pong::redraw() |
226 | 228 |
lineVert(m_rightPosY, m_rightPosY + m_padSize - 1, m_width - 1, |
227 | 229 |
m_pConnRight ? m_padColor : m_computerColor); |
228 | 230 |
|
229 |
- // draw ball |
|
231 |
+ // draw ball (blinking if goal delay is active) |
|
232 |
+ if (m_goalDelay <= 0 || (m_goalDelay & 4) == 0) { |
|
230 | 233 |
pixel(m_ballPosY, m_ballPosX, m_ballColor); |
234 |
+ } |
|
231 | 235 |
|
232 | 236 |
// send updated image buffer as frame |
233 | 237 |
sendFrame(); |
... | ... |
@@ -236,6 +240,9 @@ void Pong::redraw() |
236 | 240 |
/// callback when requested time reached |
237 | 241 |
void Pong::timeCall() |
238 | 242 |
{ |
243 |
+ // game is running |
|
244 |
+ if (m_goalDelay <= 0 && m_ballPosX >= 0 && m_ballPosY >= 0) { |
|
245 |
+ |
|
239 | 246 |
// computer player: move pads |
240 | 247 |
if (! m_pConnLeft) { |
241 | 248 |
computerLeft(); |
... | ... |
@@ -251,6 +258,23 @@ void Pong::timeCall() |
251 | 258 |
m_ballPosX += m_ballDirX; |
252 | 259 |
m_ballPosY += m_ballDirY; |
253 | 260 |
|
261 |
+ // detect goal |
|
262 |
+ detectGoal(); |
|
263 |
+ |
|
264 |
+ } |
|
265 |
+ // goal delay (blinking ball) |
|
266 |
+ else if (m_goalDelay > 0) { |
|
267 |
+ |
|
268 |
+ --m_goalDelay; |
|
269 |
+ |
|
270 |
+ // new ball when delay is over |
|
271 |
+ if (m_goalDelay <= 0) { |
|
272 |
+ startBall(); |
|
273 |
+ return; // startBall calls redraw() and planTimeCall() |
|
274 |
+ } |
|
275 |
+ |
|
276 |
+ } |
|
277 |
+ |
|
254 | 278 |
// draw and send frame |
255 | 279 |
redraw(); |
256 | 280 |
|
... | ... |
@@ -265,6 +289,11 @@ void Pong::timeCall() |
265 | 289 |
*/ |
266 | 290 |
void Pong::processKey(char key, int &padPosY) |
267 | 291 |
{ |
292 |
+ // do not move pad if goal delay is active |
|
293 |
+ if (m_goalDelay > 0) { |
|
294 |
+ return; |
|
295 |
+ } |
|
296 |
+ |
|
268 | 297 |
// move pad (2 = up, 8 = down), do not move pad out of field |
269 | 298 |
if (key == '2' && padPosY > 0) { |
270 | 299 |
--padPosY; |
... | ... |
@@ -359,6 +388,11 @@ void Pong::computerComputePadPos(int padBallX, int padY, |
359 | 388 |
*/ |
360 | 389 |
void Pong::computerMovePad(int padYmin, int padYmax, int &padPosY) const |
361 | 390 |
{ |
391 |
+ // do not move pad if goal delay is active |
|
392 |
+ if (m_goalDelay > 0) { |
|
393 |
+ return; |
|
394 |
+ } |
|
395 |
+ |
|
362 | 396 |
// move pad, do not move pad out of field |
363 | 397 |
if (padPosY > padYmax && padPosY > 0) { |
364 | 398 |
--padPosY; |
... | ... |
@@ -425,18 +459,21 @@ void Pong::bounceBallLeft() |
425 | 459 |
if (m_ballPosY == m_leftPosY - 1 && m_ballDirY > 0) { |
426 | 460 |
m_ballDirX = 1; |
427 | 461 |
m_ballDirY = -1; |
462 |
+ ++m_bounceCnt; |
|
428 | 463 |
} |
429 | 464 |
|
430 | 465 |
// bottom corner |
431 | 466 |
else if (m_ballPosY == m_leftPosY + m_padSize && m_ballDirY < 0) { |
432 | 467 |
m_ballDirX = 1; |
433 | 468 |
m_ballDirY = 1; |
469 |
+ ++m_bounceCnt; |
|
434 | 470 |
} |
435 | 471 |
|
436 | 472 |
// pad edge |
437 | 473 |
else if (m_ballPosY >= m_leftPosY && |
438 | 474 |
m_ballPosY < m_leftPosY + m_padSize) { |
439 | 475 |
m_ballDirX = 1; |
476 |
+ ++m_bounceCnt; |
|
440 | 477 |
} |
441 | 478 |
} |
442 | 479 |
|
... | ... |
@@ -451,31 +488,59 @@ void Pong::bounceBallRight() |
451 | 488 |
if (m_ballPosY == m_rightPosY - 1 && m_ballDirY > 0) { |
452 | 489 |
m_ballDirX = -1; |
453 | 490 |
m_ballDirY = -1; |
491 |
+ ++m_bounceCnt; |
|
454 | 492 |
} |
455 | 493 |
|
456 | 494 |
// bottom corner |
457 | 495 |
else if (m_ballPosY == m_rightPosY + m_padSize && m_ballDirY < 0) { |
458 | 496 |
m_ballDirX = -1; |
459 | 497 |
m_ballDirY = 1; |
498 |
+ ++m_bounceCnt; |
|
460 | 499 |
} |
461 | 500 |
|
462 | 501 |
// pad edge |
463 | 502 |
else if (m_ballPosY >= m_rightPosY && |
464 | 503 |
m_ballPosY < m_rightPosY + m_padSize) { |
465 | 504 |
m_ballDirX = -1; |
505 |
+ ++m_bounceCnt; |
|
506 |
+ } |
|
507 |
+} |
|
508 |
+ |
|
509 |
+/// detect goal |
|
510 |
+void Pong::detectGoal() |
|
511 |
+{ |
|
512 |
+ static int goalBlinkCnt = 3; |
|
513 |
+ static int goalDelay = goalBlinkCnt * 8 + 3; |
|
514 |
+ |
|
515 |
+ // ball at far left - goal for right player |
|
516 |
+ if (m_ballPosX == 0) { |
|
517 |
+ m_goalDelay = goalDelay; |
|
518 |
+ ++m_scoreRight; |
|
519 |
+ } |
|
520 |
+ |
|
521 |
+ // ball at far right - goal for left player |
|
522 |
+ else if (m_ballPosX == m_width - 1) { |
|
523 |
+ m_goalDelay = goalDelay; |
|
524 |
+ ++m_scoreLeft; |
|
466 | 525 |
} |
467 | 526 |
} |
468 | 527 |
|
469 | 528 |
/// request next time call - or cancel request if not needed |
470 | 529 |
void Pong::planTimeCall() |
471 | 530 |
{ |
531 |
+ // no time call needed if game is not running |
|
472 | 532 |
if (m_ballPosX < 0 || m_ballPosY < 0) { |
473 | 533 |
m_mgrs.m_callMgr.cancelTimeCall(this); |
474 | 534 |
return; |
475 | 535 |
} |
476 | 536 |
|
537 |
+ // compute interval based on score and bounce count |
|
538 |
+ float speedup = 10 * (m_scoreLeft + m_scoreRight) + m_bounceCnt; |
|
539 |
+ float interval = 0.05f + 0.1f * expf(-0.03f * speedup); |
|
540 |
+ |
|
541 |
+ // request next time call |
|
477 | 542 |
Time stepTime; |
478 |
- stepTime.fromMs(100); |
|
543 |
+ stepTime.fromFloatSec(interval); |
|
479 | 544 |
m_mgrs.m_callMgr.requestTimeCall(this, Time::now() + stepTime); |
480 | 545 |
} |
481 | 546 |
|
... | ... |
@@ -487,6 +552,15 @@ void Pong::hideBall() |
487 | 552 |
m_ballDirX = 0; |
488 | 553 |
m_ballDirY = 0; |
489 | 554 |
|
555 |
+ // no delays, ball has not bounced at pad |
|
556 |
+ m_leftDelay = 0; |
|
557 |
+ m_rightDelay = 0; |
|
558 |
+ m_goalDelay = 0; |
|
559 |
+ m_bounceCnt = 0; |
|
560 |
+ |
|
561 |
+ // draw and send frame |
|
562 |
+ redraw(); |
|
563 |
+ |
|
490 | 564 |
// update time call request |
491 | 565 |
planTimeCall(); |
492 | 566 |
} |
... | ... |
@@ -501,6 +575,15 @@ void Pong::startBall() |
501 | 575 |
m_ballDirX = (rand() & 1) * 2 - 1; |
502 | 576 |
m_ballDirY = (rand() & 1) * 2 - 1; |
503 | 577 |
|
578 |
+ // no delays, ball has not bounced at pad |
|
579 |
+ m_leftDelay = 0; |
|
580 |
+ m_rightDelay = 0; |
|
581 |
+ m_goalDelay = 0; |
|
582 |
+ m_bounceCnt = 0; |
|
583 |
+ |
|
584 |
+ // draw and send frame |
|
585 |
+ redraw(); |
|
586 |
+ |
|
504 | 587 |
// request first time call if needed |
505 | 588 |
planTimeCall(); |
506 | 589 |
} |
... | ... |
@@ -152,6 +152,9 @@ protected: |
152 | 152 |
/// bounce ball at right pad |
153 | 153 |
void bounceBallRight(); |
154 | 154 |
|
155 |
+ /// detect goal |
|
156 |
+ void detectGoal(); |
|
157 |
+ |
|
155 | 158 |
/// request next time call - or cancel request if not needed |
156 | 159 |
void planTimeCall(); |
157 | 160 |
|
... | ... |
@@ -176,6 +179,8 @@ protected: |
176 | 179 |
ColorData m_lineColor; ///< center line color |
177 | 180 |
ColorData m_padColor; ///< phone player pad color |
178 | 181 |
ColorData m_computerColor; ///< computer player pad color |
182 |
+ OpConn *m_pConnLeft; ///< operator connection left player (or NULL) |
|
183 |
+ OpConn *m_pConnRight; ///< operator connection right player (o NULL) |
|
179 | 184 |
int m_ballPosX; ///< ball position X |
180 | 185 |
int m_ballPosY; ///< ball position Y |
181 | 186 |
int m_ballDirX; ///< ball direction X |
... | ... |
@@ -185,8 +190,10 @@ protected: |
185 | 190 |
int m_rightPosY; ///< position of top pixel of right pad |
186 | 191 |
int m_leftDelay; ///< delay for computer moving left pad |
187 | 192 |
int m_rightDelay; ///< delay for computer moving right pad |
188 |
- OpConn *m_pConnLeft; ///< operator connection left player (or NULL) |
|
189 |
- OpConn *m_pConnRight; ///< operator connection right player (o NULL) |
|
193 |
+ int m_goalDelay; ///< delay after goal (blinking ball) |
|
194 |
+ int m_bounceCnt; ///< how often the current ball bounced at pad |
|
195 |
+ int m_scoreLeft; ///< score of left player |
|
196 |
+ int m_scoreRight; ///< score of right player |
|
190 | 197 |
}; // class Canvas |
191 | 198 |
|
192 | 199 |
} // namespace Blinker |
193 | 200 |