1 /* pong, Copyright (c) 2003 Jeremy English <jenglish@myself.com>
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.
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
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
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:
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?
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
36 * There is some info at http://www.mameworld.net/discrete/Atari/Atari.htm#Pong
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.
43 #include "screenhack.h"
45 /* #define OUTPUT_POS */
47 typedef struct _paddle {
57 typedef struct _ball {
77 analogtv_reception reception;
85 analogtv_font score_font;
90 PONG_W = ANALOGTV_VIS_LEN,
91 PONG_H = ANALOGTV_VISLINES,
96 hit_top_bottom(struct state *st)
98 if ( (st->ball.y <= PONG_TMARG) ||
99 (st->ball.y+st->ball.h >= PONG_H) )
104 new_game(struct state *st)
106 /* Starts a Whole New Game*/
107 st->ball.x = PONG_W/2;
108 st->ball.y = PONG_H/2;
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);
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;
125 st->l_paddle.h = PONG_H/4;
126 st->r_paddle.h = PONG_H/4;
130 start_game(struct state *st)
133 st->ball.x = PONG_W/2;
134 st->ball.y = PONG_H/2;
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);
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;
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;
154 hit_paddle(struct state *st)
156 if ( st->ball.x + st->ball.w >= st->r_paddle.x &&
157 st->bx > 0 ) /*we are traveling to the right*/
159 if ((st->ball.y + st->ball.h > st->r_paddle.y) &&
160 (st->ball.y < st->r_paddle.y + st->r_paddle.h))
163 st->l_paddle.wait = 0;
164 st->r_paddle.wait = 1;
165 st->r_paddle.lock = 0;
166 st->l_paddle.lock = 0;
170 st->r_paddle.score++;
171 if (st->r_paddle.score >=10)
178 if (st->ball.x <= st->l_paddle.x + st->l_paddle.w &&
179 st->bx < 0 ) /*we are traveling to the left*/
181 if ( st->ball.y + st->ball.h > st->l_paddle.y &&
182 st->ball.y < st->l_paddle.y + st->l_paddle.h)
185 st->l_paddle.wait = 1;
186 st->r_paddle.wait = 0;
187 st->r_paddle.lock = 0;
188 st->l_paddle.lock = 0;
192 st->l_paddle.score++;
193 if (st->l_paddle.score >= 10)
202 pong_init (Display *dpy, Window window)
204 struct state *st = (struct state *) calloc (1, sizeof(*st));
207 st->tv=analogtv_allocate(st->dpy, st->window);
208 analogtv_set_defaults(st->tv, "");
210 analogtv_make_font(st->dpy, st->window, &st->score_font,
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',
222 analogtv_font_set_char(&st->score_font, '1',
229 analogtv_font_set_char(&st->score_font, '2',
236 analogtv_font_set_char(&st->score_font, '3',
243 analogtv_font_set_char(&st->score_font, '4',
250 analogtv_font_set_char(&st->score_font, '5',
257 analogtv_font_set_char(&st->score_font, '6',
264 analogtv_font_set_char(&st->score_font, '7',
271 analogtv_font_set_char(&st->score_font, '8',
278 analogtv_font_set_char(&st->score_font, '9',
286 st->score_font.y_mult *= 2;
287 st->score_font.x_mult *= 2;
290 printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H);
293 st->inp=analogtv_input_allocate();
294 analogtv_setup_sync(st->inp, 0, 0);
296 st->reception.input = st->inp;
297 st->reception.level = 2.0;
301 st->reception.multipath = frand(1.0);
304 st->reception.multipath=0.0;
311 st->l_paddle.y = 100;
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;
320 st->ball.x = PONG_W/2;
321 st->ball.y = PONG_H/2;
325 st->m_unit = get_integer_resource (st->dpy, "speed", "Integer");
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);
335 analogtv_draw_solid(st->inp,
336 ANALOGTV_VIS_START, ANALOGTV_VIS_END,
337 ANALOGTV_TOP, ANALOGTV_BOT,
344 p_logic(struct state *st, Paddle *p)
348 targ = st->ball.y + st->by * (st->r_paddle.x-st->ball.x) / st->bx;
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;
356 if (targ > PONG_H) targ=PONG_H;
357 if (targ < 0) targ=0;
359 if (targ < p->y && !p->lock)
361 p->y -= st->paddle_rate;
363 else if (targ > (p->y + p->h) && !p->lock)
365 p->y += st->paddle_rate;
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;
378 p_hit_top_bottom(Paddle *p)
380 if(p->y <= PONG_TMARG)
384 if((p->y + p->h) >= PONG_H)
386 p->y = PONG_H - p->h;
391 XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h);
394 XClearArea(dpy,window, p->x, p->y + p->h,
395 p->w, (old_v + p->h) - (p->y + p->h), 0);
397 else if (old_v < p->y)
399 XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0);
403 paint_paddle(struct state *st, Paddle *p)
405 analogtv_draw_solid(st->inp,
406 ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
407 ANALOGTV_TOP, ANALOGTV_BOT,
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,
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);
423 erase_ball(struct state *st)
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,
432 paint_ball(struct state *st)
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,
441 paint_score(struct state *st)
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,
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,
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,
463 paint_net(struct state *st)
467 x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2;
469 for (y=ANALOGTV_TOP; y<ANALOGTV_BOT; y+=6) {
470 analogtv_draw_solid(st->inp, x-2, x+2, y, y+3,
472 analogtv_draw_solid(st->inp, x-2, x+2, y+3, y+6,
479 pong_draw (Display *dpy, Window window, void *closure)
481 struct state *st = (struct state *) closure;
484 st->ball.x += st->bx;
485 st->ball.y += st->by;
487 if ((random()%40)==0) {
488 if (st->bx>0) st->bx++; else st->bx--;
491 if (!st->r_paddle.wait)
493 p_logic(st, &st->r_paddle);
495 if (!st->l_paddle.wait)
497 p_logic(st, &st->l_paddle);
500 p_hit_top_bottom(&st->r_paddle);
501 p_hit_top_bottom(&st->l_paddle);
507 printf("(%d,%d,%d,%d)\n",st->ball.x,st->ball.y,st->ball.w,st->ball.h);
515 paint_paddle(st, &st->r_paddle);
516 paint_paddle(st, &st->l_paddle);
518 if (1) paint_ball(st);
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);
530 static const char *pong_defaults [] = {
531 ".background: black",
532 ".foreground: white",
539 static XrmOptionDescRec pong_options [] = {
540 { "-speed", ".speed", XrmoptionSepArg, 0 },
546 pong_reshape (Display *dpy, Window window, void *closure,
547 unsigned int w, unsigned int h)
549 struct state *st = (struct state *) closure;
550 analogtv_reconfigure (st->tv);
554 pong_event (Display *dpy, Window window, void *closure, XEvent *event)
560 pong_free (Display *dpy, Window window, void *closure)
562 struct state *st = (struct state *) closure;
563 analogtv_release(st->tv);
567 XSCREENSAVER_MODULE ("Pong", pong)