Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / plugins / pong.c
blob08899d767a329d9a095743e9e455f413cc44db43
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 (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
107 (CONFIG_KEYPAD == SANSA_M200_PAD)
108 #define PONG_QUIT BUTTON_POWER
109 #define PONG_PAUSE BUTTON_SELECT
110 #define PONG_LEFT_UP BUTTON_LEFT
111 #define PONG_LEFT_DOWN BUTTON_DOWN
112 #define PONG_RIGHT_UP BUTTON_UP
113 #define PONG_RIGHT_DOWN BUTTON_RIGHT
115 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
116 #define PONG_QUIT BUTTON_HOME
117 #define PONG_PAUSE BUTTON_SELECT
118 #define PONG_LEFT_UP BUTTON_LEFT
119 #define PONG_LEFT_DOWN BUTTON_DOWN
120 #define PONG_RIGHT_UP BUTTON_UP
121 #define PONG_RIGHT_DOWN BUTTON_RIGHT
123 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
124 #define PONG_QUIT BUTTON_POWER
125 #define PONG_PAUSE BUTTON_SELECT
126 #define PONG_LEFT_UP BUTTON_VOL_UP
127 #define PONG_LEFT_DOWN BUTTON_VOL_DOWN
128 #define PONG_RIGHT_UP BUTTON_UP
129 #define PONG_RIGHT_DOWN BUTTON_DOWN
131 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
132 #define PONG_QUIT BUTTON_POWER
133 #define PONG_LEFT_UP BUTTON_SCROLL_UP
134 #define PONG_LEFT_DOWN BUTTON_SCROLL_DOWN
135 #define PONG_RIGHT_UP BUTTON_REW
136 #define PONG_RIGHT_DOWN BUTTON_FF
138 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
139 #define PONG_QUIT BUTTON_BACK
140 #define PONG_LEFT_UP BUTTON_UP
141 #define PONG_LEFT_DOWN BUTTON_DOWN
142 #define PONG_RIGHT_UP BUTTON_VOL_UP
143 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
145 #elif (CONFIG_KEYPAD == MROBE100_PAD)
146 #define PONG_QUIT BUTTON_POWER
147 #define PONG_PAUSE BUTTON_SELECT
148 #define PONG_LEFT_UP BUTTON_MENU
149 #define PONG_LEFT_DOWN BUTTON_LEFT
150 #define PONG_RIGHT_UP BUTTON_PLAY
151 #define PONG_RIGHT_DOWN BUTTON_RIGHT
153 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
154 #define PONG_QUIT BUTTON_RC_REC
155 #define PONG_PAUSE BUTTON_RC_PLAY
156 #define PONG_LEFT_UP BUTTON_RC_VOL_UP
157 #define PONG_LEFT_DOWN BUTTON_RC_VOL_DOWN
158 #define PONG_RIGHT_UP BUTTON_VOL_UP
159 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
161 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
162 #define PONG_QUIT BUTTON_POWER
164 #elif CONFIG_KEYPAD == IAUDIO67_PAD
165 #define PONG_QUIT BUTTON_POWER
166 #define PONG_PAUSE BUTTON_MENU
167 #define PONG_LEFT_UP BUTTON_VOLUP
168 #define PONG_LEFT_DOWN BUTTON_VOLDOWN
169 #define PONG_RIGHT_UP BUTTON_RIGHT
170 #define PONG_RIGHT_DOWN BUTTON_LEFT
172 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
173 #define PONG_QUIT BUTTON_BACK
174 #define PONG_LEFT_UP BUTTON_UP
175 #define PONG_LEFT_DOWN BUTTON_DOWN
176 #define PONG_RIGHT_UP BUTTON_PLAY
177 #define PONG_RIGHT_DOWN BUTTON_MENU
179 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
180 #define PONG_QUIT BUTTON_POWER
181 #define PONG_PAUSE BUTTON_MENU
182 #define PONG_LEFT_UP BUTTON_UP
183 #define PONG_LEFT_DOWN BUTTON_DOWN
184 #define PONG_RIGHT_UP BUTTON_VOL_UP
185 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
187 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
188 #define PONG_QUIT BUTTON_POWER
189 #define PONG_PAUSE BUTTON_MENU
190 #define PONG_LEFT_UP BUTTON_UP
191 #define PONG_LEFT_DOWN BUTTON_DOWN
192 #define PONG_RIGHT_UP BUTTON_VOL_UP
193 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
195 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
196 CONFIG_KEYPAD == ONDAVX777_PAD || \
197 CONFIG_KEYPAD == MROBE500_PAD
198 #define PONG_QUIT BUTTON_POWER
200 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
201 #define PONG_QUIT BUTTON_REC
202 #define PONG_PAUSE BUTTON_PLAY
203 #define PONG_LEFT_UP BUTTON_UP
204 #define PONG_LEFT_DOWN BUTTON_DOWN
205 #define PONG_RIGHT_UP BUTTON_FFWD
206 #define PONG_RIGHT_DOWN BUTTON_REW
208 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
209 #define PONG_QUIT BUTTON_REC
210 #define PONG_PAUSE BUTTON_OK
211 #define PONG_LEFT_UP BUTTON_MENU
212 #define PONG_LEFT_DOWN BUTTON_PREV
213 #define PONG_RIGHT_UP BUTTON_PLAY
214 #define PONG_RIGHT_DOWN BUTTON_NEXT
216 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
217 #define PONG_QUIT (BUTTON_REC|BUTTON_PLAY)
218 #define PONG_LEFT_UP BUTTON_PREV
219 #define PONG_LEFT_DOWN BUTTON_NEXT
220 #define PONG_RIGHT_UP BUTTON_VOL_UP
221 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
223 #else
224 #error No keymap defined!
225 #endif
227 #ifdef HAVE_TOUCHSCREEN
228 #ifndef PONG_QUIT
229 #define PONG_QUIT BUTTON_TOPMIDDLE
230 #endif
231 #ifndef PONG_LEFT_UP
232 #define PONG_LEFT_UP BUTTON_TOPLEFT
233 #endif
234 #ifndef PONG_LEFT_DOWN
235 #define PONG_LEFT_DOWN BUTTON_BOTTOMLEFT
236 #endif
237 #ifndef PONG_RIGHT_UP
238 #define PONG_RIGHT_UP BUTTON_TOPRIGHT
239 #endif
240 #ifndef PONG_RIGHT_DOWN
241 #define PONG_RIGHT_DOWN BUTTON_BOTTOMRIGHT
242 #endif
243 #ifndef PONG_PAUSE
244 #define PONG_PAUSE BUTTON_CENTER
245 #endif
246 #endif
248 struct pong {
249 int ballx; /* current X*RES position of the ball */
250 int bally; /* current Y*RES position of the ball */
251 int w_pad[2]; /* wanted current Y positions of pads */
252 int e_pad[2]; /* existing current Y positions of pads */
253 int ballspeedx; /* */
254 int ballspeedy; /* */
256 int score[2];
259 void singlepad(int x, int y, int set)
261 if(set) {
262 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
264 else {
265 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
266 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
267 rb->lcd_set_drawmode(DRMODE_SOLID);
271 static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH};
272 void pad(struct pong *p, int pad)
274 /* clear existing pad */
275 singlepad(xpos[pad], p->e_pad[pad], 0);
277 /* draw wanted pad */
278 singlepad(xpos[pad], p->w_pad[pad], 1);
280 /* existing is now the wanted */
281 p->e_pad[pad] = p->w_pad[pad];
284 bool wallcollide(struct pong *p, int pad)
286 /* we have already checked for pad-collision, just check if this hits
287 the wall */
288 if(pad) {
289 /* right-side */
290 if(p->ballx > LCD_WIDTH*RES)
291 return true;
293 else {
294 if(p->ballx < 0)
295 return true;
297 return false;
300 /* returns true if the ball has hit a pad, and then the info variable
301 will have extra angle info */
303 bool padcollide(struct pong *p, int pad, int *info)
305 int x = p->ballx/RES;
306 int y = p->bally/RES;
308 if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
309 (y + BALL_HEIGHT > p->e_pad[pad])) {
310 /* Y seems likely right */
312 /* store the delta between ball-middle MINUS pad-middle, so
313 it returns:
314 0 when the ball hits exactly the middle of the pad
315 positive numbers when the ball is below the middle of the pad
316 negative numbers when the ball is above the middle of the pad
318 max number is +- PAD_HEIGHT/2
321 *info = (y+BALL_HEIGHT/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
323 if(pad) {
324 /* right-side */
325 if((x + BALL_WIDTH) >= (LCD_WIDTH - PAD_WIDTH))
326 return true; /* phump */
328 else {
329 if(x <= PAD_WIDTH)
330 return true;
333 return false; /* nah */
336 void bounce(struct pong *p, int pad, int info)
338 (void)pad; /* not used right now */
339 p->ballspeedx = -p->ballspeedx;
341 /* info is the hit-angle into the pad */
342 if(p->ballspeedy > 0) {
343 /* downwards */
344 if(info > 0) {
345 /* below the middle of the pad */
346 p->ballspeedy += info * RES/3;
348 else if(info < 0) {
349 /* above the middle */
350 p->ballspeedy = info * RES/2;
353 else {
354 /* upwards */
355 if(info > 0) {
356 /* below the middle of the pad */
357 p->ballspeedy = info * RES/2;
359 else if(info < 0) {
360 /* above the middle */
361 p->ballspeedy -= info * RES/3;
365 p->ballspeedy += rb->rand()%21-10;
367 #if 0
368 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
369 #endif
372 void score(struct pong *p, int pad)
374 if(pad)
375 rb->splash(HZ/4, "right scores!");
376 else
377 rb->splash(HZ/4, "left scores!");
378 rb->lcd_clear_display();
379 p->score[pad]++;
381 /* then move the X-speed of the ball and give it a random Y position */
382 p->ballspeedx = -p->ballspeedx;
383 p->bally = rb->rand()%(LCD_HEIGHT*RES - BALL_HEIGHT);
385 /* avoid hitting the pad with the new ball */
386 p->ballx = (p->ballx < 0) ?
387 (RES * PAD_WIDTH) : (RES * (LCD_WIDTH - PAD_WIDTH - BALL_WIDTH));
389 /* restore Y-speed to default */
390 p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
392 /* set the existing pad positions to something weird to force pad
393 updates */
394 p->e_pad[0] = -1;
395 p->e_pad[1] = -1;
398 void ball(struct pong *p)
400 int x = p->ballx/RES;
401 int y = p->bally/RES;
403 int newx;
404 int newy;
406 int info;
408 /* movement */
409 p->ballx += p->ballspeedx;
410 p->bally += p->ballspeedy;
412 newx = p->ballx/RES;
413 newy = p->bally/RES;
415 /* detect if ball hits a wall */
416 if(newy + BALL_HEIGHT > LCD_HEIGHT) {
417 /* hit floor, bounce */
418 p->ballspeedy = -p->ballspeedy;
419 newy = LCD_HEIGHT - BALL_HEIGHT;
420 p->bally = newy * RES;
422 else if(newy < 0) {
423 /* hit ceiling, bounce */
424 p->ballspeedy = -p->ballspeedy;
425 p->bally = 0;
426 newy = 0;
429 /* detect if ball hit pads */
430 if(padcollide(p, 0, &info))
431 bounce(p, 0, info);
432 else if(padcollide(p, 1, &info))
433 bounce(p, 1, info);
434 else if(wallcollide(p, 0))
435 score(p, 1);
436 else if(wallcollide(p, 1))
437 score(p, 0);
439 newx = p->ballx/RES;
440 newy = p->bally/RES;
442 /* clear old position */
443 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
444 rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT);
445 rb->lcd_set_drawmode(DRMODE_SOLID);
447 /* draw the new ball position */
448 rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT);
451 void padmove(int *pos, int dir)
453 *pos += dir;
454 if(*pos > (LCD_HEIGHT-PAD_HEIGHT))
455 *pos = (LCD_HEIGHT-PAD_HEIGHT);
456 else if(*pos < 0)
457 *pos = 0;
460 int keys(struct pong *p)
462 int key;
463 #ifdef PONG_PAUSE
464 static bool pause = false;
465 #endif
467 /* number of ticks this function will loop reading keys */
468 #ifndef HAVE_TOUCHSCREEN
469 int time = 4;
470 #else
471 int time = 1;
472 #endif
473 int start = *rb->current_tick;
474 int end = start + time;
476 while(TIME_BEFORE(*rb->current_tick, end)) {
477 key = rb->button_get_w_tmo(end - *rb->current_tick);
479 #ifdef HAVE_TOUCHSCREEN
480 short touch_x, touch_y;
481 if(key & BUTTON_TOUCHSCREEN)
483 touch_x = rb->button_get_data() >> 16;
484 touch_y = rb->button_get_data() & 0xFFFF;
485 if(touch_x >= xpos[0] && touch_x <= xpos[0]+(PAD_WIDTH*4))
486 padmove(&p->w_pad[0], touch_y-(p->e_pad[0]*2+PAD_HEIGHT)/2);
488 if(touch_x >= xpos[1]-(PAD_WIDTH*4) && touch_x <= xpos[1])
489 padmove(&p->w_pad[1], touch_y-(p->e_pad[1]*2+PAD_HEIGHT)/2);
491 #endif
493 #ifdef HAS_BUTTON_HOLD
494 if (rb->button_hold())
495 return 2; /* Pause game */
496 #endif
498 if(key & PONG_QUIT
499 #ifdef PONG_RC_QUIT
500 || key & PONG_RC_QUIT
501 #endif
503 return 0; /* exit game NOW */
505 #ifdef PONG_PAUSE
506 if(key == PONG_PAUSE)
507 pause = !pause;
508 if(pause)
509 return 2; /* Pause game */
510 #endif
512 key = rb->button_status(); /* ignore BUTTON_REPEAT */
514 if(key & PONG_LEFT_DOWN) /* player left goes down */
515 padmove(&p->w_pad[0], MOVE_STEP);
517 if(key & PONG_LEFT_UP) /* player left goes up */
518 padmove(&p->w_pad[0], -MOVE_STEP);
520 if(key & PONG_RIGHT_DOWN) /* player right goes down */
521 padmove(&p->w_pad[1], MOVE_STEP);
523 if(key & PONG_RIGHT_UP) /* player right goes up */
524 padmove(&p->w_pad[1], -MOVE_STEP);
526 if(rb->default_event_handler(key) == SYS_USB_CONNECTED)
527 return -1; /* exit game because of USB */
529 return 1; /* return 0 to exit game */
532 void showscore(struct pong *p)
534 static char buffer[20];
535 int w;
537 rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]);
538 w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL);
539 rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer);
542 /* this is the plugin entry point */
543 enum plugin_status plugin_start(const void* parameter)
545 struct pong pong;
546 int game = 1;
548 /* init the struct with some silly values to start with */
550 pong.ballx = 20*RES;
551 pong.bally = 20*RES;
553 pong.e_pad[0] = 0;
554 pong.w_pad[0] = 7;
555 pong.e_pad[1] = 0;
556 pong.w_pad[1] = 40;
558 pong.ballspeedx = SPEEDX;
559 pong.ballspeedy = SPEEDY;
561 pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
563 /* if you don't use the parameter, you can do like
564 this to avoid the compiler warning about it */
565 (void)parameter;
567 /* Clear screen */
568 rb->lcd_clear_display();
570 /* go go go */
571 while(game > 0) {
572 if (game == 2) { /* Game Paused */
573 rb->splash(0, "PAUSED");
574 while(game == 2)
575 game = keys(&pong); /* short circuit */
576 rb->lcd_clear_display();
578 showscore(&pong);
579 pad(&pong, 0); /* draw left pad */
580 pad(&pong, 1); /* draw right pad */
581 ball(&pong); /* move and draw ball */
583 rb->lcd_update();
585 game = keys(&pong); /* deal with keys */
588 return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
591 #endif /* HAVE_LCD_BITMAP */