http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.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 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 static int delay;
58 Paddle l_paddle;
59 Paddle r_paddle;
60 Ball ball;
61 static int bx,by;
62 static int m_unit;
63 static int paddle_rate;
64
65 static analogtv *tv;
66 static analogtv_input *inp;
67 static analogtv_reception reception;
68
69 static int paddle_ntsc[4];
70 static int field_ntsc[4];
71 static int ball_ntsc[4];
72 static int score_ntsc[4];
73 static int net_ntsc[4];
74
75 analogtv_font score_font;
76
77 enum {
78   PONG_W = ANALOGTV_VIS_LEN,
79   PONG_H = ANALOGTV_VISLINES,
80   PONG_TMARG = 10
81 };
82
83 static void
84 hit_top_bottom(void)
85 {
86   if ( (ball.y <= PONG_TMARG) ||
87        (ball.y+ball.h >= PONG_H) )
88     by=-by;
89 }
90
91 void
92 start_game(void)
93 {
94   /*Init the ball*/
95   ball.x = PONG_W/2;
96   ball.y = PONG_H/2;
97   bx = m_unit;
98   by = m_unit;
99
100   l_paddle.wait = 1;
101   l_paddle.lock = 0;
102   r_paddle.wait = 0;
103   r_paddle.lock = 0;
104   paddle_rate = m_unit-1;
105
106   if (l_paddle.h > 10) l_paddle.h= l_paddle.h*19/20;
107   if (r_paddle.h > 10) r_paddle.h= r_paddle.h*19/20;
108 }
109
110 static void
111 hit_paddle(void)
112 {
113   if ( ball.x + ball.w >= r_paddle.x &&
114        bx > 0 ) /*we are traveling to the right*/
115     {
116       if ((ball.y + ball.h > r_paddle.y) &&
117           (ball.y < r_paddle.y + r_paddle.h))
118         {
119           bx=-bx;
120           l_paddle.wait = 0;
121           r_paddle.wait = 1;
122           r_paddle.lock = 0;
123           l_paddle.lock = 0;
124         }
125       else
126         {
127           r_paddle.score++;
128           start_game();
129         }
130     }
131
132   if (ball.x <= l_paddle.x + l_paddle.w &&
133       bx < 0 ) /*we are traveling to the left*/
134     {
135       if ( ball.y + ball.h > l_paddle.y &&
136            ball.y < l_paddle.y + l_paddle.h)
137         {
138           bx=-bx;
139           l_paddle.wait = 1;
140           r_paddle.wait = 0;
141           r_paddle.lock = 0;
142           l_paddle.lock = 0;
143         }
144       else
145         {
146           l_paddle.score++;
147           start_game();
148         }
149     }
150 }
151
152 static void
153 init_pong (Display *dpy, Window window)
154 {
155   tv=analogtv_allocate(dpy, window);
156   analogtv_set_defaults(tv, "");
157   tv->event_handler = screenhack_handle_event;
158
159   analogtv_make_font(dpy, window, &score_font,
160                      4, 6, NULL );
161
162   /* If you think we haven't learned anything since the early 70s,
163      look at this font for a while */
164   analogtv_font_set_char(&score_font, '0',
165                         "****"
166                         "*  *"
167                         "*  *"
168                         "*  *"
169                         "*  *"
170                         "****");
171   analogtv_font_set_char(&score_font, '1',
172                         "   *"
173                         "   *"
174                         "   *"
175                         "   *"
176                         "   *"
177                         "   *");
178   analogtv_font_set_char(&score_font, '2',
179                         "****"
180                         "   *"
181                         "****"
182                         "*   "
183                         "*   "
184                         "****");
185   analogtv_font_set_char(&score_font, '3',
186                         "****"
187                         "   *"
188                         "****"
189                         "   *"
190                         "   *"
191                         "****");
192   analogtv_font_set_char(&score_font, '4',
193                         "*  *"
194                         "*  *"
195                         "****"
196                         "   *"
197                         "   *"
198                         "   *");
199   analogtv_font_set_char(&score_font, '5',
200                         "****"
201                         "*   "
202                         "****"
203                         "   *"
204                         "   *"
205                         "****");
206   analogtv_font_set_char(&score_font, '6',
207                         "****"
208                         "*   "
209                         "****"
210                         "*  *"
211                         "*  *"
212                         "****");
213   analogtv_font_set_char(&score_font, '7',
214                         "****"
215                         "   *"
216                         "   *"
217                         "   *"
218                         "   *"
219                         "   *");
220   analogtv_font_set_char(&score_font, '8',
221                         "****"
222                         "*  *"
223                         "****"
224                         "*  *"
225                         "*  *"
226                         "****");
227   analogtv_font_set_char(&score_font, '9',
228                         "****"
229                         "*  *"
230                         "****"
231                         "   *"
232                         "   *"
233                         "   *");
234
235   score_font.y_mult *= 2;
236   score_font.x_mult *= 2;
237
238 #ifdef OUTPUT_POS
239   printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H);
240 #endif
241
242   inp=analogtv_input_allocate();
243   analogtv_setup_sync(inp, 0, 0);
244
245   reception.input = inp;
246   reception.level = 2.0;
247   reception.ofs=0;
248 #if 0
249   if (random()) {
250     reception.multipath = frand(1.0);
251   } else {
252 #endif
253     reception.multipath=0.0;
254 #if 0
255   }
256 #endif
257
258   delay = get_integer_resource ("delay", "Integer");
259   if (delay < 0) delay = 0;
260
261   /*Init the paddles*/
262   l_paddle.x = 8;
263   l_paddle.y = 100;
264   l_paddle.w = 16;
265   l_paddle.h = PONG_H/4;
266   l_paddle.wait = 1;
267   l_paddle.lock = 0;
268   r_paddle = l_paddle;
269   r_paddle.x = PONG_W - 8 - r_paddle.w;
270   r_paddle.wait = 0;
271   /*Init the ball*/
272   ball.x = PONG_W/2;
273   ball.y = PONG_H/2;
274   ball.w = 16;
275   ball.h = 8;
276
277   m_unit = get_integer_resource ("speed", "Integer");
278
279   start_game();
280
281   analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, field_ntsc);
282   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, ball_ntsc);
283   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, paddle_ntsc);
284   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, score_ntsc);
285   analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, net_ntsc);
286
287   analogtv_draw_solid(inp,
288                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
289                       ANALOGTV_TOP, ANALOGTV_BOT,
290                       field_ntsc);
291 }
292
293 static void
294 p_logic(Paddle *p)
295 {
296   int targ;
297   if (bx > 0) {
298     targ = ball.y + by * (r_paddle.x-ball.x) / bx;
299   }
300   else if (bx < 0) {
301     targ = ball.y - by * (ball.x - l_paddle.x - l_paddle.w) / bx;
302   }
303   else {
304     targ = ball.y;
305   }
306   if (targ > PONG_H) targ=PONG_H;
307   if (targ < 0) targ=0;
308
309   if (targ < p->y && !p->lock)
310   {
311     p->y -= paddle_rate;
312   }
313   else if (targ > (p->y + p->h) && !p->lock)
314   {
315     p->y += paddle_rate;
316   }
317   else
318   {
319     int move=targ - (p->y + p->h/2);
320     if (move>paddle_rate) move=paddle_rate;
321     if (move<-paddle_rate) move=-paddle_rate;
322     p->y += move;
323     p->lock = 1;
324   }
325 }
326
327 static void
328 p_hit_top_bottom(Paddle *p)
329 {
330   if(p->y <= PONG_TMARG)
331   {
332     p->y = PONG_TMARG;
333   }
334   if((p->y + p->h) >= PONG_H)
335   {
336     p->y = PONG_H - p->h;
337   }
338 }
339
340 /*
341   XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h);
342   if (old_v > p->y)
343   {
344     XClearArea(dpy,window, p->x, p->y + p->h,
345       p->w, (old_v + p->h) - (p->y + p->h), 0);
346   }
347   else if (old_v < p->y)
348   {
349     XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0);
350   }
351 */
352 static void
353 paint_paddle(analogtv_input *inp, Paddle *p)
354 {
355   analogtv_draw_solid(inp,
356                       ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
357                       ANALOGTV_TOP, ANALOGTV_BOT,
358                       field_ntsc);
359
360   analogtv_draw_solid(inp,
361                       ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
362                       ANALOGTV_TOP + p->y, ANALOGTV_TOP + p->y + p->h,
363                       paddle_ntsc);
364 }
365
366 /*
367   XClearArea(dpy,window, old_ballx, old_bally, ball.d, ball.d, 0);
368   XFillRectangle (dpy, window, gc, ball.x, ball.y, ball.d, ball.d);
369   XFillRectangle (dpy, window, gc, xgwa.width / 2, 0, ball.d, xgwa.height);
370 */
371
372 static void
373 erase_ball(analogtv_input *inp)
374 {
375   analogtv_draw_solid(inp,
376                       ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
377                       ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
378                       field_ntsc);
379 }
380
381 static void
382 paint_ball(analogtv_input *inp)
383 {
384   analogtv_draw_solid(inp,
385                       ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
386                       ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
387                       ball_ntsc);
388 }
389
390 static void
391 paint_score(analogtv_input *inp)
392 {
393   char buf[256];
394
395   analogtv_draw_solid(inp,
396                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
397                       ANALOGTV_TOP, ANALOGTV_TOP + 10+ score_font.char_h * score_font.y_mult,
398                       field_ntsc);
399
400   sprintf(buf, "%d",r_paddle.score%256);
401   analogtv_draw_string(inp, &score_font, buf,
402                        ANALOGTV_VIS_START + 130, ANALOGTV_TOP + 8,
403                        score_ntsc);
404
405   sprintf(buf, "%d",l_paddle.score%256);
406   analogtv_draw_string(inp, &score_font, buf,
407                        ANALOGTV_VIS_END - 200, ANALOGTV_TOP + 8,
408                        score_ntsc);
409
410 }
411
412 static void
413 paint_net(analogtv_input *inp)
414 {
415   int x,y;
416
417   x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2;
418
419   for (y=ANALOGTV_TOP; y<ANALOGTV_BOT; y+=6) {
420     analogtv_draw_solid(inp, x-2, x+2, y, y+3,
421                         net_ntsc);
422     analogtv_draw_solid(inp, x-2, x+2, y+3, y+6,
423                         field_ntsc);
424
425   }
426 }
427
428 static void
429 play_pong (void)
430 {
431   erase_ball(inp);
432
433   ball.x += bx;
434   ball.y += by;
435
436   if ((random()%40)==0) {
437     if (bx>0) bx++; else bx--;
438   }
439
440   if (!r_paddle.wait)
441   {
442     p_logic(&r_paddle);
443   }
444   if (!l_paddle.wait)
445   {
446     p_logic(&l_paddle);
447   }
448
449   p_hit_top_bottom(&r_paddle);
450   p_hit_top_bottom(&l_paddle);
451
452   hit_top_bottom();
453   hit_paddle();
454
455   #ifdef OUTPUT_POS
456   printf("(%d,%d,%d,%d)\n",ball.x,ball.y,ball.w,ball.h);
457   #endif
458
459   paint_score(inp);
460
461   paint_net(inp);
462
463   if (1) {
464     paint_paddle(inp, &r_paddle);
465     paint_paddle(inp, &l_paddle);
466   }
467   if (1) paint_ball(inp);
468
469   analogtv_handle_events(tv);
470
471   analogtv_init_signal(tv, 0.04);
472   analogtv_reception_update(&reception);
473   analogtv_add_signal(tv, &reception);
474   analogtv_draw(tv);
475 }
476
477 \f
478 char *progclass = "pong";
479
480 char *defaults [] = {
481   "*delay:      10000",
482   "*speed:      6",
483   ANALOGTV_DEFAULTS
484   "*TVContrast:      150",
485   0
486 };
487
488 XrmOptionDescRec options [] = {
489   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
490   { "-percent",         ".percent",     XrmoptionSepArg, 0 },
491   { "-speed",           ".speed",     XrmoptionSepArg, 0 },
492   ANALOGTV_OPTIONS
493   { 0, 0, 0, 0 }
494 };
495
496 void
497 screenhack (Display *dpy, Window window)
498 {
499   init_pong (dpy, window);
500   while (1)
501     {
502       play_pong ();
503     }
504 }
505