New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / pong.c
blob53edb90c00702447f76bc52398209389bd0d0777
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
23 #ifdef HAVE_LCD_BITMAP
25 PLUGIN_HEADER
27 #define PAD_HEIGHT LCD_HEIGHT / 6 /* Recorder: 10 iRiver: 21 */
28 #define PAD_WIDTH LCD_WIDTH / 50 /* Recorder: 2 iRiver: 2 */
30 #define BALL_HEIGHT LCD_HEIGHT / 32 /* Recorder: 2 iRiver: 4 */
31 #define BALL_WIDTH LCD_HEIGHT / 32 /* We want a square ball */
33 #define SPEEDX ( LCD_WIDTH * 3 ) / 2 /* Recorder: 168 iRiver: 240 */
34 #define SPEEDY LCD_HEIGHT * 2 /* Recorder: 128 iRiver: 256 */
36 #define RES 100
38 #define MOVE_STEP LCD_HEIGHT / 32 /* move pad this many steps up/down each move */
40 /* variable button definitions */
41 #if CONFIG_KEYPAD == RECORDER_PAD
42 #define PONG_QUIT BUTTON_OFF
43 #define PONG_PAUSE BUTTON_ON
44 #define PONG_LEFT_UP BUTTON_F1
45 #define PONG_LEFT_DOWN BUTTON_LEFT
46 #define PONG_RIGHT_UP BUTTON_F3
47 #define PONG_RIGHT_DOWN BUTTON_RIGHT
49 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
50 #define PONG_QUIT BUTTON_OFF
51 #define PONG_PAUSE BUTTON_ON
52 #define PONG_LEFT_UP BUTTON_F1
53 #define PONG_LEFT_DOWN BUTTON_LEFT
54 #define PONG_RIGHT_UP BUTTON_F3
55 #define PONG_RIGHT_DOWN BUTTON_RIGHT
57 #elif CONFIG_KEYPAD == ONDIO_PAD
58 #define PONG_QUIT BUTTON_OFF
59 #define PONG_PAUSE BUTTON_RIGHT
60 #define PONG_LEFT_UP BUTTON_LEFT
61 #define PONG_LEFT_DOWN BUTTON_MENU
62 #define PONG_RIGHT_UP BUTTON_UP
63 #define PONG_RIGHT_DOWN BUTTON_DOWN
65 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
66 #define PONG_QUIT BUTTON_OFF
67 #define PONG_LEFT_UP BUTTON_UP
68 #define PONG_LEFT_DOWN BUTTON_DOWN
69 #define PONG_RIGHT_UP BUTTON_ON
70 #define PONG_RIGHT_DOWN BUTTON_MODE
71 #define PONG_RC_QUIT BUTTON_RC_STOP
73 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
74 #define PONG_QUIT BUTTON_OFF
75 #define PONG_LEFT_UP BUTTON_UP
76 #define PONG_LEFT_DOWN BUTTON_DOWN
77 #define PONG_RIGHT_UP BUTTON_REC
78 #define PONG_RIGHT_DOWN BUTTON_MODE
79 #define PONG_RC_QUIT BUTTON_RC_STOP
81 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
82 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
83 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
84 #define PONG_QUIT BUTTON_SELECT
85 #define PONG_LEFT_UP BUTTON_MENU
86 #define PONG_LEFT_DOWN BUTTON_LEFT
87 #define PONG_RIGHT_UP BUTTON_RIGHT
88 #define PONG_RIGHT_DOWN BUTTON_PLAY
90 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
91 #define PONG_QUIT BUTTON_POWER
92 #define PONG_LEFT_UP BUTTON_UP
93 #define PONG_LEFT_DOWN BUTTON_DOWN
94 #define PONG_RIGHT_UP BUTTON_REC
95 #define PONG_RIGHT_DOWN BUTTON_PLAY
97 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
98 #define PONG_QUIT BUTTON_POWER
99 #define PONG_PAUSE BUTTON_SELECT
100 #define PONG_LEFT_UP BUTTON_UP
101 #define PONG_LEFT_DOWN BUTTON_DOWN
102 #define PONG_RIGHT_UP BUTTON_VOL_UP
103 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
105 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
106 #define PONG_QUIT BUTTON_POWER
107 #define PONG_PAUSE BUTTON_SELECT
108 #define PONG_LEFT_UP BUTTON_LEFT
109 #define PONG_LEFT_DOWN BUTTON_DOWN
110 #define PONG_RIGHT_UP BUTTON_UP
111 #define PONG_RIGHT_DOWN BUTTON_RIGHT
113 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
114 #define PONG_QUIT BUTTON_POWER
115 #define PONG_PAUSE BUTTON_SELECT
116 #define PONG_LEFT_UP BUTTON_VOL_UP
117 #define PONG_LEFT_DOWN BUTTON_VOL_DOWN
118 #define PONG_RIGHT_UP BUTTON_UP
119 #define PONG_RIGHT_DOWN BUTTON_DOWN
121 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
122 #define PONG_QUIT BUTTON_POWER
123 #define PONG_LEFT_UP BUTTON_SCROLL_UP
124 #define PONG_LEFT_DOWN BUTTON_SCROLL_DOWN
125 #define PONG_RIGHT_UP BUTTON_REW
126 #define PONG_RIGHT_DOWN BUTTON_FF
128 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
129 #define PONG_QUIT BUTTON_BACK
130 #define PONG_LEFT_UP BUTTON_UP
131 #define PONG_LEFT_DOWN BUTTON_DOWN
132 #define PONG_RIGHT_UP BUTTON_VOL_UP
133 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
135 #elif (CONFIG_KEYPAD == MROBE100_PAD)
136 #define PONG_QUIT BUTTON_POWER
137 #define PONG_PAUSE BUTTON_SELECT
138 #define PONG_LEFT_UP BUTTON_MENU
139 #define PONG_LEFT_DOWN BUTTON_LEFT
140 #define PONG_RIGHT_UP BUTTON_PLAY
141 #define PONG_RIGHT_DOWN BUTTON_RIGHT
143 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
144 #define PONG_QUIT BUTTON_RC_REC
145 #define PONG_PAUSE BUTTON_RC_PLAY
146 #define PONG_LEFT_UP BUTTON_RC_VOL_UP
147 #define PONG_LEFT_DOWN BUTTON_RC_VOL_DOWN
148 #define PONG_RIGHT_UP BUTTON_VOL_UP
149 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
151 #elif (CONFIG_KEYPAD == COWOND2_PAD)
152 #define PONG_QUIT BUTTON_POWER
154 #elif CONFIG_KEYPAD == IAUDIO67_PAD
155 #define PONG_QUIT BUTTON_POWER
156 #define PONG_PAUSE BUTTON_MENU
157 #define PONG_LEFT_UP BUTTON_VOLUP
158 #define PONG_LEFT_DOWN BUTTON_VOLDOWN
159 #define PONG_RIGHT_UP BUTTON_RIGHT
160 #define PONG_RIGHT_DOWN BUTTON_LEFT
162 #else
163 #error No keymap defined!
164 #endif
166 #ifdef HAVE_TOUCHSCREEN
167 #ifndef PONG_QUIT
168 #define PONG_QUIT BUTTON_TOPMIDDLE
169 #endif
170 #ifndef PONG_LEFT_UP
171 #define PONG_LEFT_UP BUTTON_TOPLEFT
172 #endif
173 #ifndef PONG_LEFT_DOWN
174 #define PONG_LEFT_DOWN BUTTON_BOTTOMLEFT
175 #endif
176 #ifndef PONG_RIGHT_UP
177 #define PONG_RIGHT_UP BUTTON_TOPRIGHT
178 #endif
179 #ifndef PONG_RIGHT_DOWN
180 #define PONG_RIGHT_DOWN BUTTON_BOTTOMRIGHT
181 #endif
182 #ifndef PONG_PAUSE
183 #define PONG_PAUSE BUTTON_CENTER
184 #endif
185 #endif
187 static const struct plugin_api* rb;
189 struct pong {
190 int ballx; /* current X*RES position of the ball */
191 int bally; /* current Y*RES position of the ball */
192 int w_pad[2]; /* wanted current Y positions of pads */
193 int e_pad[2]; /* existing current Y positions of pads */
194 int ballspeedx; /* */
195 int ballspeedy; /* */
197 int score[2];
200 void singlepad(int x, int y, int set)
202 if(set) {
203 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
205 else {
206 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
207 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
208 rb->lcd_set_drawmode(DRMODE_SOLID);
212 static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH};
213 void pad(struct pong *p, int pad)
215 /* clear existing pad */
216 singlepad(xpos[pad], p->e_pad[pad], 0);
218 /* draw wanted pad */
219 singlepad(xpos[pad], p->w_pad[pad], 1);
221 /* existing is now the wanted */
222 p->e_pad[pad] = p->w_pad[pad];
225 bool wallcollide(struct pong *p, int pad)
227 /* we have already checked for pad-collision, just check if this hits
228 the wall */
229 if(pad) {
230 /* right-side */
231 if(p->ballx > LCD_WIDTH*RES)
232 return true;
234 else {
235 if(p->ballx < 0)
236 return true;
238 return false;
241 /* returns true if the ball has hit a pad, and then the info variable
242 will have extra angle info */
244 bool padcollide(struct pong *p, int pad, int *info)
246 int x = p->ballx/RES;
247 int y = p->bally/RES;
249 if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
250 (y + BALL_HEIGHT > p->e_pad[pad])) {
251 /* Y seems likely right */
253 /* store the delta between ball-middle MINUS pad-middle, so
254 it returns:
255 0 when the ball hits exactly the middle of the pad
256 positive numbers when the ball is below the middle of the pad
257 negative numbers when the ball is above the middle of the pad
259 max number is +- PAD_HEIGHT/2
262 *info = (y+BALL_HEIGHT/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
264 if(pad) {
265 /* right-side */
266 if((x + BALL_WIDTH) >= (LCD_WIDTH - PAD_WIDTH))
267 return true; /* phump */
269 else {
270 if(x <= PAD_WIDTH)
271 return true;
274 return false; /* nah */
277 void bounce(struct pong *p, int pad, int info)
279 (void)pad; /* not used right now */
280 p->ballspeedx = -p->ballspeedx;
282 /* info is the hit-angle into the pad */
283 if(p->ballspeedy > 0) {
284 /* downwards */
285 if(info > 0) {
286 /* below the middle of the pad */
287 p->ballspeedy += info * RES/3;
289 else if(info < 0) {
290 /* above the middle */
291 p->ballspeedy = info * RES/2;
294 else {
295 /* upwards */
296 if(info > 0) {
297 /* below the middle of the pad */
298 p->ballspeedy = info * RES/2;
300 else if(info < 0) {
301 /* above the middle */
302 p->ballspeedy -= info * RES/3;
306 p->ballspeedy += rb->rand()%21-10;
308 #if 0
309 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
310 #endif
313 void score(struct pong *p, int pad)
315 if(pad)
316 rb->splash(HZ/4, "right scores!");
317 else
318 rb->splash(HZ/4, "left scores!");
319 rb->lcd_clear_display();
320 p->score[pad]++;
322 /* then move the X-speed of the ball and give it a random Y position */
323 p->ballspeedx = -p->ballspeedx;
324 p->bally = rb->rand()%(LCD_HEIGHT*RES - BALL_HEIGHT);
326 /* avoid hitting the pad with the new ball */
327 p->ballx = (p->ballx < 0) ?
328 (RES * PAD_WIDTH) : (RES * (LCD_WIDTH - PAD_WIDTH - BALL_WIDTH));
330 /* restore Y-speed to default */
331 p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
333 /* set the existing pad positions to something weird to force pad
334 updates */
335 p->e_pad[0] = -1;
336 p->e_pad[1] = -1;
339 void ball(struct pong *p)
341 int x = p->ballx/RES;
342 int y = p->bally/RES;
344 int newx;
345 int newy;
347 int info;
349 /* movement */
350 p->ballx += p->ballspeedx;
351 p->bally += p->ballspeedy;
353 newx = p->ballx/RES;
354 newy = p->bally/RES;
356 /* detect if ball hits a wall */
357 if(newy + BALL_HEIGHT > LCD_HEIGHT) {
358 /* hit floor, bounce */
359 p->ballspeedy = -p->ballspeedy;
360 newy = LCD_HEIGHT - BALL_HEIGHT;
361 p->bally = newy * RES;
363 else if(newy < 0) {
364 /* hit ceiling, bounce */
365 p->ballspeedy = -p->ballspeedy;
366 p->bally = 0;
367 newy = 0;
370 /* detect if ball hit pads */
371 if(padcollide(p, 0, &info))
372 bounce(p, 0, info);
373 else if(padcollide(p, 1, &info))
374 bounce(p, 1, info);
375 else if(wallcollide(p, 0))
376 score(p, 1);
377 else if(wallcollide(p, 1))
378 score(p, 0);
380 newx = p->ballx/RES;
381 newy = p->bally/RES;
383 /* clear old position */
384 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
385 rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT);
386 rb->lcd_set_drawmode(DRMODE_SOLID);
388 /* draw the new ball position */
389 rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT);
392 void padmove(int *pos, int dir)
394 *pos += dir;
395 if(*pos > (LCD_HEIGHT-PAD_HEIGHT))
396 *pos = (LCD_HEIGHT-PAD_HEIGHT);
397 else if(*pos < 0)
398 *pos = 0;
401 int keys(struct pong *p)
403 int key;
404 #ifdef PONG_PAUSE
405 static bool pause = false;
406 #endif
408 /* number of ticks this function will loop reading keys */
409 #ifndef HAVE_TOUCHSCREEN
410 int time = 4;
411 #else
412 int time = 1;
413 #endif
414 int start = *rb->current_tick;
415 int end = start + time;
417 while(end > *rb->current_tick) {
418 key = rb->button_get_w_tmo(end - *rb->current_tick);
420 #ifdef HAVE_TOUCHSCREEN
421 short touch_x, touch_y;
422 if(key & BUTTON_TOUCHSCREEN)
424 touch_x = rb->button_get_data() >> 16;
425 touch_y = rb->button_get_data() & 0xFFFF;
426 if(touch_x >= xpos[0] && touch_x <= xpos[0]+(PAD_WIDTH*4))
427 padmove(&p->w_pad[0], touch_y-(p->e_pad[0]*2+PAD_HEIGHT)/2);
429 if(touch_x >= xpos[1]-(PAD_WIDTH*4) && touch_x <= xpos[1])
430 padmove(&p->w_pad[1], touch_y-(p->e_pad[1]*2+PAD_HEIGHT)/2);
432 #endif
434 #ifdef HAS_BUTTON_HOLD
435 if (rb->button_hold())
436 return 2; /* Pause game */
437 #endif
439 if(key & PONG_QUIT
440 #ifdef PONG_RC_QUIT
441 || key & PONG_RC_QUIT
442 #endif
444 return 0; /* exit game NOW */
446 #ifdef PONG_PAUSE
447 if(key == PONG_PAUSE)
448 pause = !pause;
449 if(pause)
450 return 2; /* Pause game */
451 #endif
453 key = rb->button_status(); /* ignore BUTTON_REPEAT */
455 if(key & PONG_LEFT_DOWN) /* player left goes down */
456 padmove(&p->w_pad[0], MOVE_STEP);
458 if(key & PONG_LEFT_UP) /* player left goes up */
459 padmove(&p->w_pad[0], -MOVE_STEP);
461 if(key & PONG_RIGHT_DOWN) /* player right goes down */
462 padmove(&p->w_pad[1], MOVE_STEP);
464 if(key & PONG_RIGHT_UP) /* player right goes up */
465 padmove(&p->w_pad[1], -MOVE_STEP);
467 if(rb->default_event_handler(key) == SYS_USB_CONNECTED)
468 return -1; /* exit game because of USB */
470 return 1; /* return 0 to exit game */
473 void showscore(struct pong *p)
475 static char buffer[20];
476 int w;
478 rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]);
479 w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL);
480 rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer);
483 /* this is the plugin entry point */
484 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
486 struct pong pong;
487 int game = 1;
489 /* init the struct with some silly values to start with */
491 pong.ballx = 20*RES;
492 pong.bally = 20*RES;
494 pong.e_pad[0] = 0;
495 pong.w_pad[0] = 7;
496 pong.e_pad[1] = 0;
497 pong.w_pad[1] = 40;
499 pong.ballspeedx = SPEEDX;
500 pong.ballspeedy = SPEEDY;
502 pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
504 /* if you don't use the parameter, you can do like
505 this to avoid the compiler warning about it */
506 (void)parameter;
508 rb = api; /* use the "standard" rb pointer */
510 /* Clear screen */
511 rb->lcd_clear_display();
513 /* go go go */
514 while(game > 0) {
515 if (game == 2) { /* Game Paused */
516 rb->splash(0, "PAUSED");
517 while(game == 2)
518 game = keys(&pong); /* short circuit */
519 rb->lcd_clear_display();
521 showscore(&pong);
522 pad(&pong, 0); /* draw left pad */
523 pad(&pong, 1); /* draw right pad */
524 ball(&pong); /* move and draw ball */
526 rb->lcd_update();
528 game = keys(&pong); /* deal with keys */
531 return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
534 #endif /* HAVE_LCD_BITMAP */