Add 2008 to the copyright notice.
[Rockbox.git] / apps / plugins / pong.c
blob7052c0be3eec265a2b966d922f277b811d39a3aa
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Daniel Stenberg
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "plugin.h"
21 #ifdef HAVE_LCD_BITMAP
23 PLUGIN_HEADER
25 #define PAD_HEIGHT LCD_HEIGHT / 6 /* Recorder: 10 iRiver: 21 */
26 #define PAD_WIDTH LCD_WIDTH / 50 /* Recorder: 2 iRiver: 2 */
28 #define BALL_HEIGHT LCD_HEIGHT / 32 /* Recorder: 2 iRiver: 4 */
29 #define BALL_WIDTH LCD_HEIGHT / 32 /* We want a square ball */
31 #define SPEEDX ( LCD_WIDTH * 3 ) / 2 /* Recorder: 168 iRiver: 240 */
32 #define SPEEDY LCD_HEIGHT * 2 /* Recorder: 128 iRiver: 256 */
34 #define RES 100
36 #define MOVE_STEP LCD_HEIGHT / 32 /* move pad this many steps up/down each move */
38 /* variable button definitions */
39 #if CONFIG_KEYPAD == RECORDER_PAD
40 #define PONG_QUIT BUTTON_OFF
41 #define PONG_PAUSE BUTTON_ON
42 #define PONG_LEFT_UP BUTTON_F1
43 #define PONG_LEFT_DOWN BUTTON_LEFT
44 #define PONG_RIGHT_UP BUTTON_F3
45 #define PONG_RIGHT_DOWN BUTTON_RIGHT
47 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
48 #define PONG_QUIT BUTTON_OFF
49 #define PONG_PAUSE BUTTON_ON
50 #define PONG_LEFT_UP BUTTON_F1
51 #define PONG_LEFT_DOWN BUTTON_LEFT
52 #define PONG_RIGHT_UP BUTTON_F3
53 #define PONG_RIGHT_DOWN BUTTON_RIGHT
55 #elif CONFIG_KEYPAD == ONDIO_PAD
56 #define PONG_QUIT BUTTON_OFF
57 #define PONG_PAUSE BUTTON_RIGHT
58 #define PONG_LEFT_UP BUTTON_LEFT
59 #define PONG_LEFT_DOWN BUTTON_MENU
60 #define PONG_RIGHT_UP BUTTON_UP
61 #define PONG_RIGHT_DOWN BUTTON_DOWN
63 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
64 #define PONG_QUIT BUTTON_OFF
65 #define PONG_LEFT_UP BUTTON_UP
66 #define PONG_LEFT_DOWN BUTTON_DOWN
67 #define PONG_RIGHT_UP BUTTON_ON
68 #define PONG_RIGHT_DOWN BUTTON_MODE
69 #define PONG_RC_QUIT BUTTON_RC_STOP
71 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
72 #define PONG_QUIT BUTTON_OFF
73 #define PONG_LEFT_UP BUTTON_UP
74 #define PONG_LEFT_DOWN BUTTON_DOWN
75 #define PONG_RIGHT_UP BUTTON_REC
76 #define PONG_RIGHT_DOWN BUTTON_MODE
77 #define PONG_RC_QUIT BUTTON_RC_STOP
79 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
80 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
81 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
82 #define PONG_QUIT BUTTON_SELECT
83 #define PONG_LEFT_UP BUTTON_MENU
84 #define PONG_LEFT_DOWN BUTTON_LEFT
85 #define PONG_RIGHT_UP BUTTON_RIGHT
86 #define PONG_RIGHT_DOWN BUTTON_PLAY
88 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
89 #define PONG_QUIT BUTTON_POWER
90 #define PONG_LEFT_UP BUTTON_UP
91 #define PONG_LEFT_DOWN BUTTON_DOWN
92 #define PONG_RIGHT_UP BUTTON_REC
93 #define PONG_RIGHT_DOWN BUTTON_PLAY
95 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
96 #define PONG_QUIT BUTTON_POWER
97 #define PONG_PAUSE BUTTON_SELECT
98 #define PONG_LEFT_UP BUTTON_UP
99 #define PONG_LEFT_DOWN BUTTON_DOWN
100 #define PONG_RIGHT_UP BUTTON_VOL_UP
101 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
103 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
104 #define PONG_QUIT BUTTON_POWER
105 #define PONG_PAUSE BUTTON_SELECT
106 #define PONG_LEFT_UP BUTTON_LEFT
107 #define PONG_LEFT_DOWN BUTTON_DOWN
108 #define PONG_RIGHT_UP BUTTON_UP
109 #define PONG_RIGHT_DOWN BUTTON_RIGHT
111 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
112 #define PONG_QUIT BUTTON_POWER
113 #define PONG_PAUSE BUTTON_SELECT
114 #define PONG_LEFT_UP BUTTON_VOL_UP
115 #define PONG_LEFT_DOWN BUTTON_VOL_DOWN
116 #define PONG_RIGHT_UP BUTTON_UP
117 #define PONG_RIGHT_DOWN BUTTON_DOWN
119 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
120 #define PONG_QUIT BUTTON_POWER
121 #define PONG_LEFT_UP BUTTON_SCROLL_UP
122 #define PONG_LEFT_DOWN BUTTON_SCROLL_DOWN
123 #define PONG_RIGHT_UP BUTTON_REW
124 #define PONG_RIGHT_DOWN BUTTON_FF
126 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
127 #define PONG_QUIT BUTTON_BACK
128 #define PONG_LEFT_UP BUTTON_UP
129 #define PONG_LEFT_DOWN BUTTON_DOWN
130 #define PONG_RIGHT_UP BUTTON_VOL_UP
131 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
133 #endif
135 static struct plugin_api* rb;
137 struct pong {
138 int ballx; /* current X*RES position of the ball */
139 int bally; /* current Y*RES position of the ball */
140 int w_pad[2]; /* wanted current Y positions of pads */
141 int e_pad[2]; /* existing current Y positions of pads */
142 int ballspeedx; /* */
143 int ballspeedy; /* */
145 int score[2];
148 void singlepad(int x, int y, int set)
150 if(set) {
151 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
153 else {
154 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
155 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
156 rb->lcd_set_drawmode(DRMODE_SOLID);
160 void pad(struct pong *p, int pad)
162 static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH};
164 /* clear existing pad */
165 singlepad(xpos[pad], p->e_pad[pad], 0);
167 /* draw wanted pad */
168 singlepad(xpos[pad], p->w_pad[pad], 1);
170 /* existing is now the wanted */
171 p->e_pad[pad] = p->w_pad[pad];
174 bool wallcollide(struct pong *p, int pad)
176 /* we have already checked for pad-collision, just check if this hits
177 the wall */
178 if(pad) {
179 /* right-side */
180 if(p->ballx > LCD_WIDTH*RES)
181 return true;
183 else {
184 if(p->ballx < 0)
185 return true;
187 return false;
190 /* returns true if the ball has hit a pad, and then the info variable
191 will have extra angle info */
193 bool padcollide(struct pong *p, int pad, int *info)
195 int x = p->ballx/RES;
196 int y = p->bally/RES;
198 if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
199 (y + BALL_HEIGHT > p->e_pad[pad])) {
200 /* Y seems likely right */
202 /* store the delta between ball-middle MINUS pad-middle, so
203 it returns:
204 0 when the ball hits exactly the middle of the pad
205 positive numbers when the ball is below the middle of the pad
206 negative numbers when the ball is above the middle of the pad
208 max number is +- PAD_HEIGHT/2
211 *info = (y+BALL_HEIGHT/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
213 if(pad) {
214 /* right-side */
215 if((x + BALL_WIDTH) >= (LCD_WIDTH - PAD_WIDTH))
216 return true; /* phump */
218 else {
219 if(x <= PAD_WIDTH)
220 return true;
223 return false; /* nah */
226 void bounce(struct pong *p, int pad, int info)
228 (void)pad; /* not used right now */
229 p->ballspeedx = -p->ballspeedx;
231 /* info is the hit-angle into the pad */
232 if(p->ballspeedy > 0) {
233 /* downwards */
234 if(info > 0) {
235 /* below the middle of the pad */
236 p->ballspeedy += info * RES/3;
238 else if(info < 0) {
239 /* above the middle */
240 p->ballspeedy = info * RES/2;
243 else {
244 /* upwards */
245 if(info > 0) {
246 /* below the middle of the pad */
247 p->ballspeedy = info * RES/2;
249 else if(info < 0) {
250 /* above the middle */
251 p->ballspeedy -= info * RES/3;
255 p->ballspeedy += rb->rand()%21-10;
257 #if 0
258 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
259 #endif
262 void score(struct pong *p, int pad)
264 if(pad)
265 rb->splash(HZ/4, "right scores!");
266 else
267 rb->splash(HZ/4, "left scores!");
268 rb->lcd_clear_display();
269 p->score[pad]++;
271 /* then move the X-speed of the ball and give it a random Y position */
272 p->ballspeedx = -p->ballspeedx;
273 p->bally = rb->rand()%(LCD_HEIGHT*RES - BALL_HEIGHT);
275 /* avoid hitting the pad with the new ball */
276 p->ballx = (p->ballx < 0) ?
277 (RES * PAD_WIDTH) : (RES * (LCD_WIDTH - PAD_WIDTH - BALL_WIDTH));
279 /* restore Y-speed to default */
280 p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
282 /* set the existing pad positions to something weird to force pad
283 updates */
284 p->e_pad[0] = -1;
285 p->e_pad[1] = -1;
288 void ball(struct pong *p)
290 int x = p->ballx/RES;
291 int y = p->bally/RES;
293 int newx;
294 int newy;
296 int info;
298 /* movement */
299 p->ballx += p->ballspeedx;
300 p->bally += p->ballspeedy;
302 newx = p->ballx/RES;
303 newy = p->bally/RES;
305 /* detect if ball hits a wall */
306 if(newy + BALL_HEIGHT > LCD_HEIGHT) {
307 /* hit floor, bounce */
308 p->ballspeedy = -p->ballspeedy;
309 newy = LCD_HEIGHT - BALL_HEIGHT;
310 p->bally = newy * RES;
312 else if(newy < 0) {
313 /* hit ceiling, bounce */
314 p->ballspeedy = -p->ballspeedy;
315 p->bally = 0;
316 newy = 0;
319 /* detect if ball hit pads */
320 if(padcollide(p, 0, &info))
321 bounce(p, 0, info);
322 else if(padcollide(p, 1, &info))
323 bounce(p, 1, info);
324 else if(wallcollide(p, 0))
325 score(p, 1);
326 else if(wallcollide(p, 1))
327 score(p, 0);
329 newx = p->ballx/RES;
330 newy = p->bally/RES;
332 /* clear old position */
333 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
334 rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT);
335 rb->lcd_set_drawmode(DRMODE_SOLID);
337 /* draw the new ball position */
338 rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT);
341 void padmove(int *pos, int dir)
343 *pos += dir;
344 if(*pos > (LCD_HEIGHT-PAD_HEIGHT))
345 *pos = (LCD_HEIGHT-PAD_HEIGHT);
346 else if(*pos < 0)
347 *pos = 0;
350 int keys(struct pong *p)
352 int key;
353 #ifdef PONG_PAUSE
354 static bool pause = false;
355 #endif
357 int time = 4; /* number of ticks this function will loop reading keys */
358 int start = *rb->current_tick;
359 int end = start + time;
361 while(end > *rb->current_tick) {
362 key = rb->button_get_w_tmo(end - *rb->current_tick);
364 #ifdef HAS_BUTTON_HOLD
365 if (rb->button_hold())
366 return 2; /* Pause game */
367 #endif
369 if(key & PONG_QUIT
370 #ifdef PONG_RC_QUIT
371 || key & PONG_RC_QUIT
372 #endif
374 return 0; /* exit game NOW */
376 #ifdef PONG_PAUSE
377 if(key == PONG_PAUSE)
378 pause = !pause;
379 if(pause)
380 return 2; /* Pause game */
381 #endif
383 key = rb->button_status(); /* ignore BUTTON_REPEAT */
385 if(key & PONG_LEFT_DOWN) /* player left goes down */
386 padmove(&p->w_pad[0], MOVE_STEP);
388 if(key & PONG_LEFT_UP) /* player left goes up */
389 padmove(&p->w_pad[0], -MOVE_STEP);
391 if(key & PONG_RIGHT_DOWN) /* player right goes down */
392 padmove(&p->w_pad[1], MOVE_STEP);
394 if(key & PONG_RIGHT_UP) /* player right goes up */
395 padmove(&p->w_pad[1], -MOVE_STEP);
397 if(rb->default_event_handler(key) == SYS_USB_CONNECTED)
398 return -1; /* exit game because of USB */
400 return 1; /* return 0 to exit game */
403 void showscore(struct pong *p)
405 static char buffer[20];
406 int w;
408 rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]);
409 w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL);
410 rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer);
413 /* this is the plugin entry point */
414 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
416 struct pong pong;
417 int game = 1;
419 /* init the struct with some silly values to start with */
421 pong.ballx = 20*RES;
422 pong.bally = 20*RES;
424 pong.e_pad[0] = 0;
425 pong.w_pad[0] = 7;
426 pong.e_pad[1] = 0;
427 pong.w_pad[1] = 40;
429 pong.ballspeedx = SPEEDX;
430 pong.ballspeedy = SPEEDY;
432 pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
434 /* if you don't use the parameter, you can do like
435 this to avoid the compiler warning about it */
436 (void)parameter;
438 rb = api; /* use the "standard" rb pointer */
440 /* Clear screen */
441 rb->lcd_clear_display();
443 /* go go go */
444 while(game > 0) {
445 if (game == 2) { /* Game Paused */
446 rb->splash(0, "PAUSED");
447 while(game == 2)
448 game = keys(&pong); /* short circuit */
449 rb->lcd_clear_display();
451 showscore(&pong);
452 pad(&pong, 0); /* draw left pad */
453 pad(&pong, 1); /* draw right pad */
454 ball(&pong); /* move and draw ball */
456 rb->lcd_update();
458 game = keys(&pong); /* deal with keys */
461 return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
464 #endif /* HAVE_LCD_BITMAP */