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