pong: goals, score, speedup
Stefan Schuermans

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