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 * Modified by Gereon Steffens <gereon@steffens.org> to add -clock and -noise
16 * options. See http://www.burovormkrijgers.nl (ugly flash site,
17 * navigate to Portfolio/Browse/Misc/Pong Clock) for the hardware implementation
18 * that gave me the idea. In clock mode, the score reflects the current time, and
19 * the paddles simply stop moving when it's time for the other side to score. This
20 * means that the display is only updated a few seconds *after* the minute actually
21 * changes, but I think this fuzzyness fits well with the display, and since we're
22 * not displaying seconds, who cares. While I was at it, I added a -noise option
23 * to control the noisyness of the display.
25 * Permission to use, copy, modify, distribute, and sell this software and its
26 * documentation for any purpose is hereby granted without fee, provided that
27 * the above copyright notice appear in all copies and that both that
28 * copyright notice and this permission notice appear in supporting
29 * documentation. No representations are made about the suitability of this
30 * software for any purpose. It is provided "as is" without express or
35 * TLB sez: I haven't actually seen a pong game since I was about 9. Can
36 * someone who has one make this look more realistic? Issues:
38 * - the font for scores is wrong. For example '0' was square.
39 * - was there some kind of screen display when someone won?
40 * - did the ball move smoothly, or was the X or Y position quantized?
42 * It could also use better player logic: moving the paddle even when the ball
43 * is going away, and making mistakes instead of just not keeping up with the
46 * There is some info at http://www.mameworld.net/discrete/Atari/Atari.htm#Pong
48 * It says that the original Pong game did not have a microprocessor, or even a
49 * custom integrated circuit. It was all discrete logic.
53 #include "screenhack.h"
55 /* #define OUTPUT_POS */
57 typedef struct _paddle {
67 typedef struct _ball {
90 analogtv_reception reception;
98 analogtv_font score_font;
103 PONG_W = ANALOGTV_VIS_LEN,
104 PONG_H = ANALOGTV_VISLINES,
109 hit_top_bottom(struct state *st)
111 if ( (st->ball.y <= PONG_TMARG) ||
112 (st->ball.y+st->ball.h >= PONG_H) )
117 reset_score(struct state * st)
121 /* init score to current time */
122 time_t now = time(0);
123 struct tm* now_tm = localtime(&now);
125 st->r_paddle.score = now_tm->tm_hour;
126 st->l_paddle.score = now_tm->tm_min;
130 st->r_paddle.score = 0;
131 st->l_paddle.score = 0;
136 new_game(struct state *st)
138 /* Starts a Whole New Game*/
139 st->ball.x = PONG_W/2;
140 st->ball.y = PONG_H/2;
144 /* jwz: perhaps not totally accurate, but randomize things a little bit
145 so that games on multiple screens are not identical. */
146 if (random() & 1) st->by = -st->by;
147 st->ball.y += (random() % (PONG_H/6))-(PONG_H/3);
149 st->l_paddle.wait = 1;
150 st->l_paddle.lock = 0;
151 st->r_paddle.wait = 0;
152 st->r_paddle.lock = 0;
153 st->paddle_rate = st->m_unit-1;
156 st->l_paddle.h = PONG_H/4;
157 st->r_paddle.h = PONG_H/4;
161 start_game(struct state *st)
164 st->ball.x = PONG_W/2;
165 st->ball.y = PONG_H/2;
169 /* jwz: perhaps not totally accurate, but randomize things a little bit
170 so that games on multiple screens are not identical. */
171 if (random() & 1) st->by = -st->by;
172 st->ball.y += (random() % (PONG_H/6))-(PONG_H/3);
174 st->l_paddle.wait = 1;
175 st->l_paddle.lock = 0;
176 st->r_paddle.wait = 0;
177 st->r_paddle.lock = 0;
178 st->paddle_rate = st->m_unit-1;
180 if (st->l_paddle.h > 10) st->l_paddle.h= st->l_paddle.h*19/20;
181 if (st->r_paddle.h > 10) st->r_paddle.h= st->r_paddle.h*19/20;
185 hit_paddle(struct state *st)
187 if ( st->ball.x + st->ball.w >= st->r_paddle.x &&
188 st->bx > 0 ) /*we are traveling to the right*/
190 if ((st->ball.y + st->ball.h > st->r_paddle.y) &&
191 (st->ball.y < st->r_paddle.y + st->r_paddle.h))
194 st->l_paddle.wait = 0;
195 st->r_paddle.wait = 1;
196 st->r_paddle.lock = 0;
197 st->l_paddle.lock = 0;
207 st->r_paddle.score++;
208 if (st->r_paddle.score >=10)
216 if (st->ball.x <= st->l_paddle.x + st->l_paddle.w &&
217 st->bx < 0 ) /*we are traveling to the left*/
219 if ( st->ball.y + st->ball.h > st->l_paddle.y &&
220 st->ball.y < st->l_paddle.y + st->l_paddle.h)
223 st->l_paddle.wait = 1;
224 st->r_paddle.wait = 0;
225 st->r_paddle.lock = 0;
226 st->l_paddle.lock = 0;
236 st->l_paddle.score++;
237 if (st->l_paddle.score >= 10)
247 pong_init (Display *dpy, Window window)
249 struct state *st = (struct state *) calloc (1, sizeof(*st));
256 { /* regular pong font */
257 /* If you think we haven't learned anything since the early 70s,
258 look at this font for a while */
332 { /* pong clock font - hand-crafted double size looks better */
461 st->tv=analogtv_allocate(st->dpy, st->window);
462 analogtv_set_defaults(st->tv, "");
465 st->clock = get_boolean_resource(st->dpy, "clock", "Boolean");
467 analogtv_make_font(st->dpy, st->window, &st->score_font,
468 fonts[st->clock].w, fonts[st->clock].h, NULL);
472 analogtv_font_set_char(&st->score_font, '0'+i, fonts[st->clock].s[i]);
476 printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H);
479 st->inp=analogtv_input_allocate();
480 analogtv_setup_sync(st->inp, 0, 0);
482 st->reception.input = st->inp;
483 st->reception.level = 2.0;
487 st->reception.multipath = frand(1.0);
490 st->reception.multipath=0.0;
497 st->l_paddle.y = 100;
499 st->l_paddle.h = PONG_H/4;
500 st->l_paddle.wait = 1;
501 st->l_paddle.lock = 0;
502 st->r_paddle = st->l_paddle;
503 st->r_paddle.x = PONG_W - 8 - st->r_paddle.w;
504 st->r_paddle.wait = 0;
506 st->ball.x = PONG_W/2;
507 st->ball.y = PONG_H/2;
511 st->m_unit = get_integer_resource (st->dpy, "speed", "Integer");
512 st->noise = get_float_resource(st->dpy, "noise", "Float");
513 st->clock = get_boolean_resource(st->dpy, "clock", "Boolean");
517 st->score_font.y_mult *= 2;
518 st->score_font.x_mult *= 2;
525 analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, st->field_ntsc);
526 analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, st->ball_ntsc);
527 analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, st->paddle_ntsc);
528 analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, st->score_ntsc);
529 analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, st->net_ntsc);
531 analogtv_draw_solid(st->inp,
532 ANALOGTV_VIS_START, ANALOGTV_VIS_END,
533 ANALOGTV_TOP, ANALOGTV_BOT,
540 p_logic(struct state *st, Paddle *p)
544 targ = st->ball.y + st->by * (st->r_paddle.x-st->ball.x) / st->bx;
546 else if (st->bx < 0) {
547 targ = st->ball.y - st->by * (st->ball.x - st->l_paddle.x - st->l_paddle.w) / st->bx;
552 if (targ > PONG_H) targ=PONG_H;
553 if (targ < 0) targ=0;
555 if (targ < p->y && !p->lock)
557 p->y -= st->paddle_rate;
559 else if (targ > (p->y + p->h) && !p->lock)
561 p->y += st->paddle_rate;
565 int move=targ - (p->y + p->h/2);
566 if (move>st->paddle_rate) move=st->paddle_rate;
567 if (move<-st->paddle_rate) move=-st->paddle_rate;
574 p_hit_top_bottom(Paddle *p)
576 if(p->y <= PONG_TMARG)
580 if((p->y + p->h) >= PONG_H)
582 p->y = PONG_H - p->h;
587 XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h);
590 XClearArea(dpy,window, p->x, p->y + p->h,
591 p->w, (old_v + p->h) - (p->y + p->h), 0);
593 else if (old_v < p->y)
595 XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0);
599 paint_paddle(struct state *st, Paddle *p)
601 analogtv_draw_solid(st->inp,
602 ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
603 ANALOGTV_TOP, ANALOGTV_BOT,
606 analogtv_draw_solid(st->inp,
607 ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
608 ANALOGTV_TOP + p->y, ANALOGTV_TOP + p->y + p->h,
613 XClearArea(dpy,window, old_ballx, old_bally, st->ball.d, st->ball.d, 0);
614 XFillRectangle (dpy, window, gc, st->ball.x, st->ball.y, st->ball.d, st->ball.d);
615 XFillRectangle (dpy, window, gc, xgwa.width / 2, 0, st->ball.d, xgwa.height);
619 erase_ball(struct state *st)
621 analogtv_draw_solid(st->inp,
622 ANALOGTV_VIS_START + st->ball.x, ANALOGTV_VIS_START + st->ball.x + st->ball.w,
623 ANALOGTV_TOP + st->ball.y, ANALOGTV_TOP + st->ball.y + st->ball.h,
628 paint_ball(struct state *st)
630 analogtv_draw_solid(st->inp,
631 ANALOGTV_VIS_START + st->ball.x, ANALOGTV_VIS_START + st->ball.x + st->ball.w,
632 ANALOGTV_TOP + st->ball.y, ANALOGTV_TOP + st->ball.y + st->ball.h,
637 paint_score(struct state *st)
641 char* fmt = (st->clock ? "%02d" : "%d");
643 analogtv_draw_solid(st->inp,
644 ANALOGTV_VIS_START, ANALOGTV_VIS_END,
645 ANALOGTV_TOP, ANALOGTV_TOP + 10+ st->score_font.char_h * st->score_font.y_mult,
649 sprintf(buf, fmt ,st->r_paddle.score%256);
650 analogtv_draw_string(st->inp, &st->score_font, buf,
651 ANALOGTV_VIS_START + 130, ANALOGTV_TOP + 8,
654 sprintf(buf, fmt, st->l_paddle.score%256);
655 analogtv_draw_string(st->inp, &st->score_font, buf,
656 ANALOGTV_VIS_END - 200, ANALOGTV_TOP + 8,
662 paint_net(struct state *st)
666 x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2;
668 for (y=ANALOGTV_TOP; y<ANALOGTV_BOT; y+=6) {
669 analogtv_draw_solid(st->inp, x-2, x+2, y, y+3,
671 analogtv_draw_solid(st->inp, x-2, x+2, y+3, y+6,
678 pong_draw (Display *dpy, Window window, void *closure)
680 struct state *st = (struct state *) closure;
684 time_t now = time(0);
685 struct tm* tm_now = localtime(&now);
687 if (st->r_paddle.score != tm_now->tm_hour)
689 /* l paddle must score */
690 st->r_paddle.wait = 1;
692 else if (st->l_paddle.score != tm_now->tm_min)
694 /* r paddle must score */
695 st->l_paddle.wait = 1;
700 st->ball.x += st->bx;
701 st->ball.y += st->by;
705 /* in non-clock mode, occasionally increase ball speed */
706 if ((random()%40)==0) {
707 if (st->bx>0) st->bx++; else st->bx--;
711 if (!st->r_paddle.wait)
713 p_logic(st, &st->r_paddle);
715 if (!st->l_paddle.wait)
717 p_logic(st, &st->l_paddle);
720 p_hit_top_bottom(&st->r_paddle);
721 p_hit_top_bottom(&st->l_paddle);
727 printf("(%d,%d,%d,%d)\n",st->ball.x,st->ball.y,st->ball.w,st->ball.h);
735 paint_paddle(st, &st->r_paddle);
736 paint_paddle(st, &st->l_paddle);
738 if (1) paint_ball(st);
740 analogtv_init_signal(st->tv, st->noise);
741 analogtv_reception_update(&st->reception);
742 analogtv_add_signal(st->tv, &st->reception);
743 analogtv_draw(st->tv);
750 static const char *pong_defaults [] = {
751 ".background: black",
752 ".foreground: white",
761 static XrmOptionDescRec pong_options [] = {
762 { "-speed", ".speed", XrmoptionSepArg, 0 },
763 { "-noise", ".noise", XrmoptionSepArg, 0 },
764 { "-clock", ".clock", XrmoptionNoArg, "true" },
770 pong_reshape (Display *dpy, Window window, void *closure,
771 unsigned int w, unsigned int h)
773 struct state *st = (struct state *) closure;
774 analogtv_reconfigure (st->tv);
778 pong_event (Display *dpy, Window window, void *closure, XEvent *event)
784 pong_free (Display *dpy, Window window, void *closure)
786 struct state *st = (struct state *) closure;
787 analogtv_release(st->tv);
791 XSCREENSAVER_MODULE ("Pong", pong)