Introduce Rockbox Utility to the manual as automated installation option. Only rather...
[Rockbox.git] / apps / plugins / fireworks.c
blobbedf82ffda79fc6b09fdf5754a69135b1871c83c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:
10 * Copyright (C) 2007 Zakk Roberts
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"
20 #include "oldmenuapi.h"
21 #include "helper.h"
23 PLUGIN_HEADER
25 static struct plugin_api* rb;
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
38 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
39 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
40 #define BTN_MENU BUTTON_MENU
41 #define BTN_FIRE BUTTON_SELECT
42 #elif (CONFIG_KEYPAD == RECORDER_PAD)
43 #define BTN_MENU BUTTON_OFF
44 #define BTN_FIRE BUTTON_PLAY
45 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
46 #define BTN_MENU BUTTON_OFF
47 #define BTN_FIRE BUTTON_SELECT
48 #elif (CONFIG_KEYPAD == ONDIO_PAD)
49 #define BTN_MENU BUTTON_MENU
50 #define BTN_FIRE BUTTON_UP
51 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
52 #define BTN_MENU BUTTON_POWER
53 #define BTN_FIRE BUTTON_SELECT
54 #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
55 #define BTN_MENU BUTTON_MODE
56 #define BTN_FIRE BUTTON_SELECT
57 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
58 #define BTN_MENU BUTTON_MENU
59 #define BTN_FIRE BUTTON_SELECT
60 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
61 (CONFIG_KEYPAD == SANSA_C200_PAD)
62 #define BTN_MENU BUTTON_POWER
63 #define BTN_FIRE BUTTON_SELECT
64 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
65 #define BTN_MENU BUTTON_POWER
66 #define BTN_FIRE BUTTON_PLAY
67 #endif
69 /* The lowdown on source terminology:
70 * a ROCKET is launched from the LCD bottom.
71 * FIREWORKs are ejected from the rocket when it explodes. */
73 #define MAX_ROCKETS 40
74 #define ROCKET_LIFE (LCD_HEIGHT/2)
75 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
76 #define ROCKET_SIZE 2
77 #define ROCKET_MOVEMENT_RANGE 4
78 #define ROCKET_TRAIL_PARTICLES 50
80 #define MAX_FIREWORKS 40
81 #define FIREWORK_MOVEMENT_RANGE 6
82 #define FIREWORK_SIZE 2
84 /* position, speed, "phase" (age), color of all fireworks */
85 int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
86 int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
87 int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
88 int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
89 int firework_phase[MAX_ROCKETS+1];
90 #ifdef HAVE_LCD_COLOR
91 int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
92 #endif
94 /* position, speed, "phase" (age) of all rockets */
95 int rocket_xpos[MAX_ROCKETS+1];
96 int rocket_ypos[MAX_ROCKETS+1];
97 int rocket_xspeed[MAX_ROCKETS+1];
98 int rocket_yspeed[MAX_ROCKETS+1];
99 int rocket_phase[MAX_ROCKETS+1];
100 int rocket_targetphase[MAX_ROCKETS+1];
102 /* settings values. these should eventually be saved to
103 * disk. maybe a preset loading/saving system? */
104 int autofire_delay = 0;
105 int particles_per_firework = 2;
106 int particle_life = 1;
107 int gravity = 1;
108 int show_rockets = 1;
109 int frames_per_second = 4;
110 bool quit_plugin = false;
112 /* firework colors:
113 * firework_colors = brightest firework color, used most of the time.
114 * DARK colors = fireworks are nearly burnt out.
115 * DARKER colors = fireworks are several frames away from burning out.
116 * DARKEST colors = fireworks are a couple frames from burning out. */
117 #ifdef HAVE_LCD_COLOR
118 static const unsigned firework_colors[] = {
119 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
120 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
121 LCD_RGBPACK(151,84,213) };
123 static const unsigned firework_dark_colors[] = {
124 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
125 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
126 LCD_RGBPACK(75,42,105) };
128 static const unsigned firework_darker_colors[] = {
129 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
130 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
131 LCD_RGBPACK(38,21,52) };
133 static const unsigned firework_darkest_colors[] = {
134 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
135 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
136 LCD_RGBPACK(19,10,26) };
138 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
140 #endif
142 static const struct opt_items autofire_delay_settings[16] = {
143 { "Off", NULL },
144 { "50ms", NULL },
145 { "100ms", NULL },
146 { "200ms", NULL },
147 { "300ms", NULL },
148 { "300ms", NULL },
149 { "400ms", NULL },
150 { "500ms", NULL },
151 { "600ms", NULL },
152 { "700ms", NULL },
153 { "800ms", NULL },
154 { "900ms", NULL },
155 { "1s", NULL },
156 { "2s", NULL },
157 { "3s", NULL },
158 { "4s", NULL }
161 int autofire_delay_values[16] = {
162 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
164 static const struct opt_items particle_settings[8] = {
165 { "5", NULL },
166 { "10", NULL },
167 { "15", NULL },
168 { "20", NULL },
169 { "25", NULL },
170 { "30", NULL },
171 { "35", NULL },
172 { "40", NULL },
175 int particle_values[8] = {
176 5, 10, 15, 20, 25, 30, 35, 40 };
178 static const struct opt_items particle_life_settings[9] = {
179 { "20 cycles", NULL },
180 { "30 cycles", NULL },
181 { "40 cycles", NULL },
182 { "50 cycles", NULL },
183 { "60 cycles", NULL },
184 { "70 cycles", NULL },
185 { "80 cycles", NULL },
186 { "90 cycles", NULL },
187 { "100 cycles", NULL }
190 int particle_life_values[9] = {
191 20, 30, 40, 50, 60, 70, 80, 90, 100 };
193 static const struct opt_items gravity_settings[4] = {
194 { "Off", NULL },
195 { "Weak", NULL },
196 { "Moderate", NULL },
197 { "Strong", NULL },
200 int gravity_values[4] = {
201 0, 30, 20, 10 };
203 #ifdef HAVE_LCD_COLOR
205 static const struct opt_items rocket_settings[3] = {
206 { "No", NULL },
207 { "Yes (no trails)", NULL },
208 { "Yes (with trails)", NULL },
210 int rocket_values[4] = {
211 2, 1, 0 };
213 #else
215 static const struct opt_items rocket_settings[2] = {
216 { "No", NULL },
217 { "Yes", NULL },
219 int rocket_values[4] = {
220 1, 0 };
222 #endif
224 static const struct opt_items fps_settings[9] = {
225 { "20 FPS", NULL },
226 { "25 FPS", NULL },
227 { "30 FPS", NULL },
228 { "35 FPS", NULL },
229 { "40 FPS", NULL },
230 { "45 FPS", NULL },
231 { "50 FPS", NULL },
232 { "55 FPS", NULL },
233 { "60 FPS", NULL }
236 int fps_values[9] = {
237 20, 25, 30, 35, 40, 45, 50, 55, 60 };
239 static const struct menu_item items[] = {
240 { "Start Demo", NULL },
241 { "Auto-Fire", NULL },
242 { "Particles Per Firework", NULL },
243 { "Particle Life", NULL },
244 { "Gravity", NULL },
245 { "Show Rockets", NULL },
246 { "FPS (Speed)", NULL },
247 { "Quit", NULL }
250 /* called on startup. initializes all variables, etc */
251 void init_all(void)
253 int j;
255 for(j=0; j<MAX_ROCKETS; j++)
256 firework_phase[j] = -1;
259 /* called when a rocket hits its destination height.
260 * prepares all associated fireworks to be expelled. */
261 void init_explode(int x, int y, int firework, int points)
263 int i;
265 for(i=0; i<points; i++)
267 rb->srand(*rb->current_tick * i);
269 firework_xpoints[firework][i] = x;
270 firework_ypoints[firework][i] = y;
272 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
273 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
275 #ifdef HAVE_LCD_COLOR
276 firework_color[firework][i] = rb->rand() % 7;
277 #endif
281 /* called when a rocket is launched.
282 * prepares said rocket to start moving towards its destination. */
283 void init_rocket(int rocket)
285 rb->srand(*rb->current_tick);
287 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
288 rocket_ypos[rocket] = LCD_HEIGHT;
290 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
291 rocket_yspeed[rocket] = 3;
293 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
296 /* startup/configuration menu. */
297 void fireworks_menu(void)
299 int m, result;
300 bool menu_quit = false;
302 rb->lcd_setfont(FONT_UI);
303 #ifdef HAVE_LCD_COLOR
304 rb->lcd_set_background(LCD_BLACK);
305 rb->lcd_set_foreground(LCD_WHITE);
306 #endif
307 rb->lcd_clear_display();
308 rb->lcd_update();
310 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
311 NULL, NULL, NULL, NULL);
313 rb->button_clear_queue();
315 while(!menu_quit)
317 result = menu_show(m);
319 switch(result)
321 case 0:
322 rb->lcd_setfont(FONT_SYSFIXED);
324 #ifdef HAVE_LCD_COLOR
325 rb->lcd_set_background(LCD_BLACK);
326 rb->lcd_set_foreground(LCD_WHITE);
327 #endif
329 rb->lcd_clear_display();
330 rb->lcd_update();
332 init_all();
333 menu_quit = true;
334 break;
336 case 1:
337 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 16, NULL);
338 break;
340 case 2:
341 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
342 break;
344 case 3:
345 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
346 break;
348 case 4:
349 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
350 break;
352 case 5:
353 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
354 break;
356 case 6:
357 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
358 break;
360 case 7:
361 quit_plugin = true;
362 menu_quit = true;
363 break;
367 menu_exit(m);
370 /* this is the plugin entry point */
371 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
373 (void)parameter;
375 rb = api;
377 int j, i, autofire=0;
378 int thisrocket=0;
379 int start_tick, elapsed_tick;
380 int button;
382 /* set everything up.. no BL timeout, no backdrop,
383 white-text-on-black-background. */
384 backlight_force_on(rb); /* backlight control in lib/helper.c */
385 #if LCD_DEPTH > 1
386 rb->lcd_set_backdrop(NULL);
387 rb->lcd_set_background(LCD_BLACK);
388 rb->lcd_set_foreground(LCD_WHITE);
389 #endif
391 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
392 rb->cpu_boost(true);
393 #endif
395 fireworks_menu();
397 start_tick = *rb->current_tick;
399 while(!quit_plugin)
401 rb->lcd_clear_display();
403 /* loop through every possible rocket */
404 for(j=0; j<MAX_ROCKETS; j++)
406 /* if the current rocket is actually moving/"alive" then go on and
407 * move/update/explode it */
408 if(rocket_phase[j] > -1)
410 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
411 if(show_rockets==2)
413 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
414 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
415 ROCKET_SIZE, ROCKET_SIZE);
416 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
417 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
418 ROCKET_SIZE, ROCKET_SIZE);
420 #endif
422 /* move rocket */
423 rocket_xpos[j] += rocket_xspeed[j];
424 rocket_ypos[j] -= rocket_yspeed[j];
426 #ifdef HAVE_LCD_COLOR
427 rb->lcd_set_foreground(LCD_WHITE);
428 #endif
429 if(show_rockets==2 || show_rockets==1)
430 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
431 ROCKET_SIZE, ROCKET_SIZE);
433 /* if(rocket isn't "there" yet) keep moving
434 * if(rocket IS there) explode it. */
435 if(rocket_phase[j] < rocket_targetphase[j])
436 rocket_phase[j]++;
437 else
439 rocket_phase[j] = -1;
441 firework_phase[j] = 0;
442 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
446 /* and now onto the fireworks for this particular rocket... */
447 if(firework_phase[j] > -1)
449 for(i=0; i<particle_values[particles_per_firework]; i++)
451 firework_xpoints[j][i] += firework_xspeed[j][i];
452 firework_ypoints[j][i] += firework_yspeed[j][i];
454 if(gravity != 0)
455 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
457 #ifdef HAVE_LCD_COLOR
458 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
459 rb->lcd_fillrect(firework_xpoints[j][i]-1,
460 firework_ypoints[j][i]-1,
461 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
463 if(firework_phase[j] < particle_life_values[particle_life]-10)
464 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
465 else if(firework_phase[j] < particle_life_values[particle_life]-7)
466 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
467 else if(firework_phase[j] < particle_life_values[particle_life]-3)
468 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
469 else
470 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
471 #endif
472 rb->lcd_fillrect(firework_xpoints[j][i],
473 firework_ypoints[j][i],
474 FIREWORK_SIZE, FIREWORK_SIZE);
475 /* WIP - currently ugly explosion effect
476 #ifdef HAVE_LCD_COLOR
477 if(firework_phase[j] < 10)
479 rb->lcd_set_foreground(EXPLOSION_COLOR);
480 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
481 rocket_ypos[j]-firework_phase[j],
482 firework_phase[j]*2, firework_phase[j]*2);
484 #endif */
487 #ifdef HAVE_LCD_COLOR
488 rb->lcd_set_foreground(LCD_WHITE);
489 #endif
491 /* firework at its destination age?
492 * no = keep aging; yes = delete it. */
493 if(firework_phase[j] < particle_life_values[particle_life])
494 firework_phase[j]++;
495 else
496 firework_phase[j] = -1;
500 /* is autofire on? */
501 if(autofire_delay != 0)
503 elapsed_tick = *rb->current_tick - start_tick;
505 if(elapsed_tick > autofire_delay_values[autofire_delay])
507 rocket_phase[autofire] = 0;
508 init_rocket(autofire);
510 start_tick = *rb->current_tick;
512 if(autofire < MAX_ROCKETS)
513 autofire++;
514 else
515 autofire = 0;
519 rb->lcd_update();
521 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
522 switch(button)
524 case BTN_MENU: /* back to config menu */
525 fireworks_menu();
526 break;
528 case BTN_FIRE: /* fire off rockets manually */
529 case BTN_FIRE|BUTTON_REPEAT:
530 if(thisrocket < MAX_ROCKETS)
531 thisrocket++;
532 else
533 thisrocket=0;
535 rocket_phase[thisrocket] = 0;
536 init_rocket(thisrocket);
537 break;
540 /* Turn on backlight timeout (revert to settings) */
541 backlight_use_settings(rb); /* backlight control in lib/helper.c */
543 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
544 rb->cpu_boost(true);
545 #endif
547 return PLUGIN_OK;