/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: * * Copyright (C) 2007 Zakk Roberts * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" PLUGIN_HEADER static struct plugin_api* rb; /*** * FIREWORKS.C by ZAKK ROBERTS * Rockbox plugin simulating a fireworks display. * Supports all bitmap LCDs, fully scalable. * Currently disabled for Archos Recorder - runs too slow. ***/ /* All sorts of keymappings.. */ #if (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD) #define BTN_MENU BUTTON_OFF #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) #define BTN_MENU BUTTON_MENU #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == RECORDER_PAD) #define BTN_MENU BUTTON_OFF #define BTN_FIRE BUTTON_PLAY #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD) #define BTN_MENU BUTTON_OFF #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == ONDIO_PAD) #define BTN_MENU BUTTON_MENU #define BTN_FIRE BUTTON_UP #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) #define BTN_MENU BUTTON_POWER #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD) #define BTN_MENU BUTTON_MODE #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == GIGABEAT_PAD) #define BTN_MENU BUTTON_MENU #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == SANSA_E200_PAD) #define BTN_MENU BUTTON_POWER #define BTN_FIRE BUTTON_SELECT #elif (CONFIG_KEYPAD == IRIVER_H10_PAD) #define BTN_MENU BUTTON_POWER #define BTN_FIRE BUTTON_PLAY #endif /* The lowdown on source terminology: * a ROCKET is launched from the LCD bottom. * FIREWORKs are ejected from the rocket when it explodes. */ #define MAX_ROCKETS 40 #define ROCKET_LIFE (LCD_HEIGHT/2) #define ROCKET_LIFE_VAR (LCD_HEIGHT/4) #define ROCKET_SIZE 2 #define ROCKET_MOVEMENT_RANGE 4 #define ROCKET_TRAIL_PARTICLES 50 #define MAX_FIREWORKS 40 #define FIREWORK_MOVEMENT_RANGE 6 #define FIREWORK_SIZE 2 /* position, speed, "phase" (age), color of all fireworks */ int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS]; int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS]; int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS]; int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS]; int firework_phase[MAX_ROCKETS+1]; #ifdef HAVE_LCD_COLOR int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS]; #endif /* position, speed, "phase" (age) of all rockets */ int rocket_xpos[MAX_ROCKETS+1]; int rocket_ypos[MAX_ROCKETS+1]; int rocket_xspeed[MAX_ROCKETS+1]; int rocket_yspeed[MAX_ROCKETS+1]; int rocket_phase[MAX_ROCKETS+1]; int rocket_targetphase[MAX_ROCKETS+1]; /* settings values. these should eventually be saved to * disk. maybe a preset loading/saving system? */ int autofire_delay = 0; int particles_per_firework = 2; int particle_life = 1; int gravity = 1; int show_rockets = 1; int frames_per_second = 4; bool quit_plugin = false; /* firework colors: * firework_colors = brightest firework color, used most of the time. * DARK colors = fireworks are nearly burnt out. * DARKER colors = fireworks are several frames away from burning out. * DARKEST colors = fireworks are a couple frames from burning out. */ #ifdef HAVE_LCD_COLOR static const unsigned firework_colors[] = { LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61), LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254), LCD_RGBPACK(151,84,213) }; static const unsigned firework_dark_colors[] = { LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30), LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128), LCD_RGBPACK(75,42,105) }; static const unsigned firework_darker_colors[] = { LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15), LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64), LCD_RGBPACK(38,21,52) }; static const unsigned firework_darkest_colors[] = { LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7), LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32), LCD_RGBPACK(19,10,26) }; #define EXPLOSION_COLOR LCD_RGBPACK(255,240,0) #endif static const struct opt_items autofire_delay_settings[16] = { { "Off", NULL }, { "50ms", NULL }, { "100ms", NULL }, { "200ms", NULL }, { "300ms", NULL }, { "300ms", NULL }, { "400ms", NULL }, { "500ms", NULL }, { "600ms", NULL }, { "700ms", NULL }, { "800ms", NULL }, { "900ms", NULL }, { "1s", NULL }, { "2s", NULL }, { "3s", NULL }, { "4s", NULL } }; int autofire_delay_values[16] = { 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 }; static const struct opt_items particle_settings[8] = { { "5", NULL }, { "10", NULL }, { "15", NULL }, { "20", NULL }, { "25", NULL }, { "30", NULL }, { "35", NULL }, { "40", NULL }, }; int particle_values[8] = { 5, 10, 15, 20, 25, 30, 35, 40 }; static const struct opt_items particle_life_settings[9] = { { "20 cycles", NULL }, { "30 cycles", NULL }, { "40 cycles", NULL }, { "50 cycles", NULL }, { "60 cycles", NULL }, { "70 cycles", NULL }, { "80 cycles", NULL }, { "90 cycles", NULL }, { "100 cycles", NULL } }; int particle_life_values[9] = { 20, 30, 40, 50, 60, 70, 80, 90, 100 }; static const struct opt_items gravity_settings[4] = { { "Off", NULL }, { "Weak", NULL }, { "Moderate", NULL }, { "Strong", NULL }, }; int gravity_values[4] = { 0, 30, 20, 10 }; #ifdef HAVE_LCD_COLOR static const struct opt_items rocket_settings[3] = { { "No", NULL }, { "Yes (no trails)", NULL }, { "Yes (with trails)", NULL }, }; int rocket_values[4] = { 2, 1, 0 }; #else static const struct opt_items rocket_settings[2] = { { "No", NULL }, { "Yes", NULL }, }; int rocket_values[4] = { 1, 0 }; #endif static const struct opt_items fps_settings[9] = { { "20 FPS", NULL }, { "25 FPS", NULL }, { "30 FPS", NULL }, { "35 FPS", NULL }, { "40 FPS", NULL }, { "45 FPS", NULL }, { "50 FPS", NULL }, { "55 FPS", NULL }, { "60 FPS", NULL } }; int fps_values[9] = { 20, 25, 30, 35, 40, 45, 50, 55, 60 }; static const struct menu_item items[] = { { "Start Demo", NULL }, { "Auto-Fire", NULL }, { "Particles Per Firework", NULL }, { "Particle Life", NULL }, { "Gravity", NULL }, { "Show Rockets", NULL }, { "FPS (Speed)", NULL }, { "Quit", NULL } }; /* called on startup. initializes all variables, etc */ void init_all(void) { int j; for(j=0; jsrand(*rb->current_tick * i); firework_xpoints[firework][i] = x; firework_ypoints[firework][i] = y; firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2; firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2; #ifdef HAVE_LCD_COLOR firework_color[firework][i] = rb->rand() % 7; #endif } } /* called when a rocket is launched. * prepares said rocket to start moving towards its destination. */ void init_rocket(int rocket) { rb->srand(*rb->current_tick); rocket_xpos[rocket] = rb->rand() % LCD_WIDTH; rocket_ypos[rocket] = LCD_HEIGHT; rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2; rocket_yspeed[rocket] = 3; rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket]; } /* startup/configuration menu. */ void fireworks_menu(void) { int m, result; bool menu_quit = false; rb->lcd_setfont(FONT_UI); #ifdef HAVE_LCD_COLOR rb->lcd_set_background(LCD_BLACK); rb->lcd_set_foreground(LCD_WHITE); #endif rb->lcd_clear_display(); rb->lcd_update(); m = rb->menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL); rb->button_clear_queue(); while(!menu_quit) { result = rb->menu_show(m); switch(result) { case 0: rb->lcd_setfont(FONT_SYSFIXED); #ifdef HAVE_LCD_COLOR rb->lcd_set_background(LCD_BLACK); rb->lcd_set_foreground(LCD_WHITE); #endif rb->lcd_clear_display(); rb->lcd_update(); init_all(); menu_quit = true; break; case 1: rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 16, NULL); break; case 2: rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL); break; case 3: rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL); break; case 4: rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL); break; case 5: rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL); break; case 6: rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL); break; case 7: quit_plugin = true; menu_quit = true; break; } } rb->menu_exit(m); } /* this is the plugin entry point */ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { (void)parameter; rb = api; int j, i, autofire=0; int thisrocket=0; int start_tick, elapsed_tick; int button; /* set everything up.. no BL timeout, no backdrop, white-text-on-black-background. */ rb->backlight_set_timeout(1); #if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL); rb->lcd_set_background(LCD_BLACK); rb->lcd_set_foreground(LCD_WHITE); #endif #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif fireworks_menu(); start_tick = *rb->current_tick; while(!quit_plugin) { rb->lcd_clear_display(); /* loop through every possible rocket */ for(j=0; j -1) { #ifdef HAVE_LCD_COLOR /* draw trail, if requested */ if(show_rockets==2) { rb->lcd_set_foreground(LCD_RGBPACK(128,128,128)); rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j], ROCKET_SIZE, ROCKET_SIZE); rb->lcd_set_foreground(LCD_RGBPACK(64,64,64)); rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j], ROCKET_SIZE, ROCKET_SIZE); } #endif /* move rocket */ rocket_xpos[j] += rocket_xspeed[j]; rocket_ypos[j] -= rocket_yspeed[j]; #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_WHITE); #endif if(show_rockets==2 || show_rockets==1) rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j], ROCKET_SIZE, ROCKET_SIZE); /* if(rocket isn't "there" yet) keep moving * if(rocket IS there) explode it. */ if(rocket_phase[j] < rocket_targetphase[j]) rocket_phase[j]++; else { rocket_phase[j] = -1; firework_phase[j] = 0; init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]); } } /* and now onto the fireworks for this particular rocket... */ if(firework_phase[j] > -1) { for(i=0; ilcd_set_foreground(firework_darkest_colors[firework_color[j][i]]); rb->lcd_fillrect(firework_xpoints[j][i]-1, firework_ypoints[j][i]-1, FIREWORK_SIZE+2, FIREWORK_SIZE+2); if(firework_phase[j] < particle_life_values[particle_life]-10) rb->lcd_set_foreground(firework_colors[firework_color[j][i]]); else if(firework_phase[j] < particle_life_values[particle_life]-7) rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]); else if(firework_phase[j] < particle_life_values[particle_life]-3) rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]); else rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]); #endif rb->lcd_fillrect(firework_xpoints[j][i], firework_ypoints[j][i], FIREWORK_SIZE, FIREWORK_SIZE); /* WIP - currently ugly explosion effect #ifdef HAVE_LCD_COLOR if(firework_phase[j] < 10) { rb->lcd_set_foreground(EXPLOSION_COLOR); rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j], rocket_ypos[j]-firework_phase[j], firework_phase[j]*2, firework_phase[j]*2); } #endif */ } #ifdef HAVE_LCD_COLOR rb->lcd_set_foreground(LCD_WHITE); #endif /* firework at its destination age? * no = keep aging; yes = delete it. */ if(firework_phase[j] < particle_life_values[particle_life]) firework_phase[j]++; else firework_phase[j] = -1; } } /* is autofire on? */ if(autofire_delay != 0) { elapsed_tick = *rb->current_tick - start_tick; if(elapsed_tick > autofire_delay_values[autofire_delay]) { rocket_phase[autofire] = 0; init_rocket(autofire); start_tick = *rb->current_tick; if(autofire < MAX_ROCKETS) autofire++; else autofire = 0; } } rb->lcd_update(); button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]); switch(button) { case BTN_MENU: /* back to config menu */ fireworks_menu(); break; case BTN_FIRE: /* fire off rockets manually */ case BTN_FIRE|BUTTON_REPEAT: if(thisrocket < MAX_ROCKETS) thisrocket++; else thisrocket=0; rocket_phase[thisrocket] = 0; init_rocket(thisrocket); break; } } rb->backlight_set_timeout(rb->global_settings->backlight_timeout); #ifdef HAVE_ADJUSTABLE_CPU_FREQ rb->cpu_boost(true); #endif return PLUGIN_OK; }