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