Fix voicing of incorrect run time (top time instead of run time). Simplify runtime...
[kugel-rb.git] / apps / plugins / pong.c
blob60d704337fef2ebe38a156a7202f353fb38faa2e
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 #else
155 #error No keymap defined!
156 #endif
158 #ifdef HAVE_TOUCHPAD
159 #ifndef PONG_QUIT
160 #define PONG_QUIT BUTTON_TOPMIDDLE
161 #endif
162 #ifndef PONG_LEFT_UP
163 #define PONG_LEFT_UP BUTTON_TOPLEFT
164 #endif
165 #ifndef PONG_LEFT_DOWN
166 #define PONG_LEFT_DOWN BUTTON_BOTTOMLEFT
167 #endif
168 #ifndef PONG_RIGHT_UP
169 #define PONG_RIGHT_UP BUTTON_TOPRIGHT
170 #endif
171 #ifndef PONG_RIGHT_DOWN
172 #define PONG_RIGHT_DOWN BUTTON_BOTTOMRIGHT
173 #endif
174 #ifndef PONG_PAUSE
175 #define PONG_PAUSE BUTTON_CENTER
176 #endif
177 #endif
179 static const struct plugin_api* rb;
181 struct pong {
182 int ballx; /* current X*RES position of the ball */
183 int bally; /* current Y*RES position of the ball */
184 int w_pad[2]; /* wanted current Y positions of pads */
185 int e_pad[2]; /* existing current Y positions of pads */
186 int ballspeedx; /* */
187 int ballspeedy; /* */
189 int score[2];
192 void singlepad(int x, int y, int set)
194 if(set) {
195 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
197 else {
198 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
199 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
200 rb->lcd_set_drawmode(DRMODE_SOLID);
204 static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH};
205 void pad(struct pong *p, int pad)
207 /* clear existing pad */
208 singlepad(xpos[pad], p->e_pad[pad], 0);
210 /* draw wanted pad */
211 singlepad(xpos[pad], p->w_pad[pad], 1);
213 /* existing is now the wanted */
214 p->e_pad[pad] = p->w_pad[pad];
217 bool wallcollide(struct pong *p, int pad)
219 /* we have already checked for pad-collision, just check if this hits
220 the wall */
221 if(pad) {
222 /* right-side */
223 if(p->ballx > LCD_WIDTH*RES)
224 return true;
226 else {
227 if(p->ballx < 0)
228 return true;
230 return false;
233 /* returns true if the ball has hit a pad, and then the info variable
234 will have extra angle info */
236 bool padcollide(struct pong *p, int pad, int *info)
238 int x = p->ballx/RES;
239 int y = p->bally/RES;
241 if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
242 (y + BALL_HEIGHT > p->e_pad[pad])) {
243 /* Y seems likely right */
245 /* store the delta between ball-middle MINUS pad-middle, so
246 it returns:
247 0 when the ball hits exactly the middle of the pad
248 positive numbers when the ball is below the middle of the pad
249 negative numbers when the ball is above the middle of the pad
251 max number is +- PAD_HEIGHT/2
254 *info = (y+BALL_HEIGHT/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
256 if(pad) {
257 /* right-side */
258 if((x + BALL_WIDTH) >= (LCD_WIDTH - PAD_WIDTH))
259 return true; /* phump */
261 else {
262 if(x <= PAD_WIDTH)
263 return true;
266 return false; /* nah */
269 void bounce(struct pong *p, int pad, int info)
271 (void)pad; /* not used right now */
272 p->ballspeedx = -p->ballspeedx;
274 /* info is the hit-angle into the pad */
275 if(p->ballspeedy > 0) {
276 /* downwards */
277 if(info > 0) {
278 /* below the middle of the pad */
279 p->ballspeedy += info * RES/3;
281 else if(info < 0) {
282 /* above the middle */
283 p->ballspeedy = info * RES/2;
286 else {
287 /* upwards */
288 if(info > 0) {
289 /* below the middle of the pad */
290 p->ballspeedy = info * RES/2;
292 else if(info < 0) {
293 /* above the middle */
294 p->ballspeedy -= info * RES/3;
298 p->ballspeedy += rb->rand()%21-10;
300 #if 0
301 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
302 #endif
305 void score(struct pong *p, int pad)
307 if(pad)
308 rb->splash(HZ/4, "right scores!");
309 else
310 rb->splash(HZ/4, "left scores!");
311 rb->lcd_clear_display();
312 p->score[pad]++;
314 /* then move the X-speed of the ball and give it a random Y position */
315 p->ballspeedx = -p->ballspeedx;
316 p->bally = rb->rand()%(LCD_HEIGHT*RES - BALL_HEIGHT);
318 /* avoid hitting the pad with the new ball */
319 p->ballx = (p->ballx < 0) ?
320 (RES * PAD_WIDTH) : (RES * (LCD_WIDTH - PAD_WIDTH - BALL_WIDTH));
322 /* restore Y-speed to default */
323 p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
325 /* set the existing pad positions to something weird to force pad
326 updates */
327 p->e_pad[0] = -1;
328 p->e_pad[1] = -1;
331 void ball(struct pong *p)
333 int x = p->ballx/RES;
334 int y = p->bally/RES;
336 int newx;
337 int newy;
339 int info;
341 /* movement */
342 p->ballx += p->ballspeedx;
343 p->bally += p->ballspeedy;
345 newx = p->ballx/RES;
346 newy = p->bally/RES;
348 /* detect if ball hits a wall */
349 if(newy + BALL_HEIGHT > LCD_HEIGHT) {
350 /* hit floor, bounce */
351 p->ballspeedy = -p->ballspeedy;
352 newy = LCD_HEIGHT - BALL_HEIGHT;
353 p->bally = newy * RES;
355 else if(newy < 0) {
356 /* hit ceiling, bounce */
357 p->ballspeedy = -p->ballspeedy;
358 p->bally = 0;
359 newy = 0;
362 /* detect if ball hit pads */
363 if(padcollide(p, 0, &info))
364 bounce(p, 0, info);
365 else if(padcollide(p, 1, &info))
366 bounce(p, 1, info);
367 else if(wallcollide(p, 0))
368 score(p, 1);
369 else if(wallcollide(p, 1))
370 score(p, 0);
372 newx = p->ballx/RES;
373 newy = p->bally/RES;
375 /* clear old position */
376 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
377 rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT);
378 rb->lcd_set_drawmode(DRMODE_SOLID);
380 /* draw the new ball position */
381 rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT);
384 void padmove(int *pos, int dir)
386 *pos += dir;
387 if(*pos > (LCD_HEIGHT-PAD_HEIGHT))
388 *pos = (LCD_HEIGHT-PAD_HEIGHT);
389 else if(*pos < 0)
390 *pos = 0;
393 int keys(struct pong *p)
395 int key;
396 #ifdef PONG_PAUSE
397 static bool pause = false;
398 #endif
400 /* number of ticks this function will loop reading keys */
401 #ifndef HAVE_TOUCHPAD
402 int time = 4;
403 #else
404 int time = 1;
405 #endif
406 int start = *rb->current_tick;
407 int end = start + time;
409 while(end > *rb->current_tick) {
410 key = rb->button_get_w_tmo(end - *rb->current_tick);
412 #ifdef HAVE_TOUCHPAD
413 short touch_x, touch_y;
414 if(key & BUTTON_TOUCHPAD)
416 touch_x = rb->button_get_data() >> 16;
417 touch_y = rb->button_get_data() & 0xFFFF;
418 if(touch_x >= xpos[0] && touch_x <= xpos[0]+(PAD_WIDTH*4))
419 padmove(&p->w_pad[0], touch_y-(p->e_pad[0]*2+PAD_HEIGHT)/2);
421 if(touch_x >= xpos[1]-(PAD_WIDTH*4) && touch_x <= xpos[1])
422 padmove(&p->w_pad[1], touch_y-(p->e_pad[1]*2+PAD_HEIGHT)/2);
424 #endif
426 #ifdef HAS_BUTTON_HOLD
427 if (rb->button_hold())
428 return 2; /* Pause game */
429 #endif
431 if(key & PONG_QUIT
432 #ifdef PONG_RC_QUIT
433 || key & PONG_RC_QUIT
434 #endif
436 return 0; /* exit game NOW */
438 #ifdef PONG_PAUSE
439 if(key == PONG_PAUSE)
440 pause = !pause;
441 if(pause)
442 return 2; /* Pause game */
443 #endif
445 key = rb->button_status(); /* ignore BUTTON_REPEAT */
447 if(key & PONG_LEFT_DOWN) /* player left goes down */
448 padmove(&p->w_pad[0], MOVE_STEP);
450 if(key & PONG_LEFT_UP) /* player left goes up */
451 padmove(&p->w_pad[0], -MOVE_STEP);
453 if(key & PONG_RIGHT_DOWN) /* player right goes down */
454 padmove(&p->w_pad[1], MOVE_STEP);
456 if(key & PONG_RIGHT_UP) /* player right goes up */
457 padmove(&p->w_pad[1], -MOVE_STEP);
459 if(rb->default_event_handler(key) == SYS_USB_CONNECTED)
460 return -1; /* exit game because of USB */
462 return 1; /* return 0 to exit game */
465 void showscore(struct pong *p)
467 static char buffer[20];
468 int w;
470 rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]);
471 w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL);
472 rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer);
475 /* this is the plugin entry point */
476 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
478 struct pong pong;
479 int game = 1;
481 /* init the struct with some silly values to start with */
483 pong.ballx = 20*RES;
484 pong.bally = 20*RES;
486 pong.e_pad[0] = 0;
487 pong.w_pad[0] = 7;
488 pong.e_pad[1] = 0;
489 pong.w_pad[1] = 40;
491 pong.ballspeedx = SPEEDX;
492 pong.ballspeedy = SPEEDY;
494 pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
496 /* if you don't use the parameter, you can do like
497 this to avoid the compiler warning about it */
498 (void)parameter;
500 rb = api; /* use the "standard" rb pointer */
502 /* Clear screen */
503 rb->lcd_clear_display();
505 /* go go go */
506 while(game > 0) {
507 if (game == 2) { /* Game Paused */
508 rb->splash(0, "PAUSED");
509 while(game == 2)
510 game = keys(&pong); /* short circuit */
511 rb->lcd_clear_display();
513 showscore(&pong);
514 pad(&pong, 0); /* draw left pad */
515 pad(&pong, 1); /* draw right pad */
516 ball(&pong); /* move and draw ball */
518 rb->lcd_update();
520 game = keys(&pong); /* deal with keys */
523 return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
526 #endif /* HAVE_LCD_BITMAP */