Fix warning about missing newline at the EOF
[maemo-rb.git] / apps / plugins / fireworks.c
blob52b11b7079fa2ad0895c79d47081c209c796ee01
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Zakk Roberts
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"
22 #include "lib/helper.h"
23 #include "lib/playback_control.h"
27 /***
28 * FIREWORKS.C by ZAKK ROBERTS
29 * Rockbox plugin simulating a fireworks display.
30 * Supports all bitmap LCDs, fully scalable.
31 * Currently disabled for Archos Recorder - runs too slow.
32 ***/
34 /* All sorts of keymappings.. */
35 #if (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
36 #define BTN_MENU BUTTON_OFF
37 #define BTN_FIRE BUTTON_SELECT
39 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
40 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
41 #define BTN_MENU BUTTON_MENU
42 #define BTN_FIRE BUTTON_SELECT
44 #elif (CONFIG_KEYPAD == RECORDER_PAD)
45 #define BTN_MENU BUTTON_OFF
46 #define BTN_FIRE BUTTON_PLAY
48 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
49 #define BTN_MENU BUTTON_OFF
50 #define BTN_FIRE BUTTON_SELECT
52 #elif (CONFIG_KEYPAD == ONDIO_PAD)
53 #define BTN_MENU BUTTON_MENU
54 #define BTN_FIRE BUTTON_UP
56 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
57 #define BTN_MENU BUTTON_POWER
58 #define BTN_FIRE BUTTON_SELECT
60 #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
61 #define BTN_MENU BUTTON_MODE
62 #define BTN_FIRE BUTTON_SELECT
64 #elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
65 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
66 (CONFIG_KEYPAD == MROBE100_PAD) || \
67 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
68 #define BTN_MENU BUTTON_MENU
69 #define BTN_FIRE BUTTON_SELECT
71 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
72 (CONFIG_KEYPAD == SANSA_C200_PAD) || \
73 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
74 #define BTN_MENU BUTTON_POWER
75 #define BTN_FIRE BUTTON_SELECT
77 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
78 #define BTN_MENU (BUTTON_HOME|BUTTON_REPEAT)
79 #define BTN_FIRE BUTTON_SELECT
81 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
82 #define BTN_MENU BUTTON_POWER
83 #define BTN_FIRE BUTTON_PLAY
85 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
86 #define BTN_MENU BUTTON_RC_REC
87 #define BTN_FIRE BUTTON_RC_PLAY
89 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
90 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
92 #elif CONFIG_KEYPAD == IAUDIO67_PAD
93 #define BTN_MENU BUTTON_MENU
94 #define BTN_FIRE BUTTON_PLAY
96 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
97 #define BTN_MENU BUTTON_MENU
98 #define BTN_FIRE BUTTON_SELECT
100 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
101 #define BTN_MENU BUTTON_MENU
102 #define BTN_FIRE BUTTON_SELECT
104 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
105 #define BTN_MENU BUTTON_MENU
106 #define BTN_FIRE BUTTON_PLAY
108 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
109 #define BTN_MENU BUTTON_MENU
110 #define BTN_FIRE BUTTON_PLAY
112 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
113 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
115 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
116 #define BTN_MENU BUTTON_LEFT
117 #define BTN_FIRE BUTTON_PLAY
119 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
120 #define BTN_MENU BUTTON_MENU
121 #define BTN_FIRE BUTTON_OK
123 #elif (CONFIG_KEYPAD == MPIO_HD200_PAD)
124 #define BTN_MENU BUTTON_REC
125 #define BTN_FIRE BUTTON_PLAY
127 #elif (CONFIG_KEYPAD == MPIO_HD300_PAD)
128 #define BTN_MENU BUTTON_MENU
129 #define BTN_FIRE BUTTON_PLAY
131 #elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
132 #define BTN_MENU BUTTON_SELECT
133 #define BTN_FIRE BUTTON_PLAYPAUSE
135 #elif defined(HAVE_TOUCHSCREEN)
136 /* This is a touchscreen target */
137 #else
138 #error No keymap defined!
139 #endif
141 #ifdef HAVE_TOUCHSCREEN
142 #ifndef BTN_MENU
143 #define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
144 #endif
145 #ifndef BTN_FIRE
146 #define BTN_FIRE BUTTON_CENTER
147 #endif
148 #endif
150 /* The lowdown on source terminology:
151 * a ROCKET is launched from the LCD bottom.
152 * FIREWORKs are ejected from the rocket when it explodes. */
154 #define MAX_ROCKETS 40
155 #define ROCKET_LIFE (LCD_HEIGHT/2)
156 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
157 #define ROCKET_SIZE 2
158 #define ROCKET_MOVEMENT_RANGE 4
159 #define ROCKET_TRAIL_PARTICLES 50
161 #define MAX_FIREWORKS 40
162 #define FIREWORK_MOVEMENT_RANGE 6
163 #define FIREWORK_SIZE 2
165 /* position, speed, "phase" (age), color of all fireworks */
166 int firework_xpoints[MAX_ROCKETS][MAX_FIREWORKS];
167 int firework_ypoints[MAX_ROCKETS][MAX_FIREWORKS];
168 int firework_xspeed[MAX_ROCKETS][MAX_FIREWORKS];
169 int firework_yspeed[MAX_ROCKETS][MAX_FIREWORKS];
170 int firework_phase[MAX_ROCKETS];
171 #ifdef HAVE_LCD_COLOR
172 int firework_color[MAX_ROCKETS][MAX_FIREWORKS];
173 #endif
175 /* position, speed, "phase" (age) of all rockets */
176 int rocket_xpos[MAX_ROCKETS];
177 int rocket_ypos[MAX_ROCKETS];
178 int rocket_xspeed[MAX_ROCKETS];
179 int rocket_yspeed[MAX_ROCKETS];
180 int rocket_phase[MAX_ROCKETS];
181 int rocket_targetphase[MAX_ROCKETS];
183 /* settings values. these should eventually be saved to
184 * disk. maybe a preset loading/saving system? */
185 int autofire_delay = 0;
186 int particles_per_firework = 2;
187 int particle_life = 1;
188 int gravity = 1;
189 int show_rockets = 1;
190 int frames_per_second = 4;
191 bool quit_plugin = false;
193 /* firework colors:
194 * firework_colors = brightest firework color, used most of the time.
195 * DARK colors = fireworks are nearly burnt out.
196 * DARKER colors = fireworks are several frames away from burning out.
197 * DARKEST colors = fireworks are a couple frames from burning out. */
198 #ifdef HAVE_LCD_COLOR
199 static const unsigned firework_colors[] = {
200 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
201 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
202 LCD_RGBPACK(151,84,213) };
204 static const unsigned firework_dark_colors[] = {
205 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
206 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
207 LCD_RGBPACK(75,42,105) };
209 static const unsigned firework_darker_colors[] = {
210 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
211 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
212 LCD_RGBPACK(38,21,52) };
214 static const unsigned firework_darkest_colors[] = {
215 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
216 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
217 LCD_RGBPACK(19,10,26) };
219 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
221 #endif
223 static const struct opt_items autofire_delay_settings[15] = {
224 { "Off", -1 },
225 { "50ms", -1 },
226 { "100ms", -1 },
227 { "200ms", -1 },
228 { "300ms", -1 },
229 { "400ms", -1 },
230 { "500ms", -1 },
231 { "600ms", -1 },
232 { "700ms", -1 },
233 { "800ms", -1 },
234 { "900ms", -1 },
235 { "1s", -1 },
236 { "2s", -1 },
237 { "3s", -1 },
238 { "4s", -1 }
241 int autofire_delay_values[15] = {
242 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
244 static const struct opt_items particle_settings[8] = {
245 { "5", -1 },
246 { "10", -1 },
247 { "15", -1 },
248 { "20", -1 },
249 { "25", -1 },
250 { "30", -1 },
251 { "35", -1 },
252 { "40", -1 },
255 int particle_values[8] = {
256 5, 10, 15, 20, 25, 30, 35, 40 };
258 static const struct opt_items particle_life_settings[9] = {
259 { "20 cycles", -1 },
260 { "30 cycles", -1 },
261 { "40 cycles", -1 },
262 { "50 cycles", -1 },
263 { "60 cycles", -1 },
264 { "70 cycles", -1 },
265 { "80 cycles", -1 },
266 { "90 cycles", -1 },
267 { "100 cycles", -1 }
270 int particle_life_values[9] = {
271 20, 30, 40, 50, 60, 70, 80, 90, 100 };
273 static const struct opt_items gravity_settings[4] = {
274 { "Off", -1 },
275 { "Weak", -1 },
276 { "Moderate", -1 },
277 { "Strong", -1 },
280 int gravity_values[4] = {
281 0, 30, 20, 10 };
283 #ifdef HAVE_LCD_COLOR
285 static const struct opt_items rocket_settings[3] = {
286 { "No", -1 },
287 { "Yes (no trails)", -1 },
288 { "Yes (with trails)", -1 },
290 int rocket_values[4] = {
291 2, 1, 0 };
293 #else
295 static const struct opt_items rocket_settings[2] = {
296 { "No", -1 },
297 { "Yes", -1 },
299 int rocket_values[4] = {
300 1, 0 };
302 #endif
304 static const struct opt_items fps_settings[9] = {
305 { "20 FPS", -1 },
306 { "25 FPS", -1 },
307 { "30 FPS", -1 },
308 { "35 FPS", -1 },
309 { "40 FPS", -1 },
310 { "45 FPS", -1 },
311 { "50 FPS", -1 },
312 { "55 FPS", -1 },
313 { "60 FPS", -1 }
316 int fps_values[9] = {
317 20, 25, 30, 35, 40, 45, 50, 55, 60 };
319 MENUITEM_STRINGLIST(menu, "Fireworks Menu", NULL,
320 "Start Demo", "Auto-Fire", "Particles Per Firework",
321 "Particle Life", "Gravity", "Show Rockets",
322 "FPS (Speed)", "Playback Control", "Quit");
324 /* called on startup. initializes all variables, etc */
325 static void init_all(void)
327 int j;
329 for(j=0; j<MAX_ROCKETS; j++)
331 rocket_phase[j] = -1;
332 firework_phase[j] = -1;
336 /* called when a rocket hits its destination height.
337 * prepares all associated fireworks to be expelled. */
338 static void init_explode(int x, int y, int firework, int points)
340 int i;
342 for(i=0; i<points; i++)
344 rb->srand(*rb->current_tick * i);
346 firework_xpoints[firework][i] = x;
347 firework_ypoints[firework][i] = y;
349 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
350 - FIREWORK_MOVEMENT_RANGE/2;
351 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
352 - FIREWORK_MOVEMENT_RANGE/2;
354 #ifdef HAVE_LCD_COLOR
355 firework_color[firework][i] = rb->rand() % 7;
356 #endif
360 /* called when a rocket is launched.
361 * prepares said rocket to start moving towards its destination. */
362 static void init_rocket(int rocket)
364 rb->srand(*rb->current_tick);
366 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
367 rocket_ypos[rocket] = LCD_HEIGHT;
369 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE)
370 - ROCKET_MOVEMENT_RANGE/2;
371 rocket_yspeed[rocket] = 3;
373 rocket_phase[rocket] = 0;
374 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR))
375 / rocket_yspeed[rocket];
378 /* startup/configuration menu. */
379 static void fireworks_menu(void)
381 int selected = 0, result;
382 bool menu_quit = false;
384 rb->lcd_setfont(FONT_UI);
385 #ifdef HAVE_LCD_COLOR
386 rb->lcd_set_background(LCD_BLACK);
387 rb->lcd_set_foreground(LCD_WHITE);
388 #endif
389 rb->lcd_clear_display();
390 rb->lcd_update();
392 rb->button_clear_queue();
394 while(!menu_quit)
396 result = rb->do_menu(&menu, &selected, NULL, false);
398 switch(result)
400 case 0:
401 rb->lcd_setfont(FONT_SYSFIXED);
403 #ifdef HAVE_LCD_COLOR
404 rb->lcd_set_background(LCD_BLACK);
405 rb->lcd_set_foreground(LCD_WHITE);
406 #endif
408 rb->lcd_clear_display();
409 rb->lcd_update();
411 init_all();
412 menu_quit = true;
413 break;
415 case 1:
416 rb->set_option("Auto-Fire", &autofire_delay, INT,
417 autofire_delay_settings, 15, NULL);
418 break;
420 case 2:
421 rb->set_option("Particles Per Firework", &particles_per_firework,
422 INT, particle_settings, 8, NULL);
423 break;
425 case 3:
426 rb->set_option("Particle Life", &particle_life, INT,
427 particle_life_settings, 9, NULL);
428 break;
430 case 4:
431 rb->set_option("Gravity", &gravity, INT,
432 gravity_settings, 4, NULL);
433 break;
435 case 5:
436 rb->set_option("Show Rockets", &show_rockets, INT,
437 rocket_settings, 3, NULL);
438 break;
440 case 6:
441 rb->set_option("FPS (Speed)", &frames_per_second, INT,
442 fps_settings, 9, NULL);
443 break;
445 case 7:
446 playback_control(NULL);
447 break;
449 case 8:
450 quit_plugin = true;
451 menu_quit = true;
452 break;
457 /* this is the plugin entry point */
458 enum plugin_status plugin_start(const void* parameter)
460 (void)parameter;
462 int j, i;
463 int thisrocket=0;
464 int start_tick, elapsed_tick;
465 int button;
467 /* set everything up.. no BL timeout, no backdrop,
468 white-text-on-black-background. */
469 backlight_ignore_timeout();
470 #if LCD_DEPTH > 1
471 rb->lcd_set_backdrop(NULL);
472 rb->lcd_set_background(LCD_BLACK);
473 rb->lcd_set_foreground(LCD_WHITE);
474 #endif
476 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
477 rb->cpu_boost(true);
478 #endif
480 fireworks_menu();
482 start_tick = *rb->current_tick;
484 while(!quit_plugin)
486 rb->lcd_clear_display();
488 /* loop through every possible rocket */
489 for(j=0; j<MAX_ROCKETS; j++)
491 /* if the current rocket is actually moving/"alive" then go on and
492 * move/update/explode it */
493 if(rocket_phase[j] > -1)
495 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
496 if(show_rockets==2)
498 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
499 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
500 ROCKET_SIZE, ROCKET_SIZE);
501 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
502 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j],
503 rocket_ypos[j]+rocket_yspeed[j],
504 ROCKET_SIZE, ROCKET_SIZE);
506 #endif
508 /* move rocket */
509 rocket_xpos[j] += rocket_xspeed[j];
510 rocket_ypos[j] -= rocket_yspeed[j];
512 #ifdef HAVE_LCD_COLOR
513 rb->lcd_set_foreground(LCD_WHITE);
514 #endif
515 if(show_rockets==2 || show_rockets==1)
516 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
517 ROCKET_SIZE, ROCKET_SIZE);
519 /* if(rocket isn't "there" yet) keep moving
520 * if(rocket IS there) explode it. */
521 if(rocket_phase[j] < rocket_targetphase[j])
522 rocket_phase[j]++;
523 else
525 rocket_phase[j] = -1;
527 firework_phase[j] = 0;
528 init_explode(rocket_xpos[j], rocket_ypos[j], j,
529 particle_values[particles_per_firework]);
533 /* and now onto the fireworks for this particular rocket... */
534 if(firework_phase[j] > -1)
536 for(i=0; i<particle_values[particles_per_firework]; i++)
538 firework_xpoints[j][i] += firework_xspeed[j][i];
539 firework_ypoints[j][i] += firework_yspeed[j][i];
541 if(gravity != 0)
542 firework_ypoints[j][i] += firework_phase[j]
543 /gravity_values[gravity];
545 #ifdef HAVE_LCD_COLOR
546 rb->lcd_set_foreground(
547 firework_darkest_colors[firework_color[j][i]]);
548 rb->lcd_fillrect(firework_xpoints[j][i]-1,
549 firework_ypoints[j][i]-1,
550 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
552 int phase_left = particle_life_values[particle_life]
553 - firework_phase[j];
554 if(phase_left > 10)
555 rb->lcd_set_foreground(
556 firework_colors[firework_color[j][i]]);
557 else if(phase_left > 7)
558 rb->lcd_set_foreground(
559 firework_dark_colors[firework_color[j][i]]);
560 else if(phase_left > 3)
561 rb->lcd_set_foreground(
562 firework_darker_colors[firework_color[j][i]]);
563 else
564 rb->lcd_set_foreground(
565 firework_darkest_colors[firework_color[j][i]]);
566 #endif
567 rb->lcd_fillrect(firework_xpoints[j][i],
568 firework_ypoints[j][i],
569 FIREWORK_SIZE, FIREWORK_SIZE);
570 /* WIP - currently ugly explosion effect
571 #ifdef HAVE_LCD_COLOR
572 if(firework_phase[j] < 10)
574 rb->lcd_set_foreground(EXPLOSION_COLOR);
575 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
576 rocket_ypos[j]-firework_phase[j],
577 firework_phase[j]*2, firework_phase[j]*2);
579 #endif */
582 #ifdef HAVE_LCD_COLOR
583 rb->lcd_set_foreground(LCD_WHITE);
584 #endif
586 /* firework at its destination age?
587 * no = keep aging; yes = delete it. */
588 if(firework_phase[j] < particle_life_values[particle_life])
589 firework_phase[j]++;
590 else
591 firework_phase[j] = -1;
595 /* is autofire on? */
596 if(autofire_delay != 0)
598 elapsed_tick = *rb->current_tick - start_tick;
600 if(elapsed_tick > autofire_delay_values[autofire_delay])
602 init_rocket(thisrocket);
603 if(++thisrocket == MAX_ROCKETS)
604 thisrocket = 0;
606 start_tick = *rb->current_tick;
610 rb->lcd_update();
612 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
613 switch(button)
615 case BTN_MENU: /* back to config menu */
616 fireworks_menu();
617 break;
619 case BTN_FIRE: /* fire off rockets manually */
620 case BTN_FIRE|BUTTON_REPEAT:
621 init_rocket(thisrocket);
622 if(++thisrocket == MAX_ROCKETS)
623 thisrocket=0;
624 break;
627 /* Turn on backlight timeout (revert to settings) */
628 backlight_use_settings();
630 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
631 rb->cpu_boost(false);
632 #endif
634 return PLUGIN_OK;