From 718ffdeadaea1c03d562201990ae699a87df526a Mon Sep 17 00:00:00 2001 From: Zakk Roberts Date: Mon, 1 Jan 2007 12:44:05 +0000 Subject: Fully-configurable fireworks display plugin, written by me. Supported on all bitmap LCD targets except Recorder/Ondio (currently runs way too slow on those). Have fun, and happy new year! :) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11876 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/SOURCES | 2 + apps/plugins/fireworks.c | 540 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 apps/plugins/fireworks.c (limited to 'apps/plugins') diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index cf34127f73..8b8a03213a 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -4,6 +4,7 @@ chessclock.c credits.c cube.c dict.c +fireworks.c firmware_flash.c logo.c mosaique.c @@ -93,6 +94,7 @@ rockboy.c /* not support recorder models for now */ #if (LCD_WIDTH > 112) && (LCD_HEIGHT > 64) +fireworks.c xobox.c #endif #if !defined(SANSA_E200) diff --git a/apps/plugins/fireworks.c b/apps/plugins/fireworks.c new file mode 100644 index 0000000000..5a4632749a --- /dev/null +++ b/apps/plugins/fireworks.c @@ -0,0 +1,540 @@ +/*************************************************************************** + * __________ __ ___. + * 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 == ONDIO_PAD) +#define BTN_MENU BUTTON_MENU +#define BTN_FIRE BUTTON_UP +#elif (CONFIG_KEYPAD == IAUDIO_X5_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; +} -- cgit v1.2.3