fix red.
[kugel-rb/myfork.git] / apps / plugins / fireworks.c
blob4b3e2fd117f10320e458dcb553d3819cf348dc05
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"
25 PLUGIN_HEADER
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
39 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
40 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
41 #define BTN_MENU BUTTON_MENU
42 #define BTN_FIRE BUTTON_SELECT
44 #elif (CONFIG_KEYPAD == RECORDER_PAD)
45 #define BTN_MENU BUTTON_OFF
46 #define BTN_FIRE BUTTON_PLAY
48 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
49 #define BTN_MENU BUTTON_OFF
50 #define BTN_FIRE BUTTON_SELECT
52 #elif (CONFIG_KEYPAD == ONDIO_PAD)
53 #define BTN_MENU BUTTON_MENU
54 #define BTN_FIRE BUTTON_UP
56 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
57 #define BTN_MENU BUTTON_POWER
58 #define BTN_FIRE BUTTON_SELECT
60 #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
61 #define BTN_MENU BUTTON_MODE
62 #define BTN_FIRE BUTTON_SELECT
64 #elif (CONFIG_KEYPAD == GIGABEAT_PAD) || \
65 (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
66 (CONFIG_KEYPAD == MROBE100_PAD)
67 #define BTN_MENU BUTTON_MENU
68 #define BTN_FIRE BUTTON_SELECT
70 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
71 (CONFIG_KEYPAD == SANSA_C200_PAD)
72 #define BTN_MENU BUTTON_POWER
73 #define BTN_FIRE BUTTON_SELECT
75 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
76 #define BTN_MENU (BUTTON_HOME|BUTTON_REPEAT)
77 #define BTN_FIRE BUTTON_SELECT
79 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
80 #define BTN_MENU BUTTON_POWER
81 #define BTN_FIRE BUTTON_PLAY
83 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
84 #define BTN_MENU BUTTON_RC_REC
85 #define BTN_FIRE BUTTON_RC_PLAY
87 #elif (CONFIG_KEYPAD == COWOND2_PAD)
88 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
90 #elif CONFIG_KEYPAD == IAUDIO67_PAD
91 #define BTN_MENU BUTTON_MENU
92 #define BTN_FIRE BUTTON_PLAY
94 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
95 #define BTN_MENU BUTTON_MENU
96 #define BTN_FIRE BUTTON_SELECT
98 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
99 #define BTN_MENU BUTTON_MENU
100 #define BTN_FIRE BUTTON_SELECT
102 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
103 #define BTN_MENU (BUTTON_MENU|BUTTON_REL)
105 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
106 #define BTN_MENU BUTTON_LEFT
107 #define BTN_FIRE BUTTON_PLAY
109 #elif defined(HAVE_TOUCHSCREEN)
110 /* This is a touchscreen target */
111 #else
112 #error No keymap defined!
113 #endif
115 #ifdef HAVE_TOUCHSCREEN
116 #ifndef BTN_MENU
117 #define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
118 #endif
119 #ifndef BTN_FIRE
120 #define BTN_FIRE BUTTON_CENTER
121 #endif
122 #endif
124 /* The lowdown on source terminology:
125 * a ROCKET is launched from the LCD bottom.
126 * FIREWORKs are ejected from the rocket when it explodes. */
128 #define MAX_ROCKETS 40
129 #define ROCKET_LIFE (LCD_HEIGHT/2)
130 #define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
131 #define ROCKET_SIZE 2
132 #define ROCKET_MOVEMENT_RANGE 4
133 #define ROCKET_TRAIL_PARTICLES 50
135 #define MAX_FIREWORKS 40
136 #define FIREWORK_MOVEMENT_RANGE 6
137 #define FIREWORK_SIZE 2
139 /* position, speed, "phase" (age), color of all fireworks */
140 int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
141 int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
142 int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
143 int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
144 int firework_phase[MAX_ROCKETS+1];
145 #ifdef HAVE_LCD_COLOR
146 int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
147 #endif
149 /* position, speed, "phase" (age) of all rockets */
150 int rocket_xpos[MAX_ROCKETS+1];
151 int rocket_ypos[MAX_ROCKETS+1];
152 int rocket_xspeed[MAX_ROCKETS+1];
153 int rocket_yspeed[MAX_ROCKETS+1];
154 int rocket_phase[MAX_ROCKETS+1];
155 int rocket_targetphase[MAX_ROCKETS+1];
157 /* settings values. these should eventually be saved to
158 * disk. maybe a preset loading/saving system? */
159 int autofire_delay = 0;
160 int particles_per_firework = 2;
161 int particle_life = 1;
162 int gravity = 1;
163 int show_rockets = 1;
164 int frames_per_second = 4;
165 bool quit_plugin = false;
167 /* firework colors:
168 * firework_colors = brightest firework color, used most of the time.
169 * DARK colors = fireworks are nearly burnt out.
170 * DARKER colors = fireworks are several frames away from burning out.
171 * DARKEST colors = fireworks are a couple frames from burning out. */
172 #ifdef HAVE_LCD_COLOR
173 static const unsigned firework_colors[] = {
174 LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
175 LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
176 LCD_RGBPACK(151,84,213) };
178 static const unsigned firework_dark_colors[] = {
179 LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
180 LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
181 LCD_RGBPACK(75,42,105) };
183 static const unsigned firework_darker_colors[] = {
184 LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
185 LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
186 LCD_RGBPACK(38,21,52) };
188 static const unsigned firework_darkest_colors[] = {
189 LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
190 LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
191 LCD_RGBPACK(19,10,26) };
193 #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
195 #endif
197 static const struct opt_items autofire_delay_settings[15] = {
198 { "Off", -1 },
199 { "50ms", -1 },
200 { "100ms", -1 },
201 { "200ms", -1 },
202 { "300ms", -1 },
203 { "400ms", -1 },
204 { "500ms", -1 },
205 { "600ms", -1 },
206 { "700ms", -1 },
207 { "800ms", -1 },
208 { "900ms", -1 },
209 { "1s", -1 },
210 { "2s", -1 },
211 { "3s", -1 },
212 { "4s", -1 }
215 int autofire_delay_values[15] = {
216 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
218 static const struct opt_items particle_settings[8] = {
219 { "5", -1 },
220 { "10", -1 },
221 { "15", -1 },
222 { "20", -1 },
223 { "25", -1 },
224 { "30", -1 },
225 { "35", -1 },
226 { "40", -1 },
229 int particle_values[8] = {
230 5, 10, 15, 20, 25, 30, 35, 40 };
232 static const struct opt_items particle_life_settings[9] = {
233 { "20 cycles", -1 },
234 { "30 cycles", -1 },
235 { "40 cycles", -1 },
236 { "50 cycles", -1 },
237 { "60 cycles", -1 },
238 { "70 cycles", -1 },
239 { "80 cycles", -1 },
240 { "90 cycles", -1 },
241 { "100 cycles", -1 }
244 int particle_life_values[9] = {
245 20, 30, 40, 50, 60, 70, 80, 90, 100 };
247 static const struct opt_items gravity_settings[4] = {
248 { "Off", -1 },
249 { "Weak", -1 },
250 { "Moderate", -1 },
251 { "Strong", -1 },
254 int gravity_values[4] = {
255 0, 30, 20, 10 };
257 #ifdef HAVE_LCD_COLOR
259 static const struct opt_items rocket_settings[3] = {
260 { "No", -1 },
261 { "Yes (no trails)", -1 },
262 { "Yes (with trails)", -1 },
264 int rocket_values[4] = {
265 2, 1, 0 };
267 #else
269 static const struct opt_items rocket_settings[2] = {
270 { "No", -1 },
271 { "Yes", -1 },
273 int rocket_values[4] = {
274 1, 0 };
276 #endif
278 static const struct opt_items fps_settings[9] = {
279 { "20 FPS", -1 },
280 { "25 FPS", -1 },
281 { "30 FPS", -1 },
282 { "35 FPS", -1 },
283 { "40 FPS", -1 },
284 { "45 FPS", -1 },
285 { "50 FPS", -1 },
286 { "55 FPS", -1 },
287 { "60 FPS", -1 }
290 int fps_values[9] = {
291 20, 25, 30, 35, 40, 45, 50, 55, 60 };
293 MENUITEM_STRINGLIST(menu, "Fireworks Menu", NULL,
294 "Start Demo", "Auto-Fire", "Particles Per Firework",
295 "Particle Life", "Gravity", "Show Rockets",
296 "FPS (Speed)", "Playback Control", "Quit");
298 /* called on startup. initializes all variables, etc */
299 void init_all(void)
301 int j;
303 for(j=0; j<MAX_ROCKETS; j++)
304 firework_phase[j] = -1;
307 /* called when a rocket hits its destination height.
308 * prepares all associated fireworks to be expelled. */
309 void init_explode(int x, int y, int firework, int points)
311 int i;
313 for(i=0; i<points; i++)
315 rb->srand(*rb->current_tick * i);
317 firework_xpoints[firework][i] = x;
318 firework_ypoints[firework][i] = y;
320 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
321 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
323 #ifdef HAVE_LCD_COLOR
324 firework_color[firework][i] = rb->rand() % 7;
325 #endif
329 /* called when a rocket is launched.
330 * prepares said rocket to start moving towards its destination. */
331 void init_rocket(int rocket)
333 rb->srand(*rb->current_tick);
335 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
336 rocket_ypos[rocket] = LCD_HEIGHT;
338 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
339 rocket_yspeed[rocket] = 3;
341 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
344 /* startup/configuration menu. */
345 void fireworks_menu(void)
347 int selected = 0, result;
348 bool menu_quit = false;
350 rb->lcd_setfont(FONT_UI);
351 #ifdef HAVE_LCD_COLOR
352 rb->lcd_set_background(LCD_BLACK);
353 rb->lcd_set_foreground(LCD_WHITE);
354 #endif
355 rb->lcd_clear_display();
356 rb->lcd_update();
358 rb->button_clear_queue();
360 while(!menu_quit)
362 result = rb->do_menu(&menu, &selected, NULL, false);
364 switch(result)
366 case 0:
367 rb->lcd_setfont(FONT_SYSFIXED);
369 #ifdef HAVE_LCD_COLOR
370 rb->lcd_set_background(LCD_BLACK);
371 rb->lcd_set_foreground(LCD_WHITE);
372 #endif
374 rb->lcd_clear_display();
375 rb->lcd_update();
377 init_all();
378 menu_quit = true;
379 break;
381 case 1:
382 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 15, NULL);
383 break;
385 case 2:
386 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
387 break;
389 case 3:
390 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
391 break;
393 case 4:
394 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
395 break;
397 case 5:
398 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
399 break;
401 case 6:
402 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
403 break;
405 case 7:
406 playback_control(NULL);
407 break;
409 case 8:
410 quit_plugin = true;
411 menu_quit = true;
412 break;
417 /* this is the plugin entry point */
418 enum plugin_status plugin_start(const void* parameter)
420 (void)parameter;
422 int j, i, autofire=0;
423 int thisrocket=0;
424 int start_tick, elapsed_tick;
425 int button;
427 /* set everything up.. no BL timeout, no backdrop,
428 white-text-on-black-background. */
429 backlight_force_on(); /* backlight control in lib/helper.c */
430 #if LCD_DEPTH > 1
431 rb->lcd_set_backdrop(NULL);
432 rb->lcd_set_background(LCD_BLACK);
433 rb->lcd_set_foreground(LCD_WHITE);
434 #endif
436 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
437 rb->cpu_boost(true);
438 #endif
440 fireworks_menu();
442 start_tick = *rb->current_tick;
444 while(!quit_plugin)
446 rb->lcd_clear_display();
448 /* loop through every possible rocket */
449 for(j=0; j<MAX_ROCKETS; j++)
451 /* if the current rocket is actually moving/"alive" then go on and
452 * move/update/explode it */
453 if(rocket_phase[j] > -1)
455 #ifdef HAVE_LCD_COLOR /* draw trail, if requested */
456 if(show_rockets==2)
458 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
459 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
460 ROCKET_SIZE, ROCKET_SIZE);
461 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
462 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
463 ROCKET_SIZE, ROCKET_SIZE);
465 #endif
467 /* move rocket */
468 rocket_xpos[j] += rocket_xspeed[j];
469 rocket_ypos[j] -= rocket_yspeed[j];
471 #ifdef HAVE_LCD_COLOR
472 rb->lcd_set_foreground(LCD_WHITE);
473 #endif
474 if(show_rockets==2 || show_rockets==1)
475 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
476 ROCKET_SIZE, ROCKET_SIZE);
478 /* if(rocket isn't "there" yet) keep moving
479 * if(rocket IS there) explode it. */
480 if(rocket_phase[j] < rocket_targetphase[j])
481 rocket_phase[j]++;
482 else
484 rocket_phase[j] = -1;
486 firework_phase[j] = 0;
487 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
491 /* and now onto the fireworks for this particular rocket... */
492 if(firework_phase[j] > -1)
494 for(i=0; i<particle_values[particles_per_firework]; i++)
496 firework_xpoints[j][i] += firework_xspeed[j][i];
497 firework_ypoints[j][i] += firework_yspeed[j][i];
499 if(gravity != 0)
500 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
502 #ifdef HAVE_LCD_COLOR
503 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
504 rb->lcd_fillrect(firework_xpoints[j][i]-1,
505 firework_ypoints[j][i]-1,
506 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
508 if(firework_phase[j] < particle_life_values[particle_life]-10)
509 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
510 else if(firework_phase[j] < particle_life_values[particle_life]-7)
511 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
512 else if(firework_phase[j] < particle_life_values[particle_life]-3)
513 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
514 else
515 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
516 #endif
517 rb->lcd_fillrect(firework_xpoints[j][i],
518 firework_ypoints[j][i],
519 FIREWORK_SIZE, FIREWORK_SIZE);
520 /* WIP - currently ugly explosion effect
521 #ifdef HAVE_LCD_COLOR
522 if(firework_phase[j] < 10)
524 rb->lcd_set_foreground(EXPLOSION_COLOR);
525 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
526 rocket_ypos[j]-firework_phase[j],
527 firework_phase[j]*2, firework_phase[j]*2);
529 #endif */
532 #ifdef HAVE_LCD_COLOR
533 rb->lcd_set_foreground(LCD_WHITE);
534 #endif
536 /* firework at its destination age?
537 * no = keep aging; yes = delete it. */
538 if(firework_phase[j] < particle_life_values[particle_life])
539 firework_phase[j]++;
540 else
541 firework_phase[j] = -1;
545 /* is autofire on? */
546 if(autofire_delay != 0)
548 elapsed_tick = *rb->current_tick - start_tick;
550 if(elapsed_tick > autofire_delay_values[autofire_delay])
552 rocket_phase[autofire] = 0;
553 init_rocket(autofire);
555 start_tick = *rb->current_tick;
557 if(autofire < MAX_ROCKETS)
558 autofire++;
559 else
560 autofire = 0;
564 rb->lcd_update();
566 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
567 switch(button)
569 case BTN_MENU: /* back to config menu */
570 fireworks_menu();
571 break;
573 case BTN_FIRE: /* fire off rockets manually */
574 case BTN_FIRE|BUTTON_REPEAT:
575 if(thisrocket < MAX_ROCKETS)
576 thisrocket++;
577 else
578 thisrocket=0;
580 rocket_phase[thisrocket] = 0;
581 init_rocket(thisrocket);
582 break;
585 /* Turn on backlight timeout (revert to settings) */
586 backlight_use_settings(); /* backlight control in lib/helper.c */
588 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
589 rb->cpu_boost(false);
590 #endif
592 return PLUGIN_OK;