Prepare new maemo release
[maemo-rb.git] / apps / plugins / fireworks.c
blob54efaba6cae0a4d30ea0b5fa33ccdb77ee54fe12
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"
24 #include "lib/pluginlib_actions.h"
26 /* this set the context to use with PLA */
27 static const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
29 /***
30 * FIREWORKS.C by ZAKK ROBERTS
31 * Rockbox plugin simulating a fireworks display.
32 * Supports all bitmap LCDs, fully scalable.
33 * Currently disabled for Archos Recorder - runs too slow.
34 ***/
36 /* We use PLA */
37 #define BTN_EXIT PLA_EXIT
38 #define BTN_MENU PLA_CANCEL
39 #define BTN_FIRE PLA_SELECT
40 #define BTN_FIRE_REPEAT PLA_SELECT_REPEAT
42 /* The lowdown on source terminology:
43 * a ROCKET is launched from the LCD bottom.
44 * FIREWORKs are ejected from the rocket when it explodes. */
46 #define MAX_ROCKETS 40
47 #define ROCKET_LIFE (LCD_HEIGHT/2)
48 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
49 #define ROCKET_SIZE 2
50 #define ROCKET_MOVEMENT_RANGE 4
51 #define ROCKET_TRAIL_PARTICLES 50
53 #define MAX_FIREWORKS 40
54 #define FIREWORK_MOVEMENT_RANGE 6
55 #define FIREWORK_SIZE 2
57 /* position, speed, "phase" (age), color of all fireworks */
58 int firework_xpoints[MAX_ROCKETS][MAX_FIREWORKS];
59 int firework_ypoints[MAX_ROCKETS][MAX_FIREWORKS];
60 int firework_xspeed[MAX_ROCKETS][MAX_FIREWORKS];
61 int firework_yspeed[MAX_ROCKETS][MAX_FIREWORKS];
62 int firework_phase[MAX_ROCKETS];
63 #ifdef HAVE_LCD_COLOR
64 int firework_color[MAX_ROCKETS][MAX_FIREWORKS];
65 #endif
67 /* position, speed, "phase" (age) of all rockets */
68 int rocket_xpos[MAX_ROCKETS];
69 int rocket_ypos[MAX_ROCKETS];
70 int rocket_xspeed[MAX_ROCKETS];
71 int rocket_yspeed[MAX_ROCKETS];
72 int rocket_phase[MAX_ROCKETS];
73 int rocket_targetphase[MAX_ROCKETS];
75 /* settings values. these should eventually be saved to
76 * disk. maybe a preset loading/saving system? */
77 int autofire_delay = 0;
78 int particles_per_firework = 2;
79 int particle_life = 1;
80 int gravity = 1;
81 int show_rockets = 1;
82 int frames_per_second = 4;
83 bool quit_plugin = false;
85 /* firework colors:
86 * firework_colors = brightest firework color, used most of the time.
87 * DARK colors = fireworks are nearly burnt out.
88 * DARKER colors = fireworks are several frames away from burning out.
89 * DARKEST colors = fireworks are a couple frames from burning out. */
90 #ifdef HAVE_LCD_COLOR
91 static const unsigned firework_colors[] = {
92 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
93 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
94 LCD_RGBPACK(151,84,213) };
96 static const unsigned firework_dark_colors[] = {
97 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
98 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
99 LCD_RGBPACK(75,42,105) };
101 static const unsigned firework_darker_colors[] = {
102 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
103 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
104 LCD_RGBPACK(38,21,52) };
106 static const unsigned firework_darkest_colors[] = {
107 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
108 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
109 LCD_RGBPACK(19,10,26) };
111 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
113 #endif
115 static const struct opt_items autofire_delay_settings[15] = {
116 { "Off", -1 },
117 { "50ms", -1 },
118 { "100ms", -1 },
119 { "200ms", -1 },
120 { "300ms", -1 },
121 { "400ms", -1 },
122 { "500ms", -1 },
123 { "600ms", -1 },
124 { "700ms", -1 },
125 { "800ms", -1 },
126 { "900ms", -1 },
127 { "1s", -1 },
128 { "2s", -1 },
129 { "3s", -1 },
130 { "4s", -1 }
133 int autofire_delay_values[15] = {
134 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
136 static const struct opt_items particle_settings[8] = {
137 { "5", -1 },
138 { "10", -1 },
139 { "15", -1 },
140 { "20", -1 },
141 { "25", -1 },
142 { "30", -1 },
143 { "35", -1 },
144 { "40", -1 },
147 int particle_values[8] = {
148 5, 10, 15, 20, 25, 30, 35, 40 };
150 static const struct opt_items particle_life_settings[9] = {
151 { "20 cycles", -1 },
152 { "30 cycles", -1 },
153 { "40 cycles", -1 },
154 { "50 cycles", -1 },
155 { "60 cycles", -1 },
156 { "70 cycles", -1 },
157 { "80 cycles", -1 },
158 { "90 cycles", -1 },
159 { "100 cycles", -1 }
162 int particle_life_values[9] = {
163 20, 30, 40, 50, 60, 70, 80, 90, 100 };
165 static const struct opt_items gravity_settings[4] = {
166 { "Off", -1 },
167 { "Weak", -1 },
168 { "Moderate", -1 },
169 { "Strong", -1 },
172 int gravity_values[4] = {
173 0, 30, 20, 10 };
175 #ifdef HAVE_LCD_COLOR
177 static const struct opt_items rocket_settings[3] = {
178 { "No", -1 },
179 { "Yes (no trails)", -1 },
180 { "Yes (with trails)", -1 },
182 int rocket_values[4] = {
183 2, 1, 0 };
185 #else
187 static const struct opt_items rocket_settings[2] = {
188 { "No", -1 },
189 { "Yes", -1 },
191 int rocket_values[4] = {
192 1, 0 };
194 #endif
196 static const struct opt_items fps_settings[9] = {
197 { "20 FPS", -1 },
198 { "25 FPS", -1 },
199 { "30 FPS", -1 },
200 { "35 FPS", -1 },
201 { "40 FPS", -1 },
202 { "45 FPS", -1 },
203 { "50 FPS", -1 },
204 { "55 FPS", -1 },
205 { "60 FPS", -1 }
208 int fps_values[9] = {
209 20, 25, 30, 35, 40, 45, 50, 55, 60 };
211 MENUITEM_STRINGLIST(menu, "Fireworks Menu", NULL,
212 "Start Demo", "Auto-Fire", "Particles Per Firework",
213 "Particle Life", "Gravity", "Show Rockets",
214 "FPS (Speed)", "Playback Control", "Quit");
216 /* called on startup. initializes all variables, etc */
217 static void init_all(void)
219 int j;
221 for(j=0; j<MAX_ROCKETS; j++)
223 rocket_phase[j] = -1;
224 firework_phase[j] = -1;
228 /* called when a rocket hits its destination height.
229 * prepares all associated fireworks to be expelled. */
230 static void init_explode(int x, int y, int firework, int points)
232 int i;
234 for(i=0; i<points; i++)
236 rb->srand(*rb->current_tick * i);
238 firework_xpoints[firework][i] = x;
239 firework_ypoints[firework][i] = y;
241 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
242 - FIREWORK_MOVEMENT_RANGE/2;
243 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE)
244 - FIREWORK_MOVEMENT_RANGE/2;
246 #ifdef HAVE_LCD_COLOR
247 firework_color[firework][i] = rb->rand() % 7;
248 #endif
252 /* called when a rocket is launched.
253 * prepares said rocket to start moving towards its destination. */
254 static void init_rocket(int rocket)
256 rb->srand(*rb->current_tick);
258 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
259 rocket_ypos[rocket] = LCD_HEIGHT;
261 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE)
262 - ROCKET_MOVEMENT_RANGE/2;
263 rocket_yspeed[rocket] = 3;
265 rocket_phase[rocket] = 0;
266 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR))
267 / rocket_yspeed[rocket];
270 /* startup/configuration menu. */
271 static void fireworks_menu(void)
273 int selected = 0, result;
274 bool menu_quit = false;
276 rb->lcd_setfont(FONT_UI);
277 #ifdef HAVE_LCD_COLOR
278 rb->lcd_set_background(LCD_BLACK);
279 rb->lcd_set_foreground(LCD_WHITE);
280 #endif
281 rb->lcd_clear_display();
282 rb->lcd_update();
284 rb->button_clear_queue();
286 while(!menu_quit)
288 result = rb->do_menu(&menu, &selected, NULL, false);
290 switch(result)
292 case 0:
293 rb->lcd_setfont(FONT_SYSFIXED);
295 #ifdef HAVE_LCD_COLOR
296 rb->lcd_set_background(LCD_BLACK);
297 rb->lcd_set_foreground(LCD_WHITE);
298 #endif
300 rb->lcd_clear_display();
301 rb->lcd_update();
303 init_all();
304 menu_quit = true;
305 break;
307 case 1:
308 rb->set_option("Auto-Fire", &autofire_delay, INT,
309 autofire_delay_settings, 15, NULL);
310 break;
312 case 2:
313 rb->set_option("Particles Per Firework", &particles_per_firework,
314 INT, particle_settings, 8, NULL);
315 break;
317 case 3:
318 rb->set_option("Particle Life", &particle_life, INT,
319 particle_life_settings, 9, NULL);
320 break;
322 case 4:
323 rb->set_option("Gravity", &gravity, INT,
324 gravity_settings, 4, NULL);
325 break;
327 case 5:
328 rb->set_option("Show Rockets", &show_rockets, INT,
329 rocket_settings, 3, NULL);
330 break;
332 case 6:
333 rb->set_option("FPS (Speed)", &frames_per_second, INT,
334 fps_settings, 9, NULL);
335 break;
337 case 7:
338 playback_control(NULL);
339 break;
341 case 8:
342 quit_plugin = true;
343 menu_quit = true;
344 break;
349 /* this is the plugin entry point */
350 enum plugin_status plugin_start(const void* parameter)
352 (void)parameter;
354 int j, i;
355 int thisrocket=0;
356 int start_tick, elapsed_tick;
357 int button;
359 /* set everything up.. no BL timeout, no backdrop,
360 white-text-on-black-background. */
361 backlight_ignore_timeout();
362 #if LCD_DEPTH > 1
363 rb->lcd_set_backdrop(NULL);
364 rb->lcd_set_background(LCD_BLACK);
365 rb->lcd_set_foreground(LCD_WHITE);
366 #endif
368 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
369 rb->cpu_boost(true);
370 #endif
372 fireworks_menu();
374 start_tick = *rb->current_tick;
376 while(!quit_plugin)
378 rb->lcd_clear_display();
380 /* loop through every possible rocket */
381 for(j=0; j<MAX_ROCKETS; j++)
383 /* if the current rocket is actually moving/"alive" then go on and
384 * move/update/explode it */
385 if(rocket_phase[j] > -1)
387 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
388 if(show_rockets==2)
390 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
391 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
392 ROCKET_SIZE, ROCKET_SIZE);
393 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
394 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j],
395 rocket_ypos[j]+rocket_yspeed[j],
396 ROCKET_SIZE, ROCKET_SIZE);
398 #endif
400 /* move rocket */
401 rocket_xpos[j] += rocket_xspeed[j];
402 rocket_ypos[j] -= rocket_yspeed[j];
404 #ifdef HAVE_LCD_COLOR
405 rb->lcd_set_foreground(LCD_WHITE);
406 #endif
407 if(show_rockets==2 || show_rockets==1)
408 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
409 ROCKET_SIZE, ROCKET_SIZE);
411 /* if(rocket isn't "there" yet) keep moving
412 * if(rocket IS there) explode it. */
413 if(rocket_phase[j] < rocket_targetphase[j])
414 rocket_phase[j]++;
415 else
417 rocket_phase[j] = -1;
419 firework_phase[j] = 0;
420 init_explode(rocket_xpos[j], rocket_ypos[j], j,
421 particle_values[particles_per_firework]);
425 /* and now onto the fireworks for this particular rocket... */
426 if(firework_phase[j] > -1)
428 for(i=0; i<particle_values[particles_per_firework]; i++)
430 firework_xpoints[j][i] += firework_xspeed[j][i];
431 firework_ypoints[j][i] += firework_yspeed[j][i];
433 if(gravity != 0)
434 firework_ypoints[j][i] += firework_phase[j]
435 /gravity_values[gravity];
437 #ifdef HAVE_LCD_COLOR
438 rb->lcd_set_foreground(
439 firework_darkest_colors[firework_color[j][i]]);
440 rb->lcd_fillrect(firework_xpoints[j][i]-1,
441 firework_ypoints[j][i]-1,
442 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
444 int phase_left = particle_life_values[particle_life]
445 - firework_phase[j];
446 if(phase_left > 10)
447 rb->lcd_set_foreground(
448 firework_colors[firework_color[j][i]]);
449 else if(phase_left > 7)
450 rb->lcd_set_foreground(
451 firework_dark_colors[firework_color[j][i]]);
452 else if(phase_left > 3)
453 rb->lcd_set_foreground(
454 firework_darker_colors[firework_color[j][i]]);
455 else
456 rb->lcd_set_foreground(
457 firework_darkest_colors[firework_color[j][i]]);
458 #endif
459 rb->lcd_fillrect(firework_xpoints[j][i],
460 firework_ypoints[j][i],
461 FIREWORK_SIZE, FIREWORK_SIZE);
462 /* WIP - currently ugly explosion effect
463 #ifdef HAVE_LCD_COLOR
464 if(firework_phase[j] < 10)
466 rb->lcd_set_foreground(EXPLOSION_COLOR);
467 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
468 rocket_ypos[j]-firework_phase[j],
469 firework_phase[j]*2, firework_phase[j]*2);
471 #endif */
474 #ifdef HAVE_LCD_COLOR
475 rb->lcd_set_foreground(LCD_WHITE);
476 #endif
478 /* firework at its destination age?
479 * no = keep aging; yes = delete it. */
480 if(firework_phase[j] < particle_life_values[particle_life])
481 firework_phase[j]++;
482 else
483 firework_phase[j] = -1;
487 /* is autofire on? */
488 if(autofire_delay != 0)
490 elapsed_tick = *rb->current_tick - start_tick;
492 if(elapsed_tick > autofire_delay_values[autofire_delay])
494 init_rocket(thisrocket);
495 if(++thisrocket == MAX_ROCKETS)
496 thisrocket = 0;
498 start_tick = *rb->current_tick;
502 rb->lcd_update();
504 button = pluginlib_getaction(HZ/fps_values[frames_per_second], plugin_contexts,
505 ARRAYLEN(plugin_contexts));
507 switch(button)
509 case BTN_EXIT: /* exit directly */
510 quit_plugin = true;
511 break;
513 case BTN_MENU: /* back to config menu */
514 fireworks_menu();
515 break;
517 case BTN_FIRE: /* fire off rockets manually */
518 case BTN_FIRE_REPEAT:
519 init_rocket(thisrocket);
520 if(++thisrocket == MAX_ROCKETS)
521 thisrocket=0;
522 break;
525 /* Turn on backlight timeout (revert to settings) */
526 backlight_use_settings();
528 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
529 rb->cpu_boost(false);
530 #endif
532 return PLUGIN_OK;