Rename variables sectorbuf and verbose to avoid clashes in rbutil. Cleanup exports...
[Rockbox.git] / apps / plugins / fireworks.c
blobd06cbdf9123834eeacdcfda2f4c4ce22d42c76e1
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 const 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 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
59 (CONFIG_KEYPAD == MROBE100_PAD)
60 #define BTN_MENU BUTTON_MENU
61 #define BTN_FIRE BUTTON_SELECT
62 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
63 (CONFIG_KEYPAD == SANSA_C200_PAD)
64 #define BTN_MENU BUTTON_POWER
65 #define BTN_FIRE BUTTON_SELECT
66 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
67 #define BTN_MENU BUTTON_POWER
68 #define BTN_FIRE BUTTON_PLAY
69 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
70 #define BTN_MENU BUTTON_RC_REC
71 #define BTN_FIRE BUTTON_RC_PLAY
72 #elif (CONFIG_KEYPAD == COWOND2_PAD)
73 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
74 #else
75 #error No keymap defined!
76 #endif
78 #ifdef HAVE_TOUCHPAD
79 #ifndef BTN_MENU
80 #define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
81 #endif
82 #ifndef BTN_FIRE
83 #define BTN_FIRE BUTTON_CENTER
84 #endif
85 #endif
87 /* The lowdown on source terminology:
88 * a ROCKET is launched from the LCD bottom.
89 * FIREWORKs are ejected from the rocket when it explodes. */
91 #define MAX_ROCKETS 40
92 #define ROCKET_LIFE (LCD_HEIGHT/2)
93 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
94 #define ROCKET_SIZE 2
95 #define ROCKET_MOVEMENT_RANGE 4
96 #define ROCKET_TRAIL_PARTICLES 50
98 #define MAX_FIREWORKS 40
99 #define FIREWORK_MOVEMENT_RANGE 6
100 #define FIREWORK_SIZE 2
102 /* position, speed, "phase" (age), color of all fireworks */
103 int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
104 int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
105 int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
106 int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
107 int firework_phase[MAX_ROCKETS+1];
108 #ifdef HAVE_LCD_COLOR
109 int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
110 #endif
112 /* position, speed, "phase" (age) of all rockets */
113 int rocket_xpos[MAX_ROCKETS+1];
114 int rocket_ypos[MAX_ROCKETS+1];
115 int rocket_xspeed[MAX_ROCKETS+1];
116 int rocket_yspeed[MAX_ROCKETS+1];
117 int rocket_phase[MAX_ROCKETS+1];
118 int rocket_targetphase[MAX_ROCKETS+1];
120 /* settings values. these should eventually be saved to
121 * disk. maybe a preset loading/saving system? */
122 int autofire_delay = 0;
123 int particles_per_firework = 2;
124 int particle_life = 1;
125 int gravity = 1;
126 int show_rockets = 1;
127 int frames_per_second = 4;
128 bool quit_plugin = false;
130 /* firework colors:
131 * firework_colors = brightest firework color, used most of the time.
132 * DARK colors = fireworks are nearly burnt out.
133 * DARKER colors = fireworks are several frames away from burning out.
134 * DARKEST colors = fireworks are a couple frames from burning out. */
135 #ifdef HAVE_LCD_COLOR
136 static const unsigned firework_colors[] = {
137 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
138 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
139 LCD_RGBPACK(151,84,213) };
141 static const unsigned firework_dark_colors[] = {
142 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
143 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
144 LCD_RGBPACK(75,42,105) };
146 static const unsigned firework_darker_colors[] = {
147 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
148 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
149 LCD_RGBPACK(38,21,52) };
151 static const unsigned firework_darkest_colors[] = {
152 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
153 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
154 LCD_RGBPACK(19,10,26) };
156 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
158 #endif
160 static const struct opt_items autofire_delay_settings[15] = {
161 { "Off", -1 },
162 { "50ms", -1 },
163 { "100ms", -1 },
164 { "200ms", -1 },
165 { "300ms", -1 },
166 { "400ms", -1 },
167 { "500ms", -1 },
168 { "600ms", -1 },
169 { "700ms", -1 },
170 { "800ms", -1 },
171 { "900ms", -1 },
172 { "1s", -1 },
173 { "2s", -1 },
174 { "3s", -1 },
175 { "4s", -1 }
178 int autofire_delay_values[15] = {
179 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
181 static const struct opt_items particle_settings[8] = {
182 { "5", -1 },
183 { "10", -1 },
184 { "15", -1 },
185 { "20", -1 },
186 { "25", -1 },
187 { "30", -1 },
188 { "35", -1 },
189 { "40", -1 },
192 int particle_values[8] = {
193 5, 10, 15, 20, 25, 30, 35, 40 };
195 static const struct opt_items particle_life_settings[9] = {
196 { "20 cycles", -1 },
197 { "30 cycles", -1 },
198 { "40 cycles", -1 },
199 { "50 cycles", -1 },
200 { "60 cycles", -1 },
201 { "70 cycles", -1 },
202 { "80 cycles", -1 },
203 { "90 cycles", -1 },
204 { "100 cycles", -1 }
207 int particle_life_values[9] = {
208 20, 30, 40, 50, 60, 70, 80, 90, 100 };
210 static const struct opt_items gravity_settings[4] = {
211 { "Off", -1 },
212 { "Weak", -1 },
213 { "Moderate", -1 },
214 { "Strong", -1 },
217 int gravity_values[4] = {
218 0, 30, 20, 10 };
220 #ifdef HAVE_LCD_COLOR
222 static const struct opt_items rocket_settings[3] = {
223 { "No", -1 },
224 { "Yes (no trails)", -1 },
225 { "Yes (with trails)", -1 },
227 int rocket_values[4] = {
228 2, 1, 0 };
230 #else
232 static const struct opt_items rocket_settings[2] = {
233 { "No", -1 },
234 { "Yes", -1 },
236 int rocket_values[4] = {
237 1, 0 };
239 #endif
241 static const struct opt_items fps_settings[9] = {
242 { "20 FPS", -1 },
243 { "25 FPS", -1 },
244 { "30 FPS", -1 },
245 { "35 FPS", -1 },
246 { "40 FPS", -1 },
247 { "45 FPS", -1 },
248 { "50 FPS", -1 },
249 { "55 FPS", -1 },
250 { "60 FPS", -1 }
253 int fps_values[9] = {
254 20, 25, 30, 35, 40, 45, 50, 55, 60 };
256 static const struct menu_item items[] = {
257 { "Start Demo", NULL },
258 { "Auto-Fire", NULL },
259 { "Particles Per Firework", NULL },
260 { "Particle Life", NULL },
261 { "Gravity", NULL },
262 { "Show Rockets", NULL },
263 { "FPS (Speed)", NULL },
264 { "Quit", NULL }
267 /* called on startup. initializes all variables, etc */
268 void init_all(void)
270 int j;
272 for(j=0; j<MAX_ROCKETS; j++)
273 firework_phase[j] = -1;
276 /* called when a rocket hits its destination height.
277 * prepares all associated fireworks to be expelled. */
278 void init_explode(int x, int y, int firework, int points)
280 int i;
282 for(i=0; i<points; i++)
284 rb->srand(*rb->current_tick * i);
286 firework_xpoints[firework][i] = x;
287 firework_ypoints[firework][i] = y;
289 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
290 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
292 #ifdef HAVE_LCD_COLOR
293 firework_color[firework][i] = rb->rand() % 7;
294 #endif
298 /* called when a rocket is launched.
299 * prepares said rocket to start moving towards its destination. */
300 void init_rocket(int rocket)
302 rb->srand(*rb->current_tick);
304 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
305 rocket_ypos[rocket] = LCD_HEIGHT;
307 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
308 rocket_yspeed[rocket] = 3;
310 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
313 /* startup/configuration menu. */
314 void fireworks_menu(void)
316 int m, result;
317 bool menu_quit = false;
319 rb->lcd_setfont(FONT_UI);
320 #ifdef HAVE_LCD_COLOR
321 rb->lcd_set_background(LCD_BLACK);
322 rb->lcd_set_foreground(LCD_WHITE);
323 #endif
324 rb->lcd_clear_display();
325 rb->lcd_update();
327 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
328 NULL, NULL, NULL, NULL);
330 rb->button_clear_queue();
332 while(!menu_quit)
334 result = menu_show(m);
336 switch(result)
338 case 0:
339 rb->lcd_setfont(FONT_SYSFIXED);
341 #ifdef HAVE_LCD_COLOR
342 rb->lcd_set_background(LCD_BLACK);
343 rb->lcd_set_foreground(LCD_WHITE);
344 #endif
346 rb->lcd_clear_display();
347 rb->lcd_update();
349 init_all();
350 menu_quit = true;
351 break;
353 case 1:
354 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 15, NULL);
355 break;
357 case 2:
358 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
359 break;
361 case 3:
362 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
363 break;
365 case 4:
366 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
367 break;
369 case 5:
370 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
371 break;
373 case 6:
374 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
375 break;
377 case 7:
378 quit_plugin = true;
379 menu_quit = true;
380 break;
384 menu_exit(m);
387 /* this is the plugin entry point */
388 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
390 (void)parameter;
392 rb = api;
394 int j, i, autofire=0;
395 int thisrocket=0;
396 int start_tick, elapsed_tick;
397 int button;
399 /* set everything up.. no BL timeout, no backdrop,
400 white-text-on-black-background. */
401 backlight_force_on(rb); /* backlight control in lib/helper.c */
402 #if LCD_DEPTH > 1
403 rb->lcd_set_backdrop(NULL);
404 rb->lcd_set_background(LCD_BLACK);
405 rb->lcd_set_foreground(LCD_WHITE);
406 #endif
408 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
409 rb->cpu_boost(true);
410 #endif
412 fireworks_menu();
414 start_tick = *rb->current_tick;
416 while(!quit_plugin)
418 rb->lcd_clear_display();
420 /* loop through every possible rocket */
421 for(j=0; j<MAX_ROCKETS; j++)
423 /* if the current rocket is actually moving/"alive" then go on and
424 * move/update/explode it */
425 if(rocket_phase[j] > -1)
427 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
428 if(show_rockets==2)
430 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
431 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
432 ROCKET_SIZE, ROCKET_SIZE);
433 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
434 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
435 ROCKET_SIZE, ROCKET_SIZE);
437 #endif
439 /* move rocket */
440 rocket_xpos[j] += rocket_xspeed[j];
441 rocket_ypos[j] -= rocket_yspeed[j];
443 #ifdef HAVE_LCD_COLOR
444 rb->lcd_set_foreground(LCD_WHITE);
445 #endif
446 if(show_rockets==2 || show_rockets==1)
447 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
448 ROCKET_SIZE, ROCKET_SIZE);
450 /* if(rocket isn't "there" yet) keep moving
451 * if(rocket IS there) explode it. */
452 if(rocket_phase[j] < rocket_targetphase[j])
453 rocket_phase[j]++;
454 else
456 rocket_phase[j] = -1;
458 firework_phase[j] = 0;
459 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
463 /* and now onto the fireworks for this particular rocket... */
464 if(firework_phase[j] > -1)
466 for(i=0; i<particle_values[particles_per_firework]; i++)
468 firework_xpoints[j][i] += firework_xspeed[j][i];
469 firework_ypoints[j][i] += firework_yspeed[j][i];
471 if(gravity != 0)
472 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
474 #ifdef HAVE_LCD_COLOR
475 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
476 rb->lcd_fillrect(firework_xpoints[j][i]-1,
477 firework_ypoints[j][i]-1,
478 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
480 if(firework_phase[j] < particle_life_values[particle_life]-10)
481 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
482 else if(firework_phase[j] < particle_life_values[particle_life]-7)
483 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
484 else if(firework_phase[j] < particle_life_values[particle_life]-3)
485 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
486 else
487 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
488 #endif
489 rb->lcd_fillrect(firework_xpoints[j][i],
490 firework_ypoints[j][i],
491 FIREWORK_SIZE, FIREWORK_SIZE);
492 /* WIP - currently ugly explosion effect
493 #ifdef HAVE_LCD_COLOR
494 if(firework_phase[j] < 10)
496 rb->lcd_set_foreground(EXPLOSION_COLOR);
497 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
498 rocket_ypos[j]-firework_phase[j],
499 firework_phase[j]*2, firework_phase[j]*2);
501 #endif */
504 #ifdef HAVE_LCD_COLOR
505 rb->lcd_set_foreground(LCD_WHITE);
506 #endif
508 /* firework at its destination age?
509 * no = keep aging; yes = delete it. */
510 if(firework_phase[j] < particle_life_values[particle_life])
511 firework_phase[j]++;
512 else
513 firework_phase[j] = -1;
517 /* is autofire on? */
518 if(autofire_delay != 0)
520 elapsed_tick = *rb->current_tick - start_tick;
522 if(elapsed_tick > autofire_delay_values[autofire_delay])
524 rocket_phase[autofire] = 0;
525 init_rocket(autofire);
527 start_tick = *rb->current_tick;
529 if(autofire < MAX_ROCKETS)
530 autofire++;
531 else
532 autofire = 0;
536 rb->lcd_update();
538 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
539 switch(button)
541 case BTN_MENU: /* back to config menu */
542 fireworks_menu();
543 break;
545 case BTN_FIRE: /* fire off rockets manually */
546 case BTN_FIRE|BUTTON_REPEAT:
547 if(thisrocket < MAX_ROCKETS)
548 thisrocket++;
549 else
550 thisrocket=0;
552 rocket_phase[thisrocket] = 0;
553 init_rocket(thisrocket);
554 break;
557 /* Turn on backlight timeout (revert to settings) */
558 backlight_use_settings(rb); /* backlight control in lib/helper.c */
560 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
561 rb->cpu_boost(false);
562 #endif
564 return PLUGIN_OK;