http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / pong.c
1 /* pong, Copyright (c) 2003 Jeremy English <jenglish@myself.com>
2  * A pong screen saver
3  *
4  * Modified by Brian Sawicki <sawicki@imsa.edu> to fix a small bug.
5  * Before this fix after a certain point the paddles would be too
6  * small for the program to effectively hit the ball.  The score would
7  * then skyrocket as the paddles missed most every time. Added a max
8  * so that once a paddle gets 10 the entire game restarts.  Special
9  * thanks to Scott Zager for some help on this.
10  *
11  * Modified by Trevor Blackwell <tlb@tlb.org> to use analogtv.[ch] display.
12  * Also added gradual acceleration of the ball, shrinking of paddles, and
13  * scorekeeping.
14  *
15  * Permission to use, copy, modify, distribute, and sell this software and its
16  * documentation for any purpose is hereby granted without fee, provided that
17  * the above copyright notice appear in all copies and that both that
18  * copyright notice and this permission notice appear in supporting
19  * documentation.  No representations are made about the suitability of this
20  * software for any purpose.  It is provided "as is" without express or
21  * implied warranty.
22  */
23
24 /*
25  * TLB sez: I haven't actually seen a pong game since I was about 9. Can
26  * someone who has one make this look more realistic? Issues:
27  *
28  *  - the font for scores is wrong. For example '0' was square.
29  *  - was there some kind of screen display when someone won?
30  *  - did the ball move smoothly, or was the X or Y position quantized?
31  *
32  * It could also use better player logic: moving the paddle even when the ball
33  * is going away, and making mistakes instead of just not keeping up with the
34  * speeding ball.
35  *
36  * There is some info at http://www.mameworld.net/discrete/Atari/Atari.htm#Pong
37  *
38  * It says that the original Pong game did not have a microprocessor, or even a
39  * custom integrated circuit. It was all discrete logic.
40  *
41  */
42
43 #include "screenhack.h"
44 #include "analogtv.h"
45 /* #define OUTPUT_POS */
46
47 typedef struct _paddle {
48   int x;
49   int y;
50   int w;
51   int h;
52   int wait;
53   int lock;
54   int score;
55 } Paddle;
56
57 typedef struct _ball {
58   int x;
59   int y;
60   int w;
61   int h;
62 } Ball;
63
64 Paddle l_paddle;
65 Paddle r_paddle;
66 Ball ball;
67 static int bx,by;
68 static int m_unit;
69 static int paddle_rate;
70
71 static analogtv *tv;
72 static analogtv_input *inp;
73 static analogtv_reception reception;
74
75 static int paddle_ntsc[4];
76 static int field_ntsc[4];
77 static int ball_ntsc[4];
78 static int score_ntsc[4];
79 static int net_ntsc[4];
80
81 analogtv_font score_font;
82
83 enum {
84   PONG_W = ANALOGTV_VIS_LEN,
85   PONG_H = ANALOGTV_VISLINES,
86   PONG_TMARG = 10
87 };
88
89 static void
90 hit_top_bottom(void)
91 {
92   if ( (ball.y <= PONG_TMARG) ||
93        (ball.y+ball.h >= PONG_H) )
94     by=-by;
95 }
96
97 void
98 new_game(void)
99 {
100   /* Starts a Whole New Game*/
101   ball.x = PONG_W/2;
102   ball.y = PONG_H/2;
103   bx = m_unit;
104   by = m_unit;
105
106   l_paddle.wait = 1;
107   l_paddle.lock = 0;
108   r_paddle.wait = 0;
109   r_paddle.lock = 0;
110   paddle_rate = m_unit-1;
111   r_paddle.score = 0;
112   l_paddle.score = 0;
113
114   l_paddle.h = PONG_H/4;
115   r_paddle.h = PONG_H/4;
116 }
117
118 void
119 start_game(void)
120 {
121   /*Init the ball*/
122   ball.x = PONG_W/2;
123   ball.y = PONG_H/2;
124   bx = m_unit;
125   by = m_unit;
126
127   l_paddle.wait = 1;
128   l_paddle.lock = 0;
129   r_paddle.wait = 0;
130   r_paddle.lock = 0;
131   paddle_rate = m_unit-1;
132
133   if (l_paddle.h > 10) l_paddle.h= l_paddle.h*19/20;
134   if (r_paddle.h > 10) r_paddle.h= r_paddle.h*19/20;
135 }
136
137 static void
138 hit_paddle(void)
139 {
140   if ( ball.x + ball.w >= r_paddle.x &&
141        bx > 0 ) /*we are traveling to the right*/
142     {
143       if ((ball.y + ball.h > r_paddle.y) &&
144           (ball.y < r_paddle.y + r_paddle.h))
145         {
146           bx=-bx;
147           l_paddle.wait = 0;
148           r_paddle.wait = 1;
149           r_paddle.lock = 0;
150           l_paddle.lock = 0;
151         }
152       else
153         {
154           r_paddle.score++;
155           if (r_paddle.score >=10)
156                 new_game();
157           else 
158           start_game();
159         }
160     }
161
162   if (ball.x <= l_paddle.x + l_paddle.w &&
163       bx < 0 ) /*we are traveling to the left*/
164     {
165       if ( ball.y + ball.h > l_paddle.y &&
166            ball.y < l_paddle.y + l_paddle.h)
167         {
168           bx=-bx;
169           l_paddle.wait = 1;
170           r_paddle.wait = 0;
171           r_paddle.lock = 0;
172           l_paddle.lock = 0;
173         }
174       else
175         {
176           l_paddle.score++;
177           if (l_paddle.score >= 10)
178                 new_game();
179           else
180           start_game();
181         }
182     }
183 }
184
185 static void
186 init_pong (Display *dpy, Window window)
187 {
188   tv=analogtv_allocate(dpy, window);
189   analogtv_set_defaults(tv, "");
190   tv->event_handler = screenhack_handle_event;
191
192   analogtv_make_font(dpy, window, &score_font,
193                      4, 6, NULL );
194
195   /* If you think we haven't learned anything since the early 70s,
196      look at this font for a while */
197   analogtv_font_set_char(&score_font, '0',
198                         "****"
199                         "*  *"
200                         "*  *"
201                         "*  *"
202                         "*  *"
203                         "****");
204   analogtv_font_set_char(&score_font, '1',
205                         "   *"
206                         "   *"
207                         "   *"
208                         "   *"
209                         "   *"
210                         "   *");
211   analogtv_font_set_char(&score_font, '2',
212                         "****"
213                         "   *"
214                         "****"
215                         "*   "
216                         "*   "
217                         "****");
218   analogtv_font_set_char(&score_font, '3',
219                         "****"
220                         "   *"
221                         "****"
222                         "   *"
223                         "   *"
224                         "****");
225   analogtv_font_set_char(&score_font, '4',
226                         "*  *"
227                         "*  *"
228                         "****"
229                         "   *"
230                         "   *"
231                         "   *");
232   analogtv_font_set_char(&score_font, '5',
233                         "****"
234                         "*   "
235                         "****"
236                         "   *"
237                         "   *"
238                         "****");
239   analogtv_font_set_char(&score_font, '6',
240                         "****"
241                         "*   "
242                         "****"
243                         "*  *"
244                         "*  *"
245                         "****");
246   analogtv_font_set_char(&score_font, '7',
247                         "****"
248                         "   *"
249                         "   *"
250                         "   *"
251                         "   *"
252                         "   *");
253   analogtv_font_set_char(&score_font, '8',
254                         "****"
255                         "*  *"
256                         "****"
257                         "*  *"
258                         "*  *"
259                         "****");
260   analogtv_font_set_char(&score_font, '9',
261                         "****"
262                         "*  *"
263                         "****"
264                         "   *"
265                         "   *"
266                         "   *");
267
268   score_font.y_mult *= 2;
269   score_font.x_mult *= 2;
270
271 #ifdef OUTPUT_POS
272   printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H);
273 #endif
274
275   inp=analogtv_input_allocate();
276   analogtv_setup_sync(inp, 0, 0);
277
278   reception.input = inp;
279   reception.level = 2.0;
280   reception.ofs=0;
281 #if 0
282   if (random()) {
283     reception.multipath = frand(1.0);
284   } else {
285 #endif
286     reception.multipath=0.0;
287 #if 0
288   }
289 #endif
290
291   /*Init the paddles*/
292   l_paddle.x = 8;
293   l_paddle.y = 100;
294   l_paddle.w = 16;
295   l_paddle.h = PONG_H/4;
296   l_paddle.wait = 1;
297   l_paddle.lock = 0;
298   r_paddle = l_paddle;
299   r_paddle.x = PONG_W - 8 - r_paddle.w;
300   r_paddle.wait = 0;
301   /*Init the ball*/
302   ball.x = PONG_W/2;
303   ball.y = PONG_H/2;
304   ball.w = 16;
305   ball.h = 8;
306
307   m_unit = get_integer_resource ("speed", "Integer");
308
309   start_game();
310
311   analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, field_ntsc);
312   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, ball_ntsc);
313   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, paddle_ntsc);
314   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, score_ntsc);
315   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, net_ntsc);
316
317   analogtv_draw_solid(inp,
318                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
319                       ANALOGTV_TOP, ANALOGTV_BOT,
320                       field_ntsc);
321 }
322
323 static void
324 p_logic(Paddle *p)
325 {
326   int targ;
327   if (bx > 0) {
328     targ = ball.y + by * (r_paddle.x-ball.x) / bx;
329   }
330   else if (bx < 0) {
331     targ = ball.y - by * (ball.x - l_paddle.x - l_paddle.w) / bx;
332   }
333   else {
334     targ = ball.y;
335   }
336   if (targ > PONG_H) targ=PONG_H;
337   if (targ < 0) targ=0;
338
339   if (targ < p->y && !p->lock)
340   {
341     p->y -= paddle_rate;
342   }
343   else if (targ > (p->y + p->h) && !p->lock)
344   {
345     p->y += paddle_rate;
346   }
347   else
348   {
349     int move=targ - (p->y + p->h/2);
350     if (move>paddle_rate) move=paddle_rate;
351     if (move<-paddle_rate) move=-paddle_rate;
352     p->y += move;
353     p->lock = 1;
354   }
355 }
356
357 static void
358 p_hit_top_bottom(Paddle *p)
359 {
360   if(p->y <= PONG_TMARG)
361   {
362     p->y = PONG_TMARG;
363   }
364   if((p->y + p->h) >= PONG_H)
365   {
366     p->y = PONG_H - p->h;
367   }
368 }
369
370 /*
371   XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h);
372   if (old_v > p->y)
373   {
374     XClearArea(dpy,window, p->x, p->y + p->h,
375       p->w, (old_v + p->h) - (p->y + p->h), 0);
376   }
377   else if (old_v < p->y)
378   {
379     XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0);
380   }
381 */
382 static void
383 paint_paddle(analogtv_input *inp, Paddle *p)
384 {
385   analogtv_draw_solid(inp,
386                       ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
387                       ANALOGTV_TOP, ANALOGTV_BOT,
388                       field_ntsc);
389
390   analogtv_draw_solid(inp,
391                       ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
392                       ANALOGTV_TOP + p->y, ANALOGTV_TOP + p->y + p->h,
393                       paddle_ntsc);
394 }
395
396 /*
397   XClearArea(dpy,window, old_ballx, old_bally, ball.d, ball.d, 0);
398   XFillRectangle (dpy, window, gc, ball.x, ball.y, ball.d, ball.d);
399   XFillRectangle (dpy, window, gc, xgwa.width / 2, 0, ball.d, xgwa.height);
400 */
401
402 static void
403 erase_ball(analogtv_input *inp)
404 {
405   analogtv_draw_solid(inp,
406                       ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
407                       ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
408                       field_ntsc);
409 }
410
411 static void
412 paint_ball(analogtv_input *inp)
413 {
414   analogtv_draw_solid(inp,
415                       ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
416                       ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
417                       ball_ntsc);
418 }
419
420 static void
421 paint_score(analogtv_input *inp)
422 {
423   char buf[256];
424
425   analogtv_draw_solid(inp,
426                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
427                       ANALOGTV_TOP, ANALOGTV_TOP + 10+ score_font.char_h * score_font.y_mult,
428                       field_ntsc);
429
430   sprintf(buf, "%d",r_paddle.score%256);
431   analogtv_draw_string(inp, &score_font, buf,
432                        ANALOGTV_VIS_START + 130, ANALOGTV_TOP + 8,
433                        score_ntsc);
434
435   sprintf(buf, "%d",l_paddle.score%256);
436   analogtv_draw_string(inp, &score_font, buf,
437                        ANALOGTV_VIS_END - 200, ANALOGTV_TOP + 8,
438                        score_ntsc);
439
440 }
441
442 static void
443 paint_net(analogtv_input *inp)
444 {
445   int x,y;
446
447   x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2;
448
449   for (y=ANALOGTV_TOP; y<ANALOGTV_BOT; y+=6) {
450     analogtv_draw_solid(inp, x-2, x+2, y, y+3,
451                         net_ntsc);
452     analogtv_draw_solid(inp, x-2, x+2, y+3, y+6,
453                         field_ntsc);
454
455   }
456 }
457
458 static void
459 play_pong (void)
460 {
461   erase_ball(inp);
462
463   ball.x += bx;
464   ball.y += by;
465
466   if ((random()%40)==0) {
467     if (bx>0) bx++; else bx--;
468   }
469
470   if (!r_paddle.wait)
471   {
472     p_logic(&r_paddle);
473   }
474   if (!l_paddle.wait)
475   {
476     p_logic(&l_paddle);
477   }
478
479   p_hit_top_bottom(&r_paddle);
480   p_hit_top_bottom(&l_paddle);
481
482   hit_top_bottom();
483   hit_paddle();
484
485   #ifdef OUTPUT_POS
486   printf("(%d,%d,%d,%d)\n",ball.x,ball.y,ball.w,ball.h);
487   #endif
488
489   paint_score(inp);
490
491   paint_net(inp);
492
493   if (1) {
494     paint_paddle(inp, &r_paddle);
495     paint_paddle(inp, &l_paddle);
496   }
497   if (1) paint_ball(inp);
498
499   analogtv_handle_events(tv);
500
501   analogtv_init_signal(tv, 0.04);
502   analogtv_reception_update(&reception);
503   analogtv_add_signal(tv, &reception);
504   analogtv_draw(tv);
505 }
506
507 \f
508 char *progclass = "pong";
509
510 char *defaults [] = {
511   ".background: black",
512   ".foreground: white",
513   "*speed:      6",
514   ANALOGTV_DEFAULTS
515   "*TVContrast:      150",
516   0
517 };
518
519 XrmOptionDescRec options [] = {
520   { "-percent",         ".percent",     XrmoptionSepArg, 0 },
521   { "-speed",           ".speed",     XrmoptionSepArg, 0 },
522   ANALOGTV_OPTIONS
523   { 0, 0, 0, 0 }
524 };
525
526 void
527 screenhack (Display *dpy, Window window)
528 {
529   init_pong (dpy, window);
530   while (1)
531     {
532       play_pong ();
533     }
534 }
535