summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/clix.c855
-rw-r--r--manual/plugins/clix.tex29
-rw-r--r--manual/plugins/main.tex2
5 files changed, 888 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 8e4bb7d7c4..a7a8e75b61 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -12,6 +12,7 @@ chessbox,games
12chessclock,apps 12chessclock,apps
13chip8,viewers 13chip8,viewers
14chopper,games 14chopper,games
15clix,games
15clock,apps 16clock,apps
16credits,viewers 17credits,viewers
17cube,demos 18cube,demos
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 5fb62a9117..92d1ae6ef6 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -56,6 +56,7 @@ wavview.c
56robotfindskitten.c 56robotfindskitten.c
57 57
58#ifdef HAVE_LCD_COLOR 58#ifdef HAVE_LCD_COLOR
59clix.c
59ppmviewer.c 60ppmviewer.c
60#endif 61#endif
61 62
diff --git a/apps/plugins/clix.c b/apps/plugins/clix.c
new file mode 100644
index 0000000000..4768859762
--- /dev/null
+++ b/apps/plugins/clix.c
@@ -0,0 +1,855 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2008-2009 Rene Peinthor
11 * Contribution from Johannes Schwarz (new menu system, use of highscore lib)
12 *
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include "plugin.h"
21#include "lib/highscore.h"
22#include "lib/playback_control.h"
23#include "lib/display_text.h"
24
25PLUGIN_HEADER
26
27#if (CONFIG_KEYPAD == SANSA_E200_PAD)
28#define CLIX_BUTTON_QUIT BUTTON_POWER
29#define CLIX_BUTTON_UP BUTTON_UP
30#define CLIX_BUTTON_DOWN BUTTON_DOWN
31#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
32#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
33#define CLIX_BUTTON_LEFT BUTTON_LEFT
34#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
35#define CLIX_BUTTON_CLICK BUTTON_SELECT
36
37#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
38#define CLIX_BUTTON_QUIT BUTTON_POWER
39#define CLIX_BUTTON_UP BUTTON_UP
40#define CLIX_BUTTON_DOWN BUTTON_DOWN
41#define CLIX_BUTTON_LEFT BUTTON_LEFT
42#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
43#define CLIX_BUTTON_CLICK BUTTON_SELECT
44
45#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
46#define CLIX_BUTTON_QUIT BUTTON_HOME
47#define CLIX_BUTTON_UP BUTTON_UP
48#define CLIX_BUTTON_DOWN BUTTON_DOWN
49#define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
50#define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
51#define CLIX_BUTTON_LEFT BUTTON_LEFT
52#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
53#define CLIX_BUTTON_CLICK BUTTON_SELECT
54
55#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
56#define CLIX_BUTTON_QUIT BUTTON_POWER
57#define CLIX_BUTTON_UP BUTTON_UP
58#define CLIX_BUTTON_DOWN BUTTON_DOWN
59#define CLIX_BUTTON_SCROLL_FWD BUTTON_VOL_UP
60#define CLIX_BUTTON_SCROLL_BACK BUTTON_VOL_DOWN
61#define CLIX_BUTTON_LEFT BUTTON_LEFT
62#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
63#define CLIX_BUTTON_CLICK BUTTON_SELECT
64
65#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
66 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
67 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
68#define CLIX_BUTTON_QUIT BUTTON_MENU
69#define CLIX_BUTTON_UP BUTTON_SCROLL_BACK
70#define CLIX_BUTTON_DOWN BUTTON_SCROLL_FWD
71#define CLIX_BUTTON_CLICK BUTTON_SELECT
72#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
73#define CLIX_BUTTON_LEFT BUTTON_LEFT
74
75#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
76#define CLIX_BUTTON_QUIT BUTTON_POWER
77#define CLIX_BUTTON_LEFT BUTTON_LEFT
78#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
79#define CLIX_BUTTON_CLICK BUTTON_SELECT
80#define CLIX_BUTTON_UP BUTTON_UP
81#define CLIX_BUTTON_DOWN BUTTON_DOWN
82
83#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
84#define CLIX_BUTTON_QUIT BUTTON_BACK
85#define CLIX_BUTTON_LEFT BUTTON_LEFT
86#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
87#define CLIX_BUTTON_CLICK BUTTON_SELECT
88#define CLIX_BUTTON_UP BUTTON_UP
89#define CLIX_BUTTON_DOWN BUTTON_DOWN
90
91#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
92#define CLIX_BUTTON_QUIT BUTTON_POWER
93#define CLIX_BUTTON_LEFT BUTTON_LEFT
94#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
95#define CLIX_BUTTON_CLICK BUTTON_PLAY
96#define CLIX_BUTTON_UP BUTTON_SCROLL_UP
97#define CLIX_BUTTON_DOWN BUTTON_SCROLL_DOWN
98
99#elif CONFIG_KEYPAD == IAUDIO67_PAD
100#define CLIX_BUTTON_QUIT BUTTON_POWER
101#define CLIX_BUTTON_LEFT BUTTON_LEFT
102#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
103#define CLIX_BUTTON_CLICK BUTTON_PLAY
104#define CLIX_BUTTON_UP BUTTON_STOP
105#define CLIX_BUTTON_DOWN BUTTON_PLAY
106
107#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
108#define CLIX_BUTTON_QUIT BUTTON_POWER
109#define CLIX_BUTTON_LEFT BUTTON_LEFT
110#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
111#define CLIX_BUTTON_CLICK BUTTON_SELECT
112#define CLIX_BUTTON_UP BUTTON_UP
113#define CLIX_BUTTON_DOWN BUTTON_DOWN
114
115#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
116#define CLIX_BUTTON_QUIT BUTTON_BACK
117#define CLIX_BUTTON_LEFT BUTTON_LEFT
118#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
119#define CLIX_BUTTON_CLICK BUTTON_SELECT
120#define CLIX_BUTTON_UP BUTTON_UP
121#define CLIX_BUTTON_DOWN BUTTON_DOWN
122
123#elif (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD)
124#define CLIX_BUTTON_QUIT BUTTON_POWER
125#define CLIX_BUTTON_LEFT BUTTON_LEFT
126#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
127#define CLIX_BUTTON_CLICK BUTTON_SELECT
128#define CLIX_BUTTON_UP BUTTON_UP
129#define CLIX_BUTTON_DOWN BUTTON_DOWN
130
131#elif CONFIG_KEYPAD == COWOND2_PAD
132#define CLIX_BUTTON_QUIT BUTTON_POWER
133
134#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
135 (CONFIG_KEYPAD == MROBE500_PAD)
136#define CLIX_BUTTON_QUIT BUTTON_POWER
137
138#else
139#error "no keymap"
140#endif
141
142#ifdef HAVE_TOUCHSCREEN
143#ifndef CLIX_BUTTON_LEFT
144#define CLIX_BUTTON_LEFT BUTTON_MIDLEFT
145#endif
146#ifndef CLIX_BUTTON_RIGHT
147#define CLIX_BUTTON_RIGHT BUTTON_MIDRIGHT
148#endif
149#ifndef CLIX_BUTTON_CLICK
150#define CLIX_BUTTON_CLICK BUTTON_CENTER
151#endif
152#ifndef CLIX_BUTTON_UP
153#define CLIX_BUTTON_UP BUTTON_TOPMIDDLE
154#endif
155#ifndef CLIX_BUTTON_DOWN
156#define CLIX_BUTTON_DOWN BUTTON_BOTTOMMIDDLE
157#endif
158#endif
159
160#define HIGHSCORE_FILE PLUGIN_GAMES_DIR "/clix.score"
161#define NUM_SCORES 5
162struct highscore highest[NUM_SCORES];
163
164#define NUM_LEVELS 9
165#define BLINK_TICKCOUNT 25
166#define MARGIN 5
167
168#if (LCD_WIDTH >= LCD_HEIGHT)
169#define BOARD_WIDTH 18
170#define BOARD_HEIGHT 12
171#else
172#define BOARD_WIDTH 12
173#define BOARD_HEIGHT 18
174#endif
175
176#if (LCD_WIDTH >= 306 && LCD_HEIGHT>= 204)
177#define CELL_SIZE 16
178
179#elif (LCD_WIDTH >= 270 && LCD_HEIGHT>= 180)
180#define CELL_SIZE 14
181
182#elif (LCD_WIDTH >= 234 && LCD_HEIGHT>= 156)
183#define CELL_SIZE 12
184
185#elif (LCD_WIDTH >= 198 && LCD_HEIGHT>= 132)
186#define CELL_SIZE 10
187
188#elif (LCD_WIDTH >= 162 && LCD_HEIGHT>= 108)
189#define CELL_SIZE 8
190
191#elif (LCD_WIDTH >= 126 && LCD_HEIGHT>= 84)
192#define CELL_SIZE 6
193
194#elif (LCD_WIDTH >= 60)
195#define CELL_SIZE 4
196#endif
197
198#define XYPOS(x,y) ((y) * BOARD_WIDTH + x)
199#define XOFS LCD_WIDTH/2-(BOARD_WIDTH * (CELL_SIZE + 1)/2)
200#define YOFS (LCD_HEIGHT+10)/2-(BOARD_HEIGHT * (CELL_SIZE + 1)/2)
201
202
203struct clix_game_state_t {
204 unsigned char level; /* current level */
205 char x,y; /* current positions of the cursor */
206 char board[BOARD_WIDTH * BOARD_HEIGHT]; /* play board*/
207 /* state of selected fields,maybe we can store this in the play board too */
208 bool board_selected[ BOARD_WIDTH * BOARD_HEIGHT];
209 char selected_count;
210 unsigned short score; /* current game score */
211 char status;
212 bool blink; /* true if selected CELLS are currently white */
213};
214
215/* game state enum */
216enum {
217 CLIX_GAMEOVER = -1,
218 CLIX_CONTINUE,
219 CLIX_CLEARED
220};
221
222/* cell color enum */
223enum {
224 CC_BLACK = -1,
225 CC_BLUE,
226 CC_GREEN,
227 CC_RED,
228 CC_YELLOW,
229 CC_ORANGE,
230 CC_CYAN,
231 CC_BROWN,
232 CC_PINK,
233 CC_DARK_BLUE,
234 CC_DARK_GREEN
235};
236
237/* display the highscore list and highlight the last one */
238static void clix_show_highscores(int position)
239{
240 int i, w, h;
241 char str[30];
242
243#ifdef HAVE_LCD_COLOR
244 rb->lcd_set_background(LCD_BLACK);
245 rb->lcd_set_foreground(LCD_WHITE);
246#endif
247 rb->button_clear_queue();
248 rb->lcd_clear_display();
249
250 rb->lcd_setfont(FONT_UI);
251 rb->lcd_getstringsize("High Scores", &w, &h);
252 /* check wether it fits on screen */
253 if ((4*h + h*(NUM_SCORES-1) + MARGIN) > LCD_HEIGHT) {
254 rb->lcd_setfont(FONT_SYSFIXED);
255 rb->lcd_getstringsize("High Scores", &w, &h);
256 }
257 rb->lcd_putsxy(LCD_WIDTH/2-w/2, MARGIN, "High Scores");
258 rb->lcd_putsxy(LCD_WIDTH/4-w/4,2*h, "Score");
259 rb->lcd_putsxy(LCD_WIDTH*3/4-w/4,2*h, "Level");
260
261 for (i = 0; i<NUM_SCORES; i++)
262 {
263#ifdef HAVE_LCD_COLOR
264 if (i == position) {
265 rb->lcd_set_foreground(LCD_RGBPACK(245,0,0));
266 }
267#endif
268 rb->snprintf (str, sizeof (str), "%d)", i+1);
269 rb->lcd_putsxy (MARGIN,3*h + h*i, str);
270 rb->snprintf (str, sizeof (str), "%d", highest[i].score);
271 rb->lcd_putsxy (LCD_WIDTH/4-w/4,3*h + h*i, str);
272 rb->snprintf (str, sizeof (str), "%d", highest[i].level);
273 rb->lcd_putsxy (LCD_WIDTH*3/4-w/4,3*h + h*i, str);
274 if(i == position) {
275#ifdef HAVE_LCD_COLOR
276 rb->lcd_set_foreground(LCD_WHITE);
277#else
278 rb->lcd_hline(MARGIN, LCD_WIDTH-MARGIN, 3*h + h*(i+1));
279#endif
280 }
281 }
282 rb->lcd_update();
283 rb->button_get(true);
284 rb->lcd_setfont(FONT_SYSFIXED);
285}
286
287/* recursiv function to check if a neighbour cell is of the same color
288 if so call the function with the neighbours position
289*/
290static void clix_set_selected(struct clix_game_state_t* state,
291 const int x, const int y)
292{
293 state->selected_count++;
294 state->board_selected[ XYPOS( x, y)] = true;
295 int current_color = state->board[ XYPOS( x, y)];
296
297 if( (x - 1) >= 0 &&
298 state->board[ XYPOS( x - 1, y)] == current_color &&
299 state->board_selected[ XYPOS(x - 1, y)] == false)
300 clix_set_selected( state, x - 1, y);
301
302 if( (y + 1) < BOARD_HEIGHT &&
303 state->board[ XYPOS( x, y + 1)] == current_color &&
304 state->board_selected[ XYPOS(x, y + 1)] == false)
305 clix_set_selected( state, x, y + 1);
306
307 if( (x + 1) < BOARD_WIDTH &&
308 state->board[ XYPOS( x + 1, y)] == current_color &&
309 state->board_selected[ XYPOS(x + 1, y)] == false)
310 clix_set_selected( state, x + 1, y);
311
312 if( (y - 1) >= 0 &&
313 state->board[ XYPOS( x, y - 1)] == current_color &&
314 state->board_selected[ XYPOS(x, y - 1)] == false)
315 clix_set_selected( state, x, y - 1);
316}
317
318/* updates "blinking" cells by finding out which one is a valid neighbours */
319static void clix_update_selected(struct clix_game_state_t* state)
320{
321 int i;
322
323 for( i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; ++i)
324 {
325 state->board_selected[i] = false;
326 }
327 state->selected_count = 0;
328
329 /* recursion starts here */
330 clix_set_selected( state, state->x, state->y);
331}
332
333/* inits the board with new random colors according to the level */
334static void clix_init_new_level(struct clix_game_state_t* state)
335{
336 int i;
337 int r;
338
339 state->y = BOARD_HEIGHT / 2;
340 state->x = BOARD_WIDTH / 2;
341
342 rb->srand( *rb->current_tick);
343 /* create a random colored board, according to the current level */
344 for(i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i)
345 {
346 r = rb->rand() % (state->level + 1);
347 state->board[i] = r;
348 }
349}
350
351/* this inits the game state structure */
352static void clix_init(struct clix_game_state_t* state)
353{
354 state->level = 1;
355 state->score = 0;
356 state->blink = false;
357 state->status = CLIX_CONTINUE;
358
359 clix_init_new_level(state);
360
361 clix_update_selected(state);
362}
363
364/* Function for drawing a cell */
365static void clix_draw_cell(struct clix_game_state_t* state, const int x, const int y)
366{
367 int realx = XOFS;
368 int realy = YOFS;
369
370 realx += x * (CELL_SIZE + 1);
371 realy += y * (CELL_SIZE + 1);
372
373 if (state->blink && state->board_selected[ XYPOS( x, y)]) {
374 rb->lcd_set_foreground(LCD_WHITE);
375 } else {
376 switch (state->board[ XYPOS( x, y)])
377 {
378 case CC_BLUE:
379 rb->lcd_set_foreground( LCD_RGBPACK( 25, 25, 255));
380 break;
381 case CC_GREEN:
382 rb->lcd_set_foreground( LCD_RGBPACK( 25, 255, 25));
383 break;
384 case CC_RED:
385 rb->lcd_set_foreground( LCD_RGBPACK( 255, 25, 25));
386 break;
387 case CC_YELLOW:
388 rb->lcd_set_foreground( LCD_RGBPACK( 225, 225, 25));
389 break;
390 case CC_ORANGE:
391 rb->lcd_set_foreground( LCD_RGBPACK( 230, 140, 15));
392 break;
393 case CC_CYAN:
394 rb->lcd_set_foreground( LCD_RGBPACK( 25, 245, 230));
395 break;
396 case CC_BROWN:
397 rb->lcd_set_foreground( LCD_RGBPACK(139, 69, 19));
398 break;
399 case CC_PINK:
400 rb->lcd_set_foreground( LCD_RGBPACK(255, 105, 180));
401 break;
402 case CC_DARK_GREEN:
403 rb->lcd_set_foreground( LCD_RGBPACK( 0, 100, 0));
404 break;
405 case CC_DARK_BLUE:
406 rb->lcd_set_foreground( LCD_RGBPACK( 280, 32, 144));
407 break;
408 default:
409 rb->lcd_set_foreground( LCD_BLACK);
410 break;
411 }
412 }
413
414 rb->lcd_fillrect( realx, realy, CELL_SIZE, CELL_SIZE);
415
416 /* draw cursor */
417 if ( x == state->x && y == state->y) {
418 rb->lcd_set_foreground( LCD_WHITE);
419 rb->lcd_drawrect( realx - 1, realy - 1, CELL_SIZE + 2, CELL_SIZE + 2);
420 }
421}
422
423/* main function of drawing the whole board and score... */
424static void clix_draw(struct clix_game_state_t* state)
425{
426 int i,j;
427 char str[30];
428
429 /* Clear screen */
430 rb->lcd_clear_display();
431 rb->lcd_set_foreground( LCD_WHITE);
432
433 rb->lcd_putsxy( MARGIN, MARGIN, "Score:");
434 rb->snprintf( str, sizeof(str), "%d", state->score);
435 rb->lcd_putsxy( 43, MARGIN, str);
436#if LCD_WIDTH <= 100
437 rb->lcd_putsxy( 75, MARGIN, "L:");
438 rb->snprintf( str, sizeof(str), "%d", state->level);
439 rb->lcd_putsxy( 90, MARGIN, str);
440#else
441 rb->lcd_putsxy( 75, MARGIN, "Level:");
442 rb->snprintf( str, sizeof(str), "%d", state->level);
443 rb->lcd_putsxy( 113, MARGIN, str);
444#endif
445 for( i = 0; i < BOARD_WIDTH; ++i)
446 {
447 for( j = 0; j < BOARD_HEIGHT; ++j)
448 {
449 clix_draw_cell( state, i, j);
450 }
451 }
452
453 rb->lcd_update();
454}
455
456static void clix_move_cursor(struct clix_game_state_t* state, const bool left)
457{
458 signed char x, y;
459
460 x = state->x;
461 do
462 {
463 y = state->y;
464 while(state->board[ XYPOS( x, y)] == CC_BLACK && y < BOARD_HEIGHT) y++;
465 if (y < BOARD_HEIGHT) {
466 state->y = y;
467 state->x = x;
468 }
469 else
470 {
471 if (left) {
472 if( x >= 0)
473 x--;
474 else
475 y = state->y;
476 }
477 else
478 {
479 if( x < BOARD_WIDTH - 1)
480 x++;
481 else
482 x = 0;
483 }
484 }
485 } while ( y != state->y);
486
487}
488
489/* returns the color of the given position, if out of bounds return CC_BLACK */
490static int clix_get_color(struct clix_game_state_t* state, const int x, const int y)
491{
492 if( x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT)
493 return state->board[XYPOS( x, y)];
494 else
495 return CC_BLACK;
496}
497
498static int clix_clear_selected(struct clix_game_state_t* state)
499{
500 int i, j, x, y;
501
502 state->status = CLIX_CLEARED;
503
504 /* clear the selected blocks */
505 for( i = 0; i < BOARD_WIDTH; ++i)
506 {
507 for( j = 0; j < BOARD_HEIGHT; ++j)
508 {
509 if( state->board_selected[ XYPOS( i, j)] )
510 {
511 state->board_selected[ XYPOS( i, j)] = false;
512 state->board[ XYPOS( i, j)] = CC_BLACK;
513 }
514 }
515 }
516
517 /* let blocks falling down */
518 for( i = BOARD_WIDTH - 1; i >= 0; --i)
519 {
520 for( j = BOARD_HEIGHT - 1; j >= 0; --j)
521 {
522 y = j;
523 while( state->board[ XYPOS( i, y + 1)] == CC_BLACK &&
524 y < BOARD_HEIGHT
525 )
526 y++;
527
528 if (y != j) {
529 state->board[ XYPOS(i, y)] = state->board[ XYPOS( i, j)];
530 state->board[ XYPOS( i, j)] = CC_BLACK;
531 }
532 }
533 }
534
535 /* count score */
536 state->score += state->selected_count * state->level;
537
538 /* check every column (from right to left) if its empty,
539 if so copy the contents from the right side */
540 for( i = BOARD_WIDTH - 1; i >= 0; --i)
541 {
542 if (state->board[ XYPOS( i, BOARD_HEIGHT - 1)] == CC_BLACK) {
543 if( (i + 1) < BOARD_WIDTH &&
544 state->board[ XYPOS( i + 1, BOARD_HEIGHT - 1)] != CC_BLACK)
545 {
546 for( x = (i + 1); x < BOARD_WIDTH; ++x)
547 {
548 for( j = 0; j < BOARD_HEIGHT; ++j)
549 {
550 state->board[ XYPOS( x - 1, j)] =
551 state->board[ XYPOS( x, j)];
552
553 state->board[ XYPOS( x, j)] = CC_BLACK;
554 }
555 }
556 }
557 }
558 else
559 state->status = CLIX_CONTINUE;
560 }
561
562 if (state->status != CLIX_CLEARED) {
563 /* check if a move is still possible, otherwise the game is over.
564 tart from the left bottom, because there are the last fields
565 at the end of the game.
566 */
567 for( i = 0; i < BOARD_WIDTH; ++i)
568 {
569 for( j = BOARD_HEIGHT - 1; j >= 0; --j)
570 {
571 if (state->board[ XYPOS( i, j)] != CC_BLACK) {
572 if ( state->board[ XYPOS( i, j)] ==
573 clix_get_color( state, i - 1, j) ||
574 state->board[ XYPOS( i, j)] ==
575 clix_get_color( state, i + 1, j) ||
576 state->board[ XYPOS( i, j)] ==
577 clix_get_color( state, i, j - 1) ||
578 state->board[ XYPOS( i, j)] ==
579 clix_get_color( state, i, j + 1)
580 )
581 {
582 /* and the loop, but in a diffrent way than usually*/
583 i = BOARD_WIDTH + 1;
584 j = -2;
585 }
586 }
587 }
588 }
589 /* if the loops ended without a possible move, the game is over */
590 if( i == BOARD_WIDTH && j == -1)
591 state->status = CLIX_GAMEOVER;
592
593 /* set cursor to the right position */
594 if (state->status == CLIX_CONTINUE) {
595 clix_move_cursor( state, true);
596 clix_update_selected( state);
597 }
598 }
599
600 return state->status;
601}
602
603static int clix_help(void)
604{
605 rb->lcd_setfont(FONT_UI);
606 rb->lcd_set_foreground(LCD_WHITE);
607#define WORDS (sizeof help_text / sizeof (char*))
608 char *help_text[] = {
609 "Clix", "", "Aim", "", "Remove", "all", "blocks", "from", "the",
610 "board", "to", "achieve", "the", "next", "level.", "You", "can",
611 "only", "remove", "blocks,", "if", "at", "least", "two", "blocks",
612 "with", "the", "same", "color", "have", "a", "direct", "connection.",
613 "The", "more", "blocks", "you", "remove", "per", "turn,", "the",
614 "more", "points", "you", "get."
615 };
616 static struct style_text formation[]={
617 { 0, TEXT_CENTER|TEXT_UNDERLINE },
618 { 2, C_RED }
619 };
620
621 if (display_text(WORDS, help_text, formation, NULL)==PLUGIN_USB_CONNECTED)
622 return PLUGIN_USB_CONNECTED;
623 int button;
624 do {
625 button = rb->button_get(true);
626 if (button == SYS_USB_CONNECTED) {
627 return PLUGIN_USB_CONNECTED;
628 }
629 } while( ( button == BUTTON_NONE )
630 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
631 rb->lcd_setfont(FONT_SYSFIXED);
632 return 0;
633}
634
635static bool _ingame;
636static int clix_menu_cb(int action, const struct menu_item_ex *this_item)
637{
638 if(action == ACTION_REQUEST_MENUITEM
639 && !_ingame && ((intptr_t)this_item)==0)
640 return ACTION_EXIT_MENUITEM;
641 return action;
642}
643
644static int clix_menu(struct clix_game_state_t* state, bool ingame)
645{
646 rb->button_clear_queue();
647 int choice = 0;
648
649 _ingame = ingame;
650
651 MENUITEM_STRINGLIST (main_menu, "Clix Menu", clix_menu_cb,
652 "Resume Game",
653 "Start New Game",
654 "Help",
655 "High Score",
656 "Playback Control",
657 "Quit");
658
659 while (true) {
660 choice = rb->do_menu(&main_menu, &choice, NULL, false);
661 switch (choice) {
662 case 0:
663 return 0;
664 case 1:
665 clix_init(state);
666 return 0;
667 case 2:
668 if (clix_help()==PLUGIN_USB_CONNECTED)
669 return 1;
670 break;
671 case 3:
672 clix_show_highscores(NUM_SCORES);
673 break;
674 case 4:
675 playback_control(NULL);
676 break;
677 case 5:
678 case MENU_ATTACHED_USB:
679 return 1;
680 default:
681 break;
682 }
683 }
684}
685
686static int clix_handle_game(struct clix_game_state_t* state)
687{
688 if (clix_menu(state, 0))
689 return 1;
690
691 int button;
692 int blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
693 int position;
694
695 int time;
696 int start;
697 int end;
698 int oldx, oldy;
699
700 while(true)
701 {
702 if (blink_tick < *rb->current_tick) {
703 state->blink = state->blink ? false : true;
704 blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
705 }
706
707 time = 6; /* number of ticks this function will loop reading keys */
708 start = *rb->current_tick;
709 end = start + time;
710 while(end > *rb->current_tick)
711 {
712 oldx = state->x;
713 oldy = state->y;
714
715 rb->button_get_w_tmo(end - *rb->current_tick);
716 button = rb->button_status();
717 rb->button_clear_queue();
718 switch( button)
719 {
720#ifdef CLIX_BUTTON_SCROLL_BACK
721 case CLIX_BUTTON_SCROLL_BACK:
722#endif
723 case CLIX_BUTTON_UP:
724 if( state->y == 0 ||
725 state->board[ XYPOS( state->x, state->y - 1)] ==
726 CC_BLACK
727 )
728 state->y = BOARD_HEIGHT - 1;
729 else
730 state->y--;
731
732 clix_move_cursor(state, true);
733 break;
734 case CLIX_BUTTON_RIGHT:
735 if( state->x == (BOARD_WIDTH - 1))
736 state->x = 0;
737 else
738 state->x++;
739
740 clix_move_cursor(state, false);
741 break;
742#ifdef CLIX_BUTTON_SCROLL_FWD
743 case CLIX_BUTTON_SCROLL_FWD:
744#endif
745 case CLIX_BUTTON_DOWN:
746 if( state->y == (BOARD_HEIGHT - 1))
747 state->y = 0;
748 else
749 state->y++;
750
751 clix_move_cursor( state, true);
752 break;
753 case CLIX_BUTTON_LEFT:
754 if( state->x == 0)
755 state->x = BOARD_WIDTH - 1;
756 else
757 state->x--;
758
759 clix_move_cursor(state, true);
760
761 break;
762 case CLIX_BUTTON_CLICK:
763 {
764 if (state->selected_count > 1) {
765 switch( clix_clear_selected( state))
766 {
767 case CLIX_CLEARED:
768 clix_draw( state);
769 if (state->level < NUM_LEVELS) {
770 rb->splash(HZ*2, "Great! Next Level!");
771 state->score += state->level * 100;
772 state->level++;
773 clix_init_new_level( state);
774 clix_update_selected( state);
775 }
776 else {
777 rb->splash(HZ*2, "Congratulation!!!");
778 rb->lcd_clear_display();
779 rb->splash(HZ*2, "You have finished the game.");
780 if (clix_menu(state, 0))
781 return 1;
782 }
783 break;
784 case CLIX_GAMEOVER:
785 clix_draw( state);
786 rb->splash(HZ*2, "Game Over!");
787 rb->lcd_clear_display();
788 if (highscore_would_update(state->score,
789 highest, NUM_SCORES)) {
790 position=highscore_update(state->score,
791 state->level, "",
792 highest,NUM_SCORES);
793 if (position == 0) {
794 rb->splash(HZ*2, "New High Score");
795 }
796 clix_show_highscores(position);
797 }
798 if (clix_menu(state, 0))
799 return 1;
800 break;
801 default:
802 rb->sleep(10); /* prevent repeating clicks */
803 break;
804 }
805 }
806 }
807 break;
808 case CLIX_BUTTON_QUIT:
809 if (clix_menu(state, 1) != 0) {
810 rb->button_clear_queue();
811 return 1;
812 }
813 break;
814 default:
815
816 break;
817 }
818
819 if( (oldx != state->x || oldy != state->y) &&
820 state->board_selected[ XYPOS( oldx, oldy)] !=
821 state->board_selected[ XYPOS( state->x, state->y)]
822 )
823 {
824 clix_update_selected(state);
825 }
826 clix_draw(state);
827 rb->sleep(time);
828 }
829 }
830}
831
832/* this is the plugin entry point */
833enum plugin_status plugin_start(const void* parameter)
834{
835 (void)parameter;
836
837#ifdef HAVE_LCD_COLOR
838 rb->lcd_set_backdrop(NULL);
839 rb->lcd_set_foreground(LCD_WHITE);
840 rb->lcd_set_background(LCD_BLACK);
841 rb->lcd_setfont(FONT_SYSFIXED);
842
843 highscore_load(HIGHSCORE_FILE, highest, NUM_SCORES);
844
845 struct clix_game_state_t state;
846 clix_handle_game( &state);
847
848 highscore_save(HIGHSCORE_FILE, highest, NUM_SCORES);
849
850 rb->lcd_set_foreground(LCD_WHITE);
851 rb->lcd_setfont(FONT_UI);
852#endif
853
854 return PLUGIN_OK;
855}
diff --git a/manual/plugins/clix.tex b/manual/plugins/clix.tex
new file mode 100644
index 0000000000..33b535a9b7
--- /dev/null
+++ b/manual/plugins/clix.tex
@@ -0,0 +1,29 @@
1\subsection{Clix}
2\screenshot{plugins/images/ss-clix}{Clix}{img:clix}
3
4The aim is to remove all blocks from the board. You can only
5remove blocks, if at least two blocks with the same color have a direct connection.
6The more blocks you remove per turn, the more points you get.
7
8\begin{table}
9 \begin{btnmap}{}{}
10 \ButtonLeft/\ButtonRight/\\
11 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD,SANSA_E200_PAD%
12 ,SANSA_C200_PAD,GIGABEAT_PAD,MROBE100_PAD,IAUDIO_X5_PAD,GIGABEAT_S_PAD}
13 {\ButtonUp/\ButtonDown}
14 \opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu/\ButtonPlay}
15 \opt{IRIVER_H10_PAD}{\ButtonScrollUp/\ButtonScrollDown}
16 & Move the cursor around the blocks \\
17 \opt{RECORDER_PAD,IRIVER_H10_PAD}{\ButtonPlay}
18 \opt{ONDIO_PAD}{\ButtonMenu}
19 \opt{IRIVER_H100_PAD,IRIVER_H300_PAD,IPOD_4G_PAD,IPOD_3G_PAD,SANSA_E200_PAD%
20 ,SANSA_C200_PAD,GIGABEAT_PAD,MROBE100_PAD,IAUDIO_X5_PAD,GIGABEAT_S_PAD}
21 {\ButtonSelect}
22 & Remove a block \\
23 \opt{RECORDER_PAD,ONDIO_PAD,IRIVER_H100_PAD,IRIVER_H300_PAD}
24 {\ButtonOff & Exit \\}%
25 \opt{IRIVER_H10_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD,MROBE100_PAD%
26 ,IAUDIO_X5_PAD}{\ButtonPower & Exit \\}%
27 \opt{GIGABEAT_S_PAD}{\ButtonBack & Exit \\}%
28 \end{btnmap}
29\end{table}
diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex
index 70f5cbd527..f500d8084e 100644
--- a/manual/plugins/main.tex
+++ b/manual/plugins/main.tex
@@ -27,6 +27,8 @@ text files%
27 27
28\opt{lcd_bitmap}{\input{plugins/chessbox.tex}} 28\opt{lcd_bitmap}{\input{plugins/chessbox.tex}}
29 29
30\opt{lcd_bitmap}{\input{plugins/clix.tex}}
31
30\opt{lcd_bitmap}{\input{plugins/chopper.tex}} 32\opt{lcd_bitmap}{\input{plugins/chopper.tex}}
31 33
32{\input{plugins/dice.tex}} 34{\input{plugins/dice.tex}}