Get rid of the 'center' parameter for splashes. There were only 2 of almost 500 splas...
[Rockbox.git] / apps / plugins / pong.c
blobb799a0a37adec0b21de0822adae30c3fac0c679b
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 #define PONG_QUIT BUTTON_SELECT
82 #define PONG_LEFT_UP BUTTON_MENU
83 #define PONG_LEFT_DOWN BUTTON_LEFT
84 #define PONG_RIGHT_UP BUTTON_RIGHT
85 #define PONG_RIGHT_DOWN BUTTON_PLAY
87 #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
88 #define PONG_QUIT BUTTON_POWER
89 #define PONG_LEFT_UP BUTTON_UP
90 #define PONG_LEFT_DOWN BUTTON_DOWN
91 #define PONG_RIGHT_UP BUTTON_REC
92 #define PONG_RIGHT_DOWN BUTTON_PLAY
94 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
95 #define PONG_QUIT BUTTON_A
96 #define PONG_PAUSE BUTTON_MENU
97 #define PONG_LEFT_UP BUTTON_UP
98 #define PONG_LEFT_DOWN BUTTON_DOWN
99 #define PONG_RIGHT_UP BUTTON_VOL_UP
100 #define PONG_RIGHT_DOWN BUTTON_VOL_DOWN
102 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
103 #define PONG_QUIT BUTTON_POWER
104 #define PONG_PAUSE BUTTON_SELECT
105 #define PONG_LEFT_UP BUTTON_LEFT
106 #define PONG_LEFT_DOWN BUTTON_DOWN
107 #define PONG_RIGHT_UP BUTTON_UP
108 #define PONG_RIGHT_DOWN BUTTON_RIGHT
110 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
111 #define PONG_QUIT BUTTON_POWER
112 #define PONG_LEFT_UP BUTTON_SCROLL_UP
113 #define PONG_LEFT_DOWN BUTTON_SCROLL_DOWN
114 #define PONG_RIGHT_UP BUTTON_REW
115 #define PONG_RIGHT_DOWN BUTTON_FF
117 #endif
119 static struct plugin_api* rb;
121 struct pong {
122 int ballx; /* current X*RES position of the ball */
123 int bally; /* current Y*RES position of the ball */
124 int w_pad[2]; /* wanted current Y positions of pads */
125 int e_pad[2]; /* existing current Y positions of pads */
126 int ballspeedx; /* */
127 int ballspeedy; /* */
129 int score[2];
132 void singlepad(int x, int y, int set)
134 if(set) {
135 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
137 else {
138 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
139 rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
140 rb->lcd_set_drawmode(DRMODE_SOLID);
144 void pad(struct pong *p, int pad)
146 static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH};
148 /* clear existing pad */
149 singlepad(xpos[pad], p->e_pad[pad], 0);
151 /* draw wanted pad */
152 singlepad(xpos[pad], p->w_pad[pad], 1);
154 /* existing is now the wanted */
155 p->e_pad[pad] = p->w_pad[pad];
158 bool wallcollide(struct pong *p, int pad)
160 /* we have already checked for pad-collision, just check if this hits
161 the wall */
162 if(pad) {
163 /* right-side */
164 if(p->ballx > LCD_WIDTH*RES)
165 return true;
167 else {
168 if(p->ballx < 0)
169 return true;
171 return false;
174 /* returns true if the ball has hit a pad, and then the info variable
175 will have extra angle info */
177 bool padcollide(struct pong *p, int pad, int *info)
179 int x = p->ballx/RES;
180 int y = p->bally/RES;
182 if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
183 (y + BALL_HEIGHT > p->e_pad[pad])) {
184 /* Y seems likely right */
186 /* store the delta between ball-middle MINUS pad-middle, so
187 it returns:
188 0 when the ball hits exactly the middle of the pad
189 positive numbers when the ball is below the middle of the pad
190 negative numbers when the ball is above the middle of the pad
192 max number is +- PAD_HEIGHT/2
195 *info = (y+BALL_HEIGHT/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
197 if(pad) {
198 /* right-side */
199 if((x + BALL_WIDTH) >= (LCD_WIDTH - PAD_WIDTH))
200 return true; /* phump */
202 else {
203 if(x <= PAD_WIDTH)
204 return true;
207 return false; /* nah */
210 void bounce(struct pong *p, int pad, int info)
212 (void)pad; /* not used right now */
213 p->ballspeedx = -p->ballspeedx;
215 /* info is the hit-angle into the pad */
216 if(p->ballspeedy > 0) {
217 /* downwards */
218 if(info > 0) {
219 /* below the middle of the pad */
220 p->ballspeedy += info * RES/3;
222 else if(info < 0) {
223 /* above the middle */
224 p->ballspeedy = info * RES/2;
227 else {
228 /* upwards */
229 if(info > 0) {
230 /* below the middle of the pad */
231 p->ballspeedy = info * RES/2;
233 else if(info < 0) {
234 /* above the middle */
235 p->ballspeedy -= info * RES/3;
239 p->ballspeedy += rb->rand()%21-10;
241 #if 0
242 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
243 #endif
246 void score(struct pong *p, int pad)
248 rb->splash(HZ/4, "%s scores!", pad?"right":"left");
249 rb->lcd_clear_display();
250 p->score[pad]++;
252 /* then move the X-speed of the ball and give it a random Y position */
253 p->ballspeedx = -p->ballspeedx;
254 p->bally = rb->rand()%(LCD_HEIGHT*RES - BALL_HEIGHT);
256 /* avoid hitting the pad with the new ball */
257 p->ballx = (p->ballx < 0) ?
258 (RES * PAD_WIDTH) : (RES * (LCD_WIDTH - PAD_WIDTH - BALL_WIDTH));
260 /* restore Y-speed to default */
261 p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
263 /* set the existing pad positions to something weird to force pad
264 updates */
265 p->e_pad[0] = -1;
266 p->e_pad[1] = -1;
269 void ball(struct pong *p)
271 int x = p->ballx/RES;
272 int y = p->bally/RES;
274 int newx;
275 int newy;
277 int info;
279 /* movement */
280 p->ballx += p->ballspeedx;
281 p->bally += p->ballspeedy;
283 newx = p->ballx/RES;
284 newy = p->bally/RES;
286 /* detect if ball hits a wall */
287 if(newy + BALL_HEIGHT > LCD_HEIGHT) {
288 /* hit floor, bounce */
289 p->ballspeedy = -p->ballspeedy;
290 newy = LCD_HEIGHT - BALL_HEIGHT;
291 p->bally = newy * RES;
293 else if(newy < 0) {
294 /* hit ceiling, bounce */
295 p->ballspeedy = -p->ballspeedy;
296 p->bally = 0;
297 newy = 0;
300 /* detect if ball hit pads */
301 if(padcollide(p, 0, &info))
302 bounce(p, 0, info);
303 else if(padcollide(p, 1, &info))
304 bounce(p, 1, info);
305 else if(wallcollide(p, 0))
306 score(p, 1);
307 else if(wallcollide(p, 1))
308 score(p, 0);
310 newx = p->ballx/RES;
311 newy = p->bally/RES;
313 /* clear old position */
314 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
315 rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGHT);
316 rb->lcd_set_drawmode(DRMODE_SOLID);
318 /* draw the new ball position */
319 rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGHT);
322 void padmove(int *pos, int dir)
324 *pos += dir;
325 if(*pos > (LCD_HEIGHT-PAD_HEIGHT))
326 *pos = (LCD_HEIGHT-PAD_HEIGHT);
327 else if(*pos < 0)
328 *pos = 0;
331 int keys(struct pong *p)
333 int key;
334 #ifdef PONG_PAUSE
335 static bool pause = false;
336 #endif
338 int time = 4; /* number of ticks this function will loop reading keys */
339 int start = *rb->current_tick;
340 int end = start + time;
342 while(end > *rb->current_tick) {
343 key = rb->button_get_w_tmo(end - *rb->current_tick);
345 #ifdef HAS_BUTTON_HOLD
346 if (rb->button_hold())
347 return 2; /* Pause game */
348 #endif
350 if(key & PONG_QUIT
351 #ifdef PONG_RC_QUIT
352 || key & PONG_RC_QUIT
353 #endif
355 return 0; /* exit game NOW */
357 #ifdef PONG_PAUSE
358 if(key == PONG_PAUSE)
359 pause = !pause;
360 if(pause)
361 return 2; /* Pause game */
362 #endif
364 key = rb->button_status(); /* ignore BUTTON_REPEAT */
366 if(key & PONG_LEFT_DOWN) /* player left goes down */
367 padmove(&p->w_pad[0], MOVE_STEP);
369 if(key & PONG_LEFT_UP) /* player left goes up */
370 padmove(&p->w_pad[0], -MOVE_STEP);
372 if(key & PONG_RIGHT_DOWN) /* player right goes down */
373 padmove(&p->w_pad[1], MOVE_STEP);
375 if(key & PONG_RIGHT_UP) /* player right goes up */
376 padmove(&p->w_pad[1], -MOVE_STEP);
378 if(rb->default_event_handler(key) == SYS_USB_CONNECTED)
379 return -1; /* exit game because of USB */
381 return 1; /* return 0 to exit game */
384 void showscore(struct pong *p)
386 static char buffer[20];
387 int w;
389 rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]);
390 w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL);
391 rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer);
394 /* this is the plugin entry point */
395 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
397 struct pong pong;
398 int game = 1;
400 /* init the struct with some silly values to start with */
402 pong.ballx = 20*RES;
403 pong.bally = 20*RES;
405 pong.e_pad[0] = 0;
406 pong.w_pad[0] = 7;
407 pong.e_pad[1] = 0;
408 pong.w_pad[1] = 40;
410 pong.ballspeedx = SPEEDX;
411 pong.ballspeedy = SPEEDY;
413 pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
415 /* if you don't use the parameter, you can do like
416 this to avoid the compiler warning about it */
417 (void)parameter;
419 rb = api; /* use the "standard" rb pointer */
421 /* Clear screen */
422 rb->lcd_clear_display();
424 /* go go go */
425 while(game > 0) {
426 if (game == 2) { /* Game Paused */
427 rb->splash(0, "PAUSED");
428 while(game == 2)
429 game = keys(&pong); /* short circuit */
430 rb->lcd_clear_display();
432 showscore(&pong);
433 pad(&pong, 0); /* draw left pad */
434 pad(&pong, 1); /* draw right pad */
435 ball(&pong); /* move and draw ball */
437 rb->lcd_update();
439 game = keys(&pong); /* deal with keys */
442 return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
445 #endif /* HAVE_LCD_BITMAP */