rockbox/apps/plugins/fireworks.c
Andrew Mahone 23d9812273 loader-initialized global plugin API:
struct plugin_api *rb is declared in PLUGIN_HEADER, and pointed to by
__header.api

the loader uses this pointer to initialize rb before calling entry_point

entry_point is no longer passed a pointer to the plugin API

all plugins, and pluginlib functions, are modified to refer to the
global rb

pluginlib functions which only served to copy the API pointer are
removed

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19776 a1c6a512-1295-4272-9138-f99709370657
2009-01-16 10:34:40 +00:00

572 lines
17 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Zakk Roberts
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/oldmenuapi.h"
#include "lib/helper.h"
PLUGIN_HEADER
/***
* 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) || \
(CONFIG_KEYPAD == IPOD_1G2G_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) || \
(CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
(CONFIG_KEYPAD == MROBE100_PAD)
#define BTN_MENU BUTTON_MENU
#define BTN_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_C200_PAD)
#define BTN_MENU BUTTON_POWER
#define BTN_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define BTN_MENU BUTTON_DOWN
#define BTN_FIRE BUTTON_SELECT
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define BTN_MENU BUTTON_POWER
#define BTN_FIRE BUTTON_PLAY
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define BTN_MENU BUTTON_RC_REC
#define BTN_FIRE BUTTON_RC_PLAY
#elif (CONFIG_KEYPAD == COWOND2_PAD)
#define BTN_MENU (BUTTON_MENU|BUTTON_REL)
#elif CONFIG_KEYPAD == IAUDIO67_PAD
#define BTN_MENU BUTTON_MENU
#define BTN_FIRE BUTTON_PLAY
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define BTN_MENU BUTTON_MENU
#define BTN_FIRE BUTTON_SELECT
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef BTN_MENU
#define BTN_MENU (BUTTON_TOPLEFT|BUTTON_REL)
#endif
#ifndef BTN_FIRE
#define BTN_FIRE BUTTON_CENTER
#endif
#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[15] = {
{ "Off", -1 },
{ "50ms", -1 },
{ "100ms", -1 },
{ "200ms", -1 },
{ "300ms", -1 },
{ "400ms", -1 },
{ "500ms", -1 },
{ "600ms", -1 },
{ "700ms", -1 },
{ "800ms", -1 },
{ "900ms", -1 },
{ "1s", -1 },
{ "2s", -1 },
{ "3s", -1 },
{ "4s", -1 }
};
int autofire_delay_values[15] = {
0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
static const struct opt_items particle_settings[8] = {
{ "5", -1 },
{ "10", -1 },
{ "15", -1 },
{ "20", -1 },
{ "25", -1 },
{ "30", -1 },
{ "35", -1 },
{ "40", -1 },
};
int particle_values[8] = {
5, 10, 15, 20, 25, 30, 35, 40 };
static const struct opt_items particle_life_settings[9] = {
{ "20 cycles", -1 },
{ "30 cycles", -1 },
{ "40 cycles", -1 },
{ "50 cycles", -1 },
{ "60 cycles", -1 },
{ "70 cycles", -1 },
{ "80 cycles", -1 },
{ "90 cycles", -1 },
{ "100 cycles", -1 }
};
int particle_life_values[9] = {
20, 30, 40, 50, 60, 70, 80, 90, 100 };
static const struct opt_items gravity_settings[4] = {
{ "Off", -1 },
{ "Weak", -1 },
{ "Moderate", -1 },
{ "Strong", -1 },
};
int gravity_values[4] = {
0, 30, 20, 10 };
#ifdef HAVE_LCD_COLOR
static const struct opt_items rocket_settings[3] = {
{ "No", -1 },
{ "Yes (no trails)", -1 },
{ "Yes (with trails)", -1 },
};
int rocket_values[4] = {
2, 1, 0 };
#else
static const struct opt_items rocket_settings[2] = {
{ "No", -1 },
{ "Yes", -1 },
};
int rocket_values[4] = {
1, 0 };
#endif
static const struct opt_items fps_settings[9] = {
{ "20 FPS", -1 },
{ "25 FPS", -1 },
{ "30 FPS", -1 },
{ "35 FPS", -1 },
{ "40 FPS", -1 },
{ "45 FPS", -1 },
{ "50 FPS", -1 },
{ "55 FPS", -1 },
{ "60 FPS", -1 }
};
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; j<MAX_ROCKETS; j++)
firework_phase[j] = -1;
}
/* called when a rocket hits its destination height.
* prepares all associated fireworks to be expelled. */
void init_explode(int x, int y, int firework, int points)
{
int i;
for(i=0; i<points; i++)
{
rb->srand(*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 = menu_init(items, sizeof(items) / sizeof(*items),
NULL, NULL, NULL, NULL);
rb->button_clear_queue();
while(!menu_quit)
{
result = 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, 15, 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;
}
}
menu_exit(m);
}
/* this is the plugin entry point */
enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
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. */
backlight_force_on(); /* backlight control in lib/helper.c */
#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<MAX_ROCKETS; j++)
{
/* if the current rocket is actually moving/"alive" then go on and
* move/update/explode it */
if(rocket_phase[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; i<particle_values[particles_per_firework]; i++)
{
firework_xpoints[j][i] += firework_xspeed[j][i];
firework_ypoints[j][i] += firework_yspeed[j][i];
if(gravity != 0)
firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
#ifdef HAVE_LCD_COLOR
rb->lcd_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;
}
}
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings(); /* backlight control in lib/helper.c */
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
return PLUGIN_OK;
}