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 | |
---|
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 | |
---|