summaryrefslogtreecommitdiff
path: root/apps/plugins/fireworks.c
diff options
context:
space:
mode:
authorZakk Roberts <midk@rockbox.org>2007-01-01 12:44:05 +0000
committerZakk Roberts <midk@rockbox.org>2007-01-01 12:44:05 +0000
commit718ffdeadaea1c03d562201990ae699a87df526a (patch)
tree65128ae3a096abbb4c0b9bab524b04db2e80b30d /apps/plugins/fireworks.c
parent147693819d3275ec9eba8b580fa6ff8ada9896f3 (diff)
downloadrockbox-718ffdeadaea1c03d562201990ae699a87df526a.tar.gz
rockbox-718ffdeadaea1c03d562201990ae699a87df526a.zip
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
Diffstat (limited to 'apps/plugins/fireworks.c')
-rw-r--r--apps/plugins/fireworks.c540
1 files changed, 540 insertions, 0 deletions
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:
9 *
10 * Copyright (C) 2007 Zakk Roberts
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "plugin.h"
20
21PLUGIN_HEADER
22
23static struct plugin_api* rb;
24
25/***
26 * FIREWORKS.C by ZAKK ROBERTS
27 * Rockbox plugin simulating a fireworks display.
28 * Supports all bitmap LCDs, fully scalable.
29 * Currently disabled for Archos Recorder - runs too slow.
30 ***/
31
32/* All sorts of keymappings.. */
33#if (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
34#define BTN_MENU BUTTON_OFF
35#define BTN_FIRE BUTTON_SELECT
36#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD)
37#define BTN_MENU BUTTON_MENU
38#define BTN_FIRE BUTTON_SELECT
39#elif (CONFIG_KEYPAD == RECORDER_PAD)
40#define BTN_MENU BUTTON_OFF
41#define BTN_FIRE BUTTON_PLAY
42#elif (CONFIG_KEYPAD == ONDIO_PAD)
43#define BTN_MENU BUTTON_MENU
44#define BTN_FIRE BUTTON_UP
45#elif (CONFIG_KEYPAD == IAUDIO_X5_PAD)
46#define BTN_MENU BUTTON_POWER
47#define BTN_FIRE BUTTON_SELECT
48#elif (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
49#define BTN_MENU BUTTON_MODE
50#define BTN_FIRE BUTTON_SELECT
51#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
52#define BTN_MENU BUTTON_MENU
53#define BTN_FIRE BUTTON_SELECT
54#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
55#define BTN_MENU BUTTON_POWER
56#define BTN_FIRE BUTTON_SELECT
57#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
58#define BTN_MENU BUTTON_POWER
59#define BTN_FIRE BUTTON_PLAY
60#endif
61
62/* The lowdown on source terminology:
63 * a ROCKET is launched from the LCD bottom.
64 * FIREWORKs are ejected from the rocket when it explodes. */
65
66#define MAX_ROCKETS 40
67#define ROCKET_LIFE (LCD_HEIGHT/2)
68#define ROCKET_LIFE_VAR (LCD_HEIGHT/4)
69#define ROCKET_SIZE 2
70#define ROCKET_MOVEMENT_RANGE 4
71#define ROCKET_TRAIL_PARTICLES 50
72
73#define MAX_FIREWORKS 40
74#define FIREWORK_MOVEMENT_RANGE 6
75#define FIREWORK_SIZE 2
76
77/* position, speed, "phase" (age), color of all fireworks */
78int firework_xpoints[MAX_ROCKETS+1][MAX_FIREWORKS];
79int firework_ypoints[MAX_ROCKETS+1][MAX_FIREWORKS];
80int firework_xspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
81int firework_yspeed[MAX_ROCKETS+1][MAX_FIREWORKS];
82int firework_phase[MAX_ROCKETS+1];
83#ifdef HAVE_LCD_COLOR
84int firework_color[MAX_ROCKETS+1][MAX_FIREWORKS];
85#endif
86
87/* position, speed, "phase" (age) of all rockets */
88int rocket_xpos[MAX_ROCKETS+1];
89int rocket_ypos[MAX_ROCKETS+1];
90int rocket_xspeed[MAX_ROCKETS+1];
91int rocket_yspeed[MAX_ROCKETS+1];
92int rocket_phase[MAX_ROCKETS+1];
93int rocket_targetphase[MAX_ROCKETS+1];
94
95/* settings values. these should eventually be saved to
96 * disk. maybe a preset loading/saving system? */
97int autofire_delay = 0;
98int particles_per_firework = 2;
99int particle_life = 1;
100int gravity = 1;
101int show_rockets = 1;
102int frames_per_second = 4;
103bool quit_plugin = false;
104
105/* firework colors:
106 * firework_colors = brightest firework color, used most of the time.
107 * DARK colors = fireworks are nearly burnt out.
108 * DARKER colors = fireworks are several frames away from burning out.
109 * DARKEST colors = fireworks are a couple frames from burning out. */
110#ifdef HAVE_LCD_COLOR
111static const unsigned firework_colors[] = {
112LCD_RGBPACK(0,255,64), LCD_RGBPACK(61,255,249), LCD_RGBPACK(255,200,61),
113LCD_RGBPACK(217,22,217), LCD_RGBPACK(22,217,132), LCD_RGBPACK(67,95,254),
114LCD_RGBPACK(151,84,213) };
115
116static const unsigned firework_dark_colors[] = {
117LCD_RGBPACK(0,128,32), LCD_RGBPACK(30,128,128), LCD_RGBPACK(128,100,30),
118LCD_RGBPACK(109,11,109), LCD_RGBPACK(11,109,66), LCD_RGBPACK(33,47,128),
119LCD_RGBPACK(75,42,105) };
120
121static const unsigned firework_darker_colors[] = {
122LCD_RGBPACK(0,64,16), LCD_RGBPACK(15,64,64), LCD_RGBPACK(64,50,15),
123LCD_RGBPACK(55,5,55), LCD_RGBPACK(5,55,33), LCD_RGBPACK(16,24,64),
124LCD_RGBPACK(38,21,52) };
125
126static const unsigned firework_darkest_colors[] = {
127LCD_RGBPACK(0,32,8), LCD_RGBPACK(7,32,32), LCD_RGBPACK(32,25,7),
128LCD_RGBPACK(27,2,27), LCD_RGBPACK(2,27,16), LCD_RGBPACK(8,12,32),
129LCD_RGBPACK(19,10,26) };
130
131#define EXPLOSION_COLOR LCD_RGBPACK(255,240,0)
132
133#endif
134
135static const struct opt_items autofire_delay_settings[16] = {
136 { "Off", NULL },
137 { "50ms", NULL },
138 { "100ms", NULL },
139 { "200ms", NULL },
140 { "300ms", NULL },
141 { "300ms", NULL },
142 { "400ms", NULL },
143 { "500ms", NULL },
144 { "600ms", NULL },
145 { "700ms", NULL },
146 { "800ms", NULL },
147 { "900ms", NULL },
148 { "1s", NULL },
149 { "2s", NULL },
150 { "3s", NULL },
151 { "4s", NULL }
152};
153
154int autofire_delay_values[16] = {
155 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400 };
156
157static const struct opt_items particle_settings[8] = {
158 { "5", NULL },
159 { "10", NULL },
160 { "15", NULL },
161 { "20", NULL },
162 { "25", NULL },
163 { "30", NULL },
164 { "35", NULL },
165 { "40", NULL },
166};
167
168int particle_values[8] = {
169 5, 10, 15, 20, 25, 30, 35, 40 };
170
171static const struct opt_items particle_life_settings[9] = {
172 { "20 cycles", NULL },
173 { "30 cycles", NULL },
174 { "40 cycles", NULL },
175 { "50 cycles", NULL },
176 { "60 cycles", NULL },
177 { "70 cycles", NULL },
178 { "80 cycles", NULL },
179 { "90 cycles", NULL },
180 { "100 cycles", NULL }
181};
182
183int particle_life_values[9] = {
184 20, 30, 40, 50, 60, 70, 80, 90, 100 };
185
186static const struct opt_items gravity_settings[4] = {
187 { "Off", NULL },
188 { "Weak", NULL },
189 { "Moderate", NULL },
190 { "Strong", NULL },
191};
192
193int gravity_values[4] = {
194 0, 30, 20, 10 };
195
196#ifdef HAVE_LCD_COLOR
197
198static const struct opt_items rocket_settings[3] = {
199 { "No", NULL },
200 { "Yes (no trails)", NULL },
201 { "Yes (with trails)", NULL },
202};
203int rocket_values[4] = {
204 2, 1, 0 };
205
206#else
207
208static const struct opt_items rocket_settings[2] = {
209 { "No", NULL },
210 { "Yes", NULL },
211};
212int rocket_values[4] = {
213 1, 0 };
214
215#endif
216
217static const struct opt_items fps_settings[9] = {
218 { "20 FPS", NULL },
219 { "25 FPS", NULL },
220 { "30 FPS", NULL },
221 { "35 FPS", NULL },
222 { "40 FPS", NULL },
223 { "45 FPS", NULL },
224 { "50 FPS", NULL },
225 { "55 FPS", NULL },
226 { "60 FPS", NULL }
227};
228
229int fps_values[9] = {
230 20, 25, 30, 35, 40, 45, 50, 55, 60 };
231
232static const struct menu_item items[] = {
233 { "Start Demo", NULL },
234 { "Auto-Fire", NULL },
235 { "Particles Per Firework", NULL },
236 { "Particle Life", NULL },
237 { "Gravity", NULL },
238 { "Show Rockets", NULL },
239 { "FPS (Speed)", NULL },
240 { "Quit", NULL }
241};
242
243/* called on startup. initializes all variables, etc */
244void init_all(void)
245{
246 int j;
247
248 for(j=0; j<MAX_ROCKETS; j++)
249 firework_phase[j] = -1;
250}
251
252/* called when a rocket hits its destination height.
253 * prepares all associated fireworks to be expelled. */
254void init_explode(int x, int y, int firework, int points)
255{
256 int i;
257
258 for(i=0; i<points; i++)
259 {
260 rb->srand(*rb->current_tick * i);
261
262 firework_xpoints[firework][i] = x;
263 firework_ypoints[firework][i] = y;
264
265 firework_xspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
266 firework_yspeed[firework][i] = (rb->rand() % FIREWORK_MOVEMENT_RANGE) - FIREWORK_MOVEMENT_RANGE/2;
267
268#ifdef HAVE_LCD_COLOR
269 firework_color[firework][i] = rb->rand() % 7;
270#endif
271 }
272}
273
274/* called when a rocket is launched.
275 * prepares said rocket to start moving towards its destination. */
276void init_rocket(int rocket)
277{
278 rb->srand(*rb->current_tick);
279
280 rocket_xpos[rocket] = rb->rand() % LCD_WIDTH;
281 rocket_ypos[rocket] = LCD_HEIGHT;
282
283 rocket_xspeed[rocket] = (rb->rand() % ROCKET_MOVEMENT_RANGE) - ROCKET_MOVEMENT_RANGE/2;
284 rocket_yspeed[rocket] = 3;
285
286 rocket_targetphase[rocket] = (ROCKET_LIFE + (rb->rand() % ROCKET_LIFE_VAR)) / rocket_yspeed[rocket];
287}
288
289/* startup/configuration menu. */
290void fireworks_menu(void)
291{
292 int m, result;
293 bool menu_quit = false;
294
295 rb->lcd_setfont(FONT_UI);
296#ifdef HAVE_LCD_COLOR
297 rb->lcd_set_background(LCD_BLACK);
298 rb->lcd_set_foreground(LCD_WHITE);
299#endif
300 rb->lcd_clear_display();
301 rb->lcd_update();
302
303 m = rb->menu_init(items, sizeof(items) / sizeof(*items),
304 NULL, NULL, NULL, NULL);
305
306 rb->button_clear_queue();
307
308 while(!menu_quit)
309 {
310 result = rb->menu_show(m);
311
312 switch(result)
313 {
314 case 0:
315 rb->lcd_setfont(FONT_SYSFIXED);
316
317#ifdef HAVE_LCD_COLOR
318 rb->lcd_set_background(LCD_BLACK);
319 rb->lcd_set_foreground(LCD_WHITE);
320#endif
321
322 rb->lcd_clear_display();
323 rb->lcd_update();
324
325 init_all();
326 menu_quit = true;
327 break;
328
329 case 1:
330 rb->set_option("Auto-Fire", &autofire_delay, INT, autofire_delay_settings, 16, NULL);
331 break;
332
333 case 2:
334 rb->set_option("Particles Per Firework", &particles_per_firework, INT, particle_settings, 8, NULL);
335 break;
336
337 case 3:
338 rb->set_option("Particle Life", &particle_life, INT, particle_life_settings, 9, NULL);
339 break;
340
341 case 4:
342 rb->set_option("Gravity", &gravity, INT, gravity_settings, 4, NULL);
343 break;
344
345 case 5:
346 rb->set_option("Show Rockets", &show_rockets, INT, rocket_settings, 3, NULL);
347 break;
348
349 case 6:
350 rb->set_option("FPS (Speed)", &frames_per_second, INT, fps_settings, 9, NULL);
351 break;
352
353 case 7:
354 quit_plugin = true;
355 menu_quit = true;
356 break;
357 }
358 }
359
360 rb->menu_exit(m);
361}
362
363/* this is the plugin entry point */
364enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
365{
366 (void)parameter;
367
368 rb = api;
369
370 int j, i, autofire=0;
371 int thisrocket=0;
372 int start_tick, elapsed_tick;
373 int button;
374
375 /* set everything up.. no BL timeout, no backdrop,
376 white-text-on-black-background. */
377 rb->backlight_set_timeout(1);
378#if LCD_DEPTH > 1
379 rb->lcd_set_backdrop(NULL);
380 rb->lcd_set_background(LCD_BLACK);
381 rb->lcd_set_foreground(LCD_WHITE);
382#endif
383
384#ifdef HAVE_ADJUSTABLE_CPU_FREQ
385 rb->cpu_boost(true);
386#endif
387
388 fireworks_menu();
389
390 start_tick = *rb->current_tick;
391
392 while(!quit_plugin)
393 {
394 rb->lcd_clear_display();
395
396 /* loop through every possible rocket */
397 for(j=0; j<MAX_ROCKETS; j++)
398 {
399 /* if the current rocket is actually moving/"alive" then go on and
400 * move/update/explode it */
401 if(rocket_phase[j] > -1)
402 {
403#ifdef HAVE_LCD_COLOR /* draw trail, if requested */
404 if(show_rockets==2)
405 {
406 rb->lcd_set_foreground(LCD_RGBPACK(128,128,128));
407 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
408 ROCKET_SIZE, ROCKET_SIZE);
409 rb->lcd_set_foreground(LCD_RGBPACK(64,64,64));
410 rb->lcd_fillrect(rocket_xpos[j]-rocket_xspeed[j], rocket_ypos[j]+rocket_yspeed[j],
411 ROCKET_SIZE, ROCKET_SIZE);
412 }
413#endif
414
415 /* move rocket */
416 rocket_xpos[j] += rocket_xspeed[j];
417 rocket_ypos[j] -= rocket_yspeed[j];
418
419#ifdef HAVE_LCD_COLOR
420 rb->lcd_set_foreground(LCD_WHITE);
421#endif
422 if(show_rockets==2 || show_rockets==1)
423 rb->lcd_fillrect(rocket_xpos[j], rocket_ypos[j],
424 ROCKET_SIZE, ROCKET_SIZE);
425
426 /* if(rocket isn't "there" yet) keep moving
427 * if(rocket IS there) explode it. */
428 if(rocket_phase[j] < rocket_targetphase[j])
429 rocket_phase[j]++;
430 else
431 {
432 rocket_phase[j] = -1;
433
434 firework_phase[j] = 0;
435 init_explode(rocket_xpos[j], rocket_ypos[j], j, particle_values[particles_per_firework]);
436 }
437 }
438
439 /* and now onto the fireworks for this particular rocket... */
440 if(firework_phase[j] > -1)
441 {
442 for(i=0; i<particle_values[particles_per_firework]; i++)
443 {
444 firework_xpoints[j][i] += firework_xspeed[j][i];
445 firework_ypoints[j][i] += firework_yspeed[j][i];
446
447 if(gravity != 0)
448 firework_ypoints[j][i] += firework_phase[j]/gravity_values[gravity];
449
450#ifdef HAVE_LCD_COLOR
451 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
452 rb->lcd_fillrect(firework_xpoints[j][i]-1,
453 firework_ypoints[j][i]-1,
454 FIREWORK_SIZE+2, FIREWORK_SIZE+2);
455
456 if(firework_phase[j] < particle_life_values[particle_life]-10)
457 rb->lcd_set_foreground(firework_colors[firework_color[j][i]]);
458 else if(firework_phase[j] < particle_life_values[particle_life]-7)
459 rb->lcd_set_foreground(firework_dark_colors[firework_color[j][i]]);
460 else if(firework_phase[j] < particle_life_values[particle_life]-3)
461 rb->lcd_set_foreground(firework_darker_colors[firework_color[j][i]]);
462 else
463 rb->lcd_set_foreground(firework_darkest_colors[firework_color[j][i]]);
464#endif
465 rb->lcd_fillrect(firework_xpoints[j][i],
466 firework_ypoints[j][i],
467 FIREWORK_SIZE, FIREWORK_SIZE);
468/* WIP - currently ugly explosion effect
469#ifdef HAVE_LCD_COLOR
470 if(firework_phase[j] < 10)
471 {
472 rb->lcd_set_foreground(EXPLOSION_COLOR);
473 rb->lcd_fillrect(rocket_xpos[j]-firework_phase[j],
474 rocket_ypos[j]-firework_phase[j],
475 firework_phase[j]*2, firework_phase[j]*2);
476 }
477#endif */
478 }
479
480#ifdef HAVE_LCD_COLOR
481 rb->lcd_set_foreground(LCD_WHITE);
482#endif
483
484 /* firework at its destination age?
485 * no = keep aging; yes = delete it. */
486 if(firework_phase[j] < particle_life_values[particle_life])
487 firework_phase[j]++;
488 else
489 firework_phase[j] = -1;
490 }
491 }
492
493 /* is autofire on? */
494 if(autofire_delay != 0)
495 {
496 elapsed_tick = *rb->current_tick - start_tick;
497
498 if(elapsed_tick > autofire_delay_values[autofire_delay])
499 {
500 rocket_phase[autofire] = 0;
501 init_rocket(autofire);
502
503 start_tick = *rb->current_tick;
504
505 if(autofire < MAX_ROCKETS)
506 autofire++;
507 else
508 autofire = 0;
509 }
510 }
511
512 rb->lcd_update();
513
514 button = rb->button_get_w_tmo(HZ/fps_values[frames_per_second]);
515 switch(button)
516 {
517 case BTN_MENU: /* back to config menu */
518 fireworks_menu();
519 break;
520
521 case BTN_FIRE: /* fire off rockets manually */
522 case BTN_FIRE|BUTTON_REPEAT:
523 if(thisrocket < MAX_ROCKETS)
524 thisrocket++;
525 else
526 thisrocket=0;
527
528 rocket_phase[thisrocket] = 0;
529 init_rocket(thisrocket);
530 break;
531 }
532 }
533 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
534
535#ifdef HAVE_ADJUSTABLE_CPU_FREQ
536 rb->cpu_boost(true);
537#endif
538
539 return PLUGIN_OK;
540}