Commit FS#9617 - Keymaps for Plugins fuze by Thomas Martitz.
[kugel-rb.git] / apps / plugins / fireworks.c
blob64b30dc36eae958b9b498cbce8c981ab49dd83f7
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"
25 PLUGIN_HEADER
27 static const struct plugin_api* rb;
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 /* All sorts of keymappings.. */
37 #if (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
38 #define BTN_MENU BUTTON_OFF
39 #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
44 #elif (CONFIG_KEYPAD == RECORDER_PAD)
45 #define BTN_MENU BUTTON_OFF
46 #define BTN_FIRE BUTTON_PLAY
47 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
48 #define BTN_MENU BUTTON_OFF
49 #define BTN_FIRE BUTTON_SELECT
50 #elif (CONFIG_KEYPAD == ONDIO_PAD)
51 #define BTN_MENU BUTTON_MENU
52 #define BTN_FIRE BUTTON_UP
53 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
54 #define BTN_MENU BUTTON_POWER
55 #define BTN_FIRE BUTTON_SELECT
56 #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
57 #define BTN_MENU BUTTON_MODE
58 #define BTN_FIRE BUTTON_SELECT
59 #elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
60 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
61 (CONFIG_KEYPAD == MROBE100_PAD)
62 #define BTN_MENU BUTTON_MENU
63 #define BTN_FIRE BUTTON_SELECT
64 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
65 (CONFIG_KEYPAD == SANSA_C200_PAD)
66 #define BTN_MENU BUTTON_POWER
67 #define BTN_FIRE BUTTON_SELECT
68 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
69 #define BTN_MENU BUTTON_DOWN
70 #define BTN_FIRE BUTTON_SELECT
71 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
72 #define BTN_MENU BUTTON_POWER
73 #define BTN_FIRE BUTTON_PLAY
74 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
75 #define BTN_MENU BUTTON_RC_REC
76 #define BTN_FIRE BUTTON_RC_PLAY
77 #elif (CONFIG_KEYPAD == COWOND2_PAD)
78 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
79 #elif CONFIG_KEYPAD == IAUDIO67_PAD
80 #define BTN_MENU BUTTON_MENU
81 #define BTN_FIRE BUTTON_PLAY
82 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
83 #define BTN_MENU BUTTON_MENU
84 #define BTN_FIRE BUTTON_SELECT
85 #else
86 #error No keymap defined!
87 #endif
89 #ifdef HAVE_TOUCHSCREEN
90 #ifndef BTN_MENU
91 #define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
92 #endif
93 #ifndef BTN_FIRE
94 #define BTN_FIRE BUTTON_CENTER
95 #endif
96 #endif
98 /* The lowdown on source terminology:
99 * a ROCKET is launched from the LCD bottom.
100 * FIREWORKs are ejected from the rocket when it explodes. */
102 #define MAX_ROCKETS 40
103 #define ROCKET_LIFE (LCD_HEIGHT/2)
104 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
105 #define ROCKET_SIZE 2
106 #define ROCKET_MOVEMENT_RANGE 4
107 #define ROCKET_TRAIL_PARTICLES 50
109 #define MAX_FIREWORKS 40
110 #define FIREWORK_MOVEMENT_RANGE 6
111 #define FIREWORK_SIZE 2
113 /* position, speed, "phase" (age), color of all fireworks */
114 int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
115 int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
116 int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
117 int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
118 int firework_phase[MAX_ROCKETS+1];
119 #ifdef HAVE_LCD_COLOR
120 int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
121 #endif
123 /* position, speed, "phase" (age) of all rockets */
124 int rocket_xpos[MAX_ROCKETS+1];
125 int rocket_ypos[MAX_ROCKETS+1];
126 int rocket_xspeed[MAX_ROCKETS+1];
127 int rocket_yspeed[MAX_ROCKETS+1];
128 int rocket_phase[MAX_ROCKETS+1];
129 int rocket_targetphase[MAX_ROCKETS+1];
131 /* settings values. these should eventually be saved to
132 * disk. maybe a preset loading/saving system? */
133 int autofire_delay = 0;
134 int particles_per_firework = 2;
135 int particle_life = 1;
136 int gravity = 1;
137 int show_rockets = 1;
138 int frames_per_second = 4;
139 bool quit_plugin = false;
141 /* firework colors:
142 * firework_colors = brightest firework color, used most of the time.
143 * DARK colors = fireworks are nearly burnt out.
144 * DARKER colors = fireworks are several frames away from burning out.
145 * DARKEST colors = fireworks are a couple frames from burning out. */
146 #ifdef HAVE_LCD_COLOR
147 static const unsigned firework_colors[] = {
148 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
149 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
150 LCD_RGBPACK(151,84,213) };
152 static const unsigned firework_dark_colors[] = {
153 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
154 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
155 LCD_RGBPACK(75,42,105) };
157 static const unsigned firework_darker_colors[] = {
158 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
159 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
160 LCD_RGBPACK(38,21,52) };
162 static const unsigned firework_darkest_colors[] = {
163 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
164 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
165 LCD_RGBPACK(19,10,26) };
167 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
169 #endif
171 static const struct opt_items autofire_delay_settings[15] = {
172 { "Off", -1 },
173 { "50ms", -1 },
174 { "100ms", -1 },
175 { "200ms", -1 },
176 { "300ms", -1 },
177 { "400ms", -1 },
178 { "500ms", -1 },
179 { "600ms", -1 },
180 { "700ms", -1 },
181 { "800ms", -1 },
182 { "900ms", -1 },
183 { "1s", -1 },
184 { "2s", -1 },
185 { "3s", -1 },
186 { "4s", -1 }
189 int autofire_delay_values[15] = {
190 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
192 static const struct opt_items particle_settings[8] = {
193 { "5", -1 },
194 { "10", -1 },
195 { "15", -1 },
196 { "20", -1 },
197 { "25", -1 },
198 { "30", -1 },
199 { "35", -1 },
200 { "40", -1 },
203 int particle_values[8] = {
204 5, 10, 15, 20, 25, 30, 35, 40 };
206 static const struct opt_items particle_life_settings[9] = {
207 { "20 cycles", -1 },
208 { "30 cycles", -1 },
209 { "40 cycles", -1 },
210 { "50 cycles", -1 },
211 { "60 cycles", -1 },
212 { "70 cycles", -1 },
213 { "80 cycles", -1 },
214 { "90 cycles", -1 },
215 { "100 cycles", -1 }
218 int particle_life_values[9] = {
219 20, 30, 40, 50, 60, 70, 80, 90, 100 };
221 static const struct opt_items gravity_settings[4] = {
222 { "Off", -1 },
223 { "Weak", -1 },
224 { "Moderate", -1 },
225 { "Strong", -1 },
228 int gravity_values[4] = {
229 0, 30, 20, 10 };
231 #ifdef HAVE_LCD_COLOR
233 static const struct opt_items rocket_settings[3] = {
234 { "No", -1 },
235 { "Yes (no trails)", -1 },
236 { "Yes (with trails)", -1 },
238 int rocket_values[4] = {
239 2, 1, 0 };
241 #else
243 static const struct opt_items rocket_settings[2] = {
244 { "No", -1 },
245 { "Yes", -1 },
247 int rocket_values[4] = {
248 1, 0 };
250 #endif
252 static const struct opt_items fps_settings[9] = {
253 { "20 FPS", -1 },
254 { "25 FPS", -1 },
255 { "30 FPS", -1 },
256 { "35 FPS", -1 },
257 { "40 FPS", -1 },
258 { "45 FPS", -1 },
259 { "50 FPS", -1 },
260 { "55 FPS", -1 },
261 { "60 FPS", -1 }
264 int fps_values[9] = {
265 20, 25, 30, 35, 40, 45, 50, 55, 60 };
267 static const struct menu_item items[] = {
268 { "Start Demo", NULL },
269 { "Auto-Fire", NULL },
270 { "Particles Per Firework", NULL },
271 { "Particle Life", NULL },
272 { "Gravity", NULL },
273 { "Show Rockets", NULL },
274 { "FPS (Speed)", NULL },
275 { "Quit", NULL }
278 /* called on startup. initializes all variables, etc */
279 void init_all(void)
281 int j;
283 for(j=0; j<MAX_ROCKETS; j++)
284 firework_phase[j] = -1;
287 /* called when a rocket hits its destination height.
288 * prepares all associated fireworks to be expelled. */
289 void init_explode(int x, int y, int firework, int points)
291 int i;
293 for(i=0; i<points; i++)
295 rb->srand(*rb->current_tick * i);
297 firework_xpoints[firework][i] = x;
298 firework_ypoints[firework][i] = y;
300 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
301 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
303 #ifdef HAVE_LCD_COLOR
304 firework_color[firework][i] = rb->rand() % 7;
305 #endif
309 /* called when a rocket is launched.
310 * prepares said rocket to start moving towards its destination. */
311 void init_rocket(int rocket)
313 rb->srand(*rb->current_tick);
315 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
316 rocket_ypos[rocket] = LCD_HEIGHT;
318 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
319 rocket_yspeed[rocket] = 3;
321 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
324 /* startup/configuration menu. */
325 void fireworks_menu(void)
327 int m, result;
328 bool menu_quit = false;
330 rb->lcd_setfont(FONT_UI);
331 #ifdef HAVE_LCD_COLOR
332 rb->lcd_set_background(LCD_BLACK);
333 rb->lcd_set_foreground(LCD_WHITE);
334 #endif
335 rb->lcd_clear_display();
336 rb->lcd_update();
338 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
339 NULL, NULL, NULL, NULL);
341 rb->button_clear_queue();
343 while(!menu_quit)
345 result = menu_show(m);
347 switch(result)
349 case 0:
350 rb->lcd_setfont(FONT_SYSFIXED);
352 #ifdef HAVE_LCD_COLOR
353 rb->lcd_set_background(LCD_BLACK);
354 rb->lcd_set_foreground(LCD_WHITE);
355 #endif
357 rb->lcd_clear_display();
358 rb->lcd_update();
360 init_all();
361 menu_quit = true;
362 break;
364 case 1:
365 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 15, NULL);
366 break;
368 case 2:
369 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
370 break;
372 case 3:
373 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
374 break;
376 case 4:
377 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
378 break;
380 case 5:
381 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
382 break;
384 case 6:
385 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
386 break;
388 case 7:
389 quit_plugin = true;
390 menu_quit = true;
391 break;
395 menu_exit(m);
398 /* this is the plugin entry point */
399 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
401 (void)parameter;
403 rb = api;
405 int j, i, autofire=0;
406 int thisrocket=0;
407 int start_tick, elapsed_tick;
408 int button;
410 /* set everything up.. no BL timeout, no backdrop,
411 white-text-on-black-background. */
412 backlight_force_on(rb); /* backlight control in lib/helper.c */
413 #if LCD_DEPTH > 1
414 rb->lcd_set_backdrop(NULL);
415 rb->lcd_set_background(LCD_BLACK);
416 rb->lcd_set_foreground(LCD_WHITE);
417 #endif
419 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
420 rb->cpu_boost(true);
421 #endif
423 fireworks_menu();
425 start_tick = *rb->current_tick;
427 while(!quit_plugin)
429 rb->lcd_clear_display();
431 /* loop through every possible rocket */
432 for(j=0; j<MAX_ROCKETS; j++)
434 /* if the current rocket is actually moving/"alive" then go on and
435 * move/update/explode it */
436 if(rocket_phase[j] > -1)
438 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
439 if(show_rockets==2)
441 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
442 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
443 ROCKET_SIZE, ROCKET_SIZE);
444 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
445 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
446 ROCKET_SIZE, ROCKET_SIZE);
448 #endif
450 /* move rocket */
451 rocket_xpos[j] += rocket_xspeed[j];
452 rocket_ypos[j] -= rocket_yspeed[j];
454 #ifdef HAVE_LCD_COLOR
455 rb->lcd_set_foreground(LCD_WHITE);
456 #endif
457 if(show_rockets==2 || show_rockets==1)
458 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
459 ROCKET_SIZE, ROCKET_SIZE);
461 /* if(rocket isn't "there" yet) keep moving
462 * if(rocket IS there) explode it. */
463 if(rocket_phase[j] < rocket_targetphase[j])
464 rocket_phase[j]++;
465 else
467 rocket_phase[j] = -1;
469 firework_phase[j] = 0;
470 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
474 /* and now onto the fireworks for this particular rocket... */
475 if(firework_phase[j] > -1)
477 for(i=0; i<particle_values[particles_per_firework]; i++)
479 firework_xpoints[j][i] += firework_xspeed[j][i];
480 firework_ypoints[j][i] += firework_yspeed[j][i];
482 if(gravity != 0)
483 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
485 #ifdef HAVE_LCD_COLOR
486 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
487 rb->lcd_fillrect(firework_xpoints[j][i]-1,
488 firework_ypoints[j][i]-1,
489 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
491 if(firework_phase[j] < particle_life_values[particle_life]-10)
492 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
493 else if(firework_phase[j] < particle_life_values[particle_life]-7)
494 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
495 else if(firework_phase[j] < particle_life_values[particle_life]-3)
496 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
497 else
498 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
499 #endif
500 rb->lcd_fillrect(firework_xpoints[j][i],
501 firework_ypoints[j][i],
502 FIREWORK_SIZE, FIREWORK_SIZE);
503 /* WIP - currently ugly explosion effect
504 #ifdef HAVE_LCD_COLOR
505 if(firework_phase[j] < 10)
507 rb->lcd_set_foreground(EXPLOSION_COLOR);
508 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
509 rocket_ypos[j]-firework_phase[j],
510 firework_phase[j]*2, firework_phase[j]*2);
512 #endif */
515 #ifdef HAVE_LCD_COLOR
516 rb->lcd_set_foreground(LCD_WHITE);
517 #endif
519 /* firework at its destination age?
520 * no = keep aging; yes = delete it. */
521 if(firework_phase[j] < particle_life_values[particle_life])
522 firework_phase[j]++;
523 else
524 firework_phase[j] = -1;
528 /* is autofire on? */
529 if(autofire_delay != 0)
531 elapsed_tick = *rb->current_tick - start_tick;
533 if(elapsed_tick > autofire_delay_values[autofire_delay])
535 rocket_phase[autofire] = 0;
536 init_rocket(autofire);
538 start_tick = *rb->current_tick;
540 if(autofire < MAX_ROCKETS)
541 autofire++;
542 else
543 autofire = 0;
547 rb->lcd_update();
549 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
550 switch(button)
552 case BTN_MENU: /* back to config menu */
553 fireworks_menu();
554 break;
556 case BTN_FIRE: /* fire off rockets manually */
557 case BTN_FIRE|BUTTON_REPEAT:
558 if(thisrocket < MAX_ROCKETS)
559 thisrocket++;
560 else
561 thisrocket=0;
563 rocket_phase[thisrocket] = 0;
564 init_rocket(thisrocket);
565 break;
568 /* Turn on backlight timeout (revert to settings) */
569 backlight_use_settings(rb); /* backlight control in lib/helper.c */
571 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
572 rb->cpu_boost(false);
573 #endif
575 return PLUGIN_OK;