FS#10075 - FUZE: QUIT Plugin by selecting BUTTON_HOME by Johannes Schwarz. Updates...
[kugel-rb/myfork.git] / apps / plugins / fireworks.c
blob726b3eb3811785972814bdd911df06c598a59603
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/oldmenuapi.h"
23 #include "lib/helper.h"
24 #include "lib/playback_control.h"
26 PLUGIN_HEADER
28 /***
29 * FIREWORKS.C by ZAKK ROBERTS
30 * Rockbox plugin simulating a fireworks display.
31 * Supports all bitmap LCDs, fully scalable.
32 * Currently disabled for Archos Recorder - runs too slow.
33 ***/
35 /* All sorts of keymappings.. */
36 #if (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
37 #define BTN_MENU BUTTON_OFF
38 #define BTN_FIRE BUTTON_SELECT
40 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
41 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
42 #define BTN_MENU BUTTON_MENU
43 #define BTN_FIRE BUTTON_SELECT
45 #elif (CONFIG_KEYPAD == RECORDER_PAD)
46 #define BTN_MENU BUTTON_OFF
47 #define BTN_FIRE BUTTON_PLAY
49 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
50 #define BTN_MENU BUTTON_OFF
51 #define BTN_FIRE BUTTON_SELECT
53 #elif (CONFIG_KEYPAD == ONDIO_PAD)
54 #define BTN_MENU BUTTON_MENU
55 #define BTN_FIRE BUTTON_UP
57 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
58 #define BTN_MENU BUTTON_POWER
59 #define BTN_FIRE BUTTON_SELECT
61 #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
62 #define BTN_MENU BUTTON_MODE
63 #define BTN_FIRE BUTTON_SELECT
65 #elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
66 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
67 (CONFIG_KEYPAD == MROBE100_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 #define BTN_MENU BUTTON_POWER
74 #define BTN_FIRE BUTTON_SELECT
76 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
77 #define BTN_MENU (BUTTON_HOME|BUTTON_REPEAT)
78 #define BTN_FIRE BUTTON_SELECT
80 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
81 #define BTN_MENU BUTTON_POWER
82 #define BTN_FIRE BUTTON_PLAY
84 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
85 #define BTN_MENU BUTTON_RC_REC
86 #define BTN_FIRE BUTTON_RC_PLAY
88 #elif (CONFIG_KEYPAD == COWOND2_PAD)
89 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
91 #elif CONFIG_KEYPAD == IAUDIO67_PAD
92 #define BTN_MENU BUTTON_MENU
93 #define BTN_FIRE BUTTON_PLAY
95 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
96 #define BTN_MENU BUTTON_MENU
97 #define BTN_FIRE BUTTON_SELECT
99 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
100 #define BTN_MENU BUTTON_MENU
101 #define BTN_FIRE BUTTON_SELECT
103 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
104 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
106 #else
107 #error No keymap defined!
108 #endif
110 #ifdef HAVE_TOUCHSCREEN
111 #ifndef BTN_MENU
112 #define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
113 #endif
114 #ifndef BTN_FIRE
115 #define BTN_FIRE BUTTON_CENTER
116 #endif
117 #endif
119 /* The lowdown on source terminology:
120 * a ROCKET is launched from the LCD bottom.
121 * FIREWORKs are ejected from the rocket when it explodes. */
123 #define MAX_ROCKETS 40
124 #define ROCKET_LIFE (LCD_HEIGHT/2)
125 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
126 #define ROCKET_SIZE 2
127 #define ROCKET_MOVEMENT_RANGE 4
128 #define ROCKET_TRAIL_PARTICLES 50
130 #define MAX_FIREWORKS 40
131 #define FIREWORK_MOVEMENT_RANGE 6
132 #define FIREWORK_SIZE 2
134 /* position, speed, "phase" (age), color of all fireworks */
135 int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
136 int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
137 int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
138 int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
139 int firework_phase[MAX_ROCKETS+1];
140 #ifdef HAVE_LCD_COLOR
141 int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
142 #endif
144 /* position, speed, "phase" (age) of all rockets */
145 int rocket_xpos[MAX_ROCKETS+1];
146 int rocket_ypos[MAX_ROCKETS+1];
147 int rocket_xspeed[MAX_ROCKETS+1];
148 int rocket_yspeed[MAX_ROCKETS+1];
149 int rocket_phase[MAX_ROCKETS+1];
150 int rocket_targetphase[MAX_ROCKETS+1];
152 /* settings values. these should eventually be saved to
153 * disk. maybe a preset loading/saving system? */
154 int autofire_delay = 0;
155 int particles_per_firework = 2;
156 int particle_life = 1;
157 int gravity = 1;
158 int show_rockets = 1;
159 int frames_per_second = 4;
160 bool quit_plugin = false;
162 /* firework colors:
163 * firework_colors = brightest firework color, used most of the time.
164 * DARK colors = fireworks are nearly burnt out.
165 * DARKER colors = fireworks are several frames away from burning out.
166 * DARKEST colors = fireworks are a couple frames from burning out. */
167 #ifdef HAVE_LCD_COLOR
168 static const unsigned firework_colors[] = {
169 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
170 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
171 LCD_RGBPACK(151,84,213) };
173 static const unsigned firework_dark_colors[] = {
174 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
175 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
176 LCD_RGBPACK(75,42,105) };
178 static const unsigned firework_darker_colors[] = {
179 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
180 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
181 LCD_RGBPACK(38,21,52) };
183 static const unsigned firework_darkest_colors[] = {
184 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
185 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
186 LCD_RGBPACK(19,10,26) };
188 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
190 #endif
192 static const struct opt_items autofire_delay_settings[15] = {
193 { "Off", -1 },
194 { "50ms", -1 },
195 { "100ms", -1 },
196 { "200ms", -1 },
197 { "300ms", -1 },
198 { "400ms", -1 },
199 { "500ms", -1 },
200 { "600ms", -1 },
201 { "700ms", -1 },
202 { "800ms", -1 },
203 { "900ms", -1 },
204 { "1s", -1 },
205 { "2s", -1 },
206 { "3s", -1 },
207 { "4s", -1 }
210 int autofire_delay_values[15] = {
211 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
213 static const struct opt_items particle_settings[8] = {
214 { "5", -1 },
215 { "10", -1 },
216 { "15", -1 },
217 { "20", -1 },
218 { "25", -1 },
219 { "30", -1 },
220 { "35", -1 },
221 { "40", -1 },
224 int particle_values[8] = {
225 5, 10, 15, 20, 25, 30, 35, 40 };
227 static const struct opt_items particle_life_settings[9] = {
228 { "20 cycles", -1 },
229 { "30 cycles", -1 },
230 { "40 cycles", -1 },
231 { "50 cycles", -1 },
232 { "60 cycles", -1 },
233 { "70 cycles", -1 },
234 { "80 cycles", -1 },
235 { "90 cycles", -1 },
236 { "100 cycles", -1 }
239 int particle_life_values[9] = {
240 20, 30, 40, 50, 60, 70, 80, 90, 100 };
242 static const struct opt_items gravity_settings[4] = {
243 { "Off", -1 },
244 { "Weak", -1 },
245 { "Moderate", -1 },
246 { "Strong", -1 },
249 int gravity_values[4] = {
250 0, 30, 20, 10 };
252 #ifdef HAVE_LCD_COLOR
254 static const struct opt_items rocket_settings[3] = {
255 { "No", -1 },
256 { "Yes (no trails)", -1 },
257 { "Yes (with trails)", -1 },
259 int rocket_values[4] = {
260 2, 1, 0 };
262 #else
264 static const struct opt_items rocket_settings[2] = {
265 { "No", -1 },
266 { "Yes", -1 },
268 int rocket_values[4] = {
269 1, 0 };
271 #endif
273 static const struct opt_items fps_settings[9] = {
274 { "20 FPS", -1 },
275 { "25 FPS", -1 },
276 { "30 FPS", -1 },
277 { "35 FPS", -1 },
278 { "40 FPS", -1 },
279 { "45 FPS", -1 },
280 { "50 FPS", -1 },
281 { "55 FPS", -1 },
282 { "60 FPS", -1 }
285 int fps_values[9] = {
286 20, 25, 30, 35, 40, 45, 50, 55, 60 };
288 static const struct menu_item items[] = {
289 { "Start Demo", NULL },
290 { "Auto-Fire", NULL },
291 { "Particles Per Firework", NULL },
292 { "Particle Life", NULL },
293 { "Gravity", NULL },
294 { "Show Rockets", NULL },
295 { "FPS (Speed)", NULL },
296 { "Playback Control", NULL },
297 { "Quit", NULL }
300 /* called on startup. initializes all variables, etc */
301 void init_all(void)
303 int j;
305 for(j=0; j<MAX_ROCKETS; j++)
306 firework_phase[j] = -1;
309 /* called when a rocket hits its destination height.
310 * prepares all associated fireworks to be expelled. */
311 void init_explode(int x, int y, int firework, int points)
313 int i;
315 for(i=0; i<points; i++)
317 rb->srand(*rb->current_tick * i);
319 firework_xpoints[firework][i] = x;
320 firework_ypoints[firework][i] = y;
322 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
323 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
325 #ifdef HAVE_LCD_COLOR
326 firework_color[firework][i] = rb->rand() % 7;
327 #endif
331 /* called when a rocket is launched.
332 * prepares said rocket to start moving towards its destination. */
333 void init_rocket(int rocket)
335 rb->srand(*rb->current_tick);
337 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
338 rocket_ypos[rocket] = LCD_HEIGHT;
340 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
341 rocket_yspeed[rocket] = 3;
343 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
346 /* startup/configuration menu. */
347 void fireworks_menu(void)
349 int m, result;
350 bool menu_quit = false;
352 rb->lcd_setfont(FONT_UI);
353 #ifdef HAVE_LCD_COLOR
354 rb->lcd_set_background(LCD_BLACK);
355 rb->lcd_set_foreground(LCD_WHITE);
356 #endif
357 rb->lcd_clear_display();
358 rb->lcd_update();
360 m = menu_init(items, sizeof(items) / sizeof(*items),
361 NULL, NULL, NULL, NULL);
363 rb->button_clear_queue();
365 while(!menu_quit)
367 result = menu_show(m);
369 switch(result)
371 case 0:
372 rb->lcd_setfont(FONT_SYSFIXED);
374 #ifdef HAVE_LCD_COLOR
375 rb->lcd_set_background(LCD_BLACK);
376 rb->lcd_set_foreground(LCD_WHITE);
377 #endif
379 rb->lcd_clear_display();
380 rb->lcd_update();
382 init_all();
383 menu_quit = true;
384 break;
386 case 1:
387 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 15, NULL);
388 break;
390 case 2:
391 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
392 break;
394 case 3:
395 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
396 break;
398 case 4:
399 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
400 break;
402 case 5:
403 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
404 break;
406 case 6:
407 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
408 break;
410 case 7:
411 playback_control(NULL);
412 break;
414 case 8:
415 quit_plugin = true;
416 menu_quit = true;
417 break;
421 menu_exit(m);
424 /* this is the plugin entry point */
425 enum plugin_status plugin_start(const void* parameter)
427 (void)parameter;
429 int j, i, autofire=0;
430 int thisrocket=0;
431 int start_tick, elapsed_tick;
432 int button;
434 /* set everything up.. no BL timeout, no backdrop,
435 white-text-on-black-background. */
436 backlight_force_on(); /* backlight control in lib/helper.c */
437 #if LCD_DEPTH > 1
438 rb->lcd_set_backdrop(NULL);
439 rb->lcd_set_background(LCD_BLACK);
440 rb->lcd_set_foreground(LCD_WHITE);
441 #endif
443 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
444 rb->cpu_boost(true);
445 #endif
447 fireworks_menu();
449 start_tick = *rb->current_tick;
451 while(!quit_plugin)
453 rb->lcd_clear_display();
455 /* loop through every possible rocket */
456 for(j=0; j<MAX_ROCKETS; j++)
458 /* if the current rocket is actually moving/"alive" then go on and
459 * move/update/explode it */
460 if(rocket_phase[j] > -1)
462 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
463 if(show_rockets==2)
465 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
466 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
467 ROCKET_SIZE, ROCKET_SIZE);
468 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
469 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
470 ROCKET_SIZE, ROCKET_SIZE);
472 #endif
474 /* move rocket */
475 rocket_xpos[j] += rocket_xspeed[j];
476 rocket_ypos[j] -= rocket_yspeed[j];
478 #ifdef HAVE_LCD_COLOR
479 rb->lcd_set_foreground(LCD_WHITE);
480 #endif
481 if(show_rockets==2 || show_rockets==1)
482 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
483 ROCKET_SIZE, ROCKET_SIZE);
485 /* if(rocket isn't "there" yet) keep moving
486 * if(rocket IS there) explode it. */
487 if(rocket_phase[j] < rocket_targetphase[j])
488 rocket_phase[j]++;
489 else
491 rocket_phase[j] = -1;
493 firework_phase[j] = 0;
494 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
498 /* and now onto the fireworks for this particular rocket... */
499 if(firework_phase[j] > -1)
501 for(i=0; i<particle_values[particles_per_firework]; i++)
503 firework_xpoints[j][i] += firework_xspeed[j][i];
504 firework_ypoints[j][i] += firework_yspeed[j][i];
506 if(gravity != 0)
507 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
509 #ifdef HAVE_LCD_COLOR
510 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
511 rb->lcd_fillrect(firework_xpoints[j][i]-1,
512 firework_ypoints[j][i]-1,
513 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
515 if(firework_phase[j] < particle_life_values[particle_life]-10)
516 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
517 else if(firework_phase[j] < particle_life_values[particle_life]-7)
518 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
519 else if(firework_phase[j] < particle_life_values[particle_life]-3)
520 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
521 else
522 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
523 #endif
524 rb->lcd_fillrect(firework_xpoints[j][i],
525 firework_ypoints[j][i],
526 FIREWORK_SIZE, FIREWORK_SIZE);
527 /* WIP - currently ugly explosion effect
528 #ifdef HAVE_LCD_COLOR
529 if(firework_phase[j] < 10)
531 rb->lcd_set_foreground(EXPLOSION_COLOR);
532 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
533 rocket_ypos[j]-firework_phase[j],
534 firework_phase[j]*2, firework_phase[j]*2);
536 #endif */
539 #ifdef HAVE_LCD_COLOR
540 rb->lcd_set_foreground(LCD_WHITE);
541 #endif
543 /* firework at its destination age?
544 * no = keep aging; yes = delete it. */
545 if(firework_phase[j] < particle_life_values[particle_life])
546 firework_phase[j]++;
547 else
548 firework_phase[j] = -1;
552 /* is autofire on? */
553 if(autofire_delay != 0)
555 elapsed_tick = *rb->current_tick - start_tick;
557 if(elapsed_tick > autofire_delay_values[autofire_delay])
559 rocket_phase[autofire] = 0;
560 init_rocket(autofire);
562 start_tick = *rb->current_tick;
564 if(autofire < MAX_ROCKETS)
565 autofire++;
566 else
567 autofire = 0;
571 rb->lcd_update();
573 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
574 switch(button)
576 case BTN_MENU: /* back to config menu */
577 fireworks_menu();
578 break;
580 case BTN_FIRE: /* fire off rockets manually */
581 case BTN_FIRE|BUTTON_REPEAT:
582 if(thisrocket < MAX_ROCKETS)
583 thisrocket++;
584 else
585 thisrocket=0;
587 rocket_phase[thisrocket] = 0;
588 init_rocket(thisrocket);
589 break;
592 /* Turn on backlight timeout (revert to settings) */
593 backlight_use_settings(); /* backlight control in lib/helper.c */
595 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
596 rb->cpu_boost(false);
597 #endif
599 return PLUGIN_OK;