Prepare new maemo release
[maemo-rb.git] / apps / plugins / clix.c
blob14b0b09cdae7ad39903178e046209060aba144b3
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008-2009 Rene Peinthor
11 * Contribution from Johannes Schwarz (new menu system, use of highscore lib)
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.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "plugin.h"
21 #include "lib/highscore.h"
22 #include "lib/playback_control.h"
23 #include "lib/display_text.h"
27 #if (CONFIG_KEYPAD == SANSA_E200_PAD) || \
28 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
29 #define CLIX_BUTTON_QUIT BUTTON_POWER
30 #define CLIX_BUTTON_UP BUTTON_UP
31 #define CLIX_BUTTON_DOWN BUTTON_DOWN
32 #define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
33 #define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
34 #define CLIX_BUTTON_LEFT BUTTON_LEFT
35 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
36 #define CLIX_BUTTON_CLICK BUTTON_SELECT
38 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
39 #define CLIX_BUTTON_QUIT BUTTON_POWER
40 #define CLIX_BUTTON_UP BUTTON_UP
41 #define CLIX_BUTTON_DOWN BUTTON_DOWN
42 #define CLIX_BUTTON_LEFT BUTTON_LEFT
43 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
44 #define CLIX_BUTTON_CLICK BUTTON_SELECT
46 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
47 #define CLIX_BUTTON_QUIT BUTTON_HOME
48 #define CLIX_BUTTON_UP BUTTON_UP
49 #define CLIX_BUTTON_DOWN BUTTON_DOWN
50 #define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
51 #define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
52 #define CLIX_BUTTON_LEFT BUTTON_LEFT
53 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
54 #define CLIX_BUTTON_CLICK BUTTON_SELECT
56 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
57 #define CLIX_BUTTON_QUIT BUTTON_POWER
58 #define CLIX_BUTTON_UP BUTTON_UP
59 #define CLIX_BUTTON_DOWN BUTTON_DOWN
60 #define CLIX_BUTTON_SCROLL_FWD BUTTON_VOL_UP
61 #define CLIX_BUTTON_SCROLL_BACK BUTTON_VOL_DOWN
62 #define CLIX_BUTTON_LEFT BUTTON_LEFT
63 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
64 #define CLIX_BUTTON_CLICK BUTTON_SELECT
66 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
67 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
68 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
69 #define CLIX_BUTTON_QUIT (BUTTON_SELECT | BUTTON_MENU)
70 #define CLIX_BUTTON_UP BUTTON_MENU
71 #define CLIX_BUTTON_DOWN BUTTON_PLAY
72 #define CLIX_BUTTON_SCROLL_FWD BUTTON_SCROLL_FWD
73 #define CLIX_BUTTON_SCROLL_BACK BUTTON_SCROLL_BACK
74 #define CLIX_BUTTON_CLICK BUTTON_SELECT
75 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
76 #define CLIX_BUTTON_LEFT BUTTON_LEFT
78 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
79 #define CLIX_BUTTON_QUIT BUTTON_POWER
80 #define CLIX_BUTTON_LEFT BUTTON_LEFT
81 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
82 #define CLIX_BUTTON_CLICK BUTTON_SELECT
83 #define CLIX_BUTTON_UP BUTTON_UP
84 #define CLIX_BUTTON_DOWN BUTTON_DOWN
86 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
87 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
88 #define CLIX_BUTTON_QUIT BUTTON_BACK
89 #define CLIX_BUTTON_LEFT BUTTON_LEFT
90 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
91 #define CLIX_BUTTON_CLICK BUTTON_SELECT
92 #define CLIX_BUTTON_UP BUTTON_UP
93 #define CLIX_BUTTON_DOWN BUTTON_DOWN
95 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
96 #define CLIX_BUTTON_QUIT BUTTON_POWER
97 #define CLIX_BUTTON_LEFT BUTTON_LEFT
98 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
99 #define CLIX_BUTTON_CLICK BUTTON_PLAY
100 #define CLIX_BUTTON_UP BUTTON_SCROLL_UP
101 #define CLIX_BUTTON_DOWN BUTTON_SCROLL_DOWN
103 #elif CONFIG_KEYPAD == IAUDIO67_PAD
104 #define CLIX_BUTTON_QUIT BUTTON_POWER
105 #define CLIX_BUTTON_LEFT BUTTON_LEFT
106 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
107 #define CLIX_BUTTON_CLICK BUTTON_PLAY
108 #define CLIX_BUTTON_UP BUTTON_STOP
109 #define CLIX_BUTTON_DOWN BUTTON_PLAY
111 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
112 #define CLIX_BUTTON_QUIT BUTTON_POWER
113 #define CLIX_BUTTON_LEFT BUTTON_LEFT
114 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
115 #define CLIX_BUTTON_CLICK BUTTON_SELECT
116 #define CLIX_BUTTON_UP BUTTON_UP
117 #define CLIX_BUTTON_DOWN BUTTON_DOWN
119 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD)
120 #define CLIX_BUTTON_QUIT BUTTON_OFF
121 #define CLIX_BUTTON_LEFT BUTTON_LEFT
122 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
123 #define CLIX_BUTTON_CLICK BUTTON_SELECT
124 #define CLIX_BUTTON_UP BUTTON_UP
125 #define CLIX_BUTTON_DOWN BUTTON_DOWN
127 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
128 #define CLIX_BUTTON_QUIT BUTTON_BACK
129 #define CLIX_BUTTON_LEFT BUTTON_LEFT
130 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
131 #define CLIX_BUTTON_CLICK BUTTON_SELECT
132 #define CLIX_BUTTON_UP BUTTON_UP
133 #define CLIX_BUTTON_DOWN BUTTON_DOWN
135 #elif (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD)
136 #define CLIX_BUTTON_QUIT BUTTON_POWER
137 #define CLIX_BUTTON_LEFT BUTTON_LEFT
138 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
139 #define CLIX_BUTTON_CLICK BUTTON_SELECT
140 #define CLIX_BUTTON_UP BUTTON_UP
141 #define CLIX_BUTTON_DOWN BUTTON_DOWN
143 #elif (CONFIG_KEYPAD == PHILIPS_HDD6330_PAD)
144 #define CLIX_BUTTON_QUIT BUTTON_POWER
145 #define CLIX_BUTTON_LEFT BUTTON_PREV
146 #define CLIX_BUTTON_RIGHT BUTTON_NEXT
147 #define CLIX_BUTTON_CLICK BUTTON_PLAY
148 #define CLIX_BUTTON_UP BUTTON_UP
149 #define CLIX_BUTTON_DOWN BUTTON_DOWN
151 #elif (CONFIG_KEYPAD == PHILIPS_SA9200_PAD)
152 #define CLIX_BUTTON_QUIT BUTTON_POWER
153 #define CLIX_BUTTON_LEFT BUTTON_PREV
154 #define CLIX_BUTTON_RIGHT BUTTON_NEXT
155 #define CLIX_BUTTON_CLICK BUTTON_PLAY
156 #define CLIX_BUTTON_UP BUTTON_UP
157 #define CLIX_BUTTON_DOWN BUTTON_DOWN
159 #elif CONFIG_KEYPAD == COWON_D2_PAD
160 #define CLIX_BUTTON_QUIT BUTTON_POWER
162 #elif (CONFIG_KEYPAD == ONDAVX747_PAD)
163 #define CLIX_BUTTON_QUIT BUTTON_POWER
164 #define CLIX_BUTTON_CLICK BUTTON_MENU
166 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
167 #define CLIX_BUTTON_QUIT BUTTON_POWER
169 #elif (CONFIG_KEYPAD == ANDROID_PAD) \
170 || (CONFIG_KEYPAD == SDL_PAD)
171 #define CLIX_BUTTON_QUIT BUTTON_BACK
173 #elif (CONFIG_KEYPAD == MROBE500_PAD)
174 #define CLIX_BUTTON_QUIT BUTTON_POWER
176 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
177 #define CLIX_BUTTON_QUIT BUTTON_REC
178 #define CLIX_BUTTON_LEFT BUTTON_LEFT
179 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
180 #define CLIX_BUTTON_CLICK BUTTON_PLAY
181 #define CLIX_BUTTON_UP BUTTON_UP
182 #define CLIX_BUTTON_DOWN BUTTON_DOWN
184 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
185 #define CLIX_BUTTON_QUIT BUTTON_REC
186 #define CLIX_BUTTON_UP BUTTON_UP
187 #define CLIX_BUTTON_DOWN BUTTON_DOWN
188 #define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAY
189 #define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
190 #define CLIX_BUTTON_LEFT BUTTON_PREV
191 #define CLIX_BUTTON_RIGHT BUTTON_NEXT
192 #define CLIX_BUTTON_CLICK BUTTON_OK
194 #elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
195 #define CLIX_BUTTON_QUIT BUTTON_POWER
196 #define CLIX_BUTTON_UP BUTTON_UP
197 #define CLIX_BUTTON_DOWN BUTTON_DOWN
198 #define CLIX_BUTTON_LEFT BUTTON_LEFT
199 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
200 #define CLIX_BUTTON_SCROLL_FWD BUTTON_BOTTOMRIGHT
201 #define CLIX_BUTTON_SCROLL_BACK BUTTON_BOTTOMLEFT
202 #define CLIX_BUTTON_CLICK BUTTON_SELECT
204 #elif (CONFIG_KEYPAD == HM60X_PAD)
205 #define CLIX_BUTTON_QUIT BUTTON_POWER
206 #define CLIX_BUTTON_UP BUTTON_UP
207 #define CLIX_BUTTON_DOWN BUTTON_DOWN
208 #define CLIX_BUTTON_SCROLL_FWD (BUTTON_POWER | BUTTON_UP)
209 #define CLIX_BUTTON_SCROLL_BACK (BUTTON_POWER | BUTTON_DOWN)
210 #define CLIX_BUTTON_LEFT BUTTON_LEFT
211 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
212 #define CLIX_BUTTON_CLICK BUTTON_SELECT
214 #elif (CONFIG_KEYPAD == HM801_PAD)
215 #define CLIX_BUTTON_QUIT BUTTON_POWER
216 #define CLIX_BUTTON_UP BUTTON_UP
217 #define CLIX_BUTTON_DOWN BUTTON_DOWN
218 #define CLIX_BUTTON_SCROLL_FWD BUTTON_NEXT
219 #define CLIX_BUTTON_SCROLL_BACK BUTTON_PREV
220 #define CLIX_BUTTON_LEFT BUTTON_LEFT
221 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
222 #define CLIX_BUTTON_CLICK BUTTON_SELECT
224 #else
225 #error "no keymap"
226 #endif
228 #ifndef CLIX_BUTTON_CLICK
229 #define CLIX_BUTTON_CLICK BUTTON_CENTER
230 #endif
232 #define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/clix.score"
233 #define NUM_SCORES 5
234 struct highscore highscores[NUM_SCORES];
236 #define NUM_LEVELS 9
237 #define BLINK_TICKCOUNT 25
238 #define MARGIN 5
240 #if (LCD_WIDTH >= LCD_HEIGHT)
241 #define BOARD_WIDTH 18
242 #define BOARD_HEIGHT 12
243 #else
244 #define BOARD_WIDTH 12
245 #define BOARD_HEIGHT 18
246 #endif
248 #if (LCD_WIDTH>=480)
249 #if (LCD_WIDTH/BOARD_WIDTH) > (LCD_HEIGHT/BOARD_HEIGHT)
250 #define CELL_SIZE (LCD_HEIGHT/BOARD_HEIGHT)
251 #else
252 #define CELL_SIZE (LCD_WIDTH/BOARD_WIDTH)
253 #endif
255 #elif (LCD_WIDTH >= 312 && LCD_HEIGHT >= 468)
256 #define CELL_SIZE 24
258 #elif (LCD_WIDTH >= 306 && LCD_HEIGHT>= 204)
259 #define CELL_SIZE 16
261 #elif (LCD_WIDTH >= 270 && LCD_HEIGHT>= 180)
262 #define CELL_SIZE 14
264 #elif (LCD_WIDTH >= 234 && LCD_HEIGHT>= 156)
265 #define CELL_SIZE 12
267 #elif (LCD_WIDTH >= 198 && LCD_HEIGHT>= 132)
268 #define CELL_SIZE 10
270 #elif (LCD_WIDTH >= 162 && LCD_HEIGHT>= 108)
271 #define CELL_SIZE 8
273 #elif (LCD_WIDTH >= 126 && LCD_HEIGHT>= 84)
274 #define CELL_SIZE 6
276 #elif (LCD_WIDTH >= 60)
277 #define CELL_SIZE 4
278 #endif
280 #define XYPOS(x,y) ((y) * BOARD_WIDTH + x)
281 #define XOFS LCD_WIDTH/2-(BOARD_WIDTH * (CELL_SIZE + 1)/2)
282 #define YOFS (LCD_HEIGHT+10)/2-(BOARD_HEIGHT * (CELL_SIZE + 1)/2)
285 struct clix_game_state_t {
286 int level; /* current level */
287 int score; /* current game score */
288 int x,y; /* current positions of the cursor */
289 int board[BOARD_WIDTH * BOARD_HEIGHT]; /* play board*/
290 /* state of selected fields,maybe we can store this in the play board too */
291 bool board_selected[ BOARD_WIDTH * BOARD_HEIGHT];
292 int selected_count;
293 int status;
294 bool blink; /* true if selected CELLS are currently white */
297 /* game state enum */
298 enum {
299 CLIX_GAMEOVER = -1,
300 CLIX_CONTINUE,
301 CLIX_CLEARED
304 /* cell color enum */
305 enum {
306 CC_BLACK = -1,
307 CC_BLUE,
308 CC_GREEN,
309 CC_RED,
310 CC_YELLOW,
311 CC_ORANGE,
312 CC_CYAN,
313 CC_BROWN,
314 CC_PINK,
315 CC_DARK_BLUE,
316 CC_DARK_GREEN
319 /* recursive function to check if a neighbour cell is of the same color
320 if so call the function with the neighbours position
322 static void clix_set_selected(struct clix_game_state_t* state,
323 const int x, const int y)
325 state->selected_count++;
326 state->board_selected[ XYPOS( x, y)] = true;
327 int current_color = state->board[ XYPOS( x, y)];
329 if( (x - 1) >= 0 &&
330 state->board[ XYPOS( x - 1, y)] == current_color &&
331 state->board_selected[ XYPOS(x - 1, y)] == false)
332 clix_set_selected( state, x - 1, y);
334 if( (y + 1) < BOARD_HEIGHT &&
335 state->board[ XYPOS( x, y + 1)] == current_color &&
336 state->board_selected[ XYPOS(x, y + 1)] == false)
337 clix_set_selected( state, x, y + 1);
339 if( (x + 1) < BOARD_WIDTH &&
340 state->board[ XYPOS( x + 1, y)] == current_color &&
341 state->board_selected[ XYPOS(x + 1, y)] == false)
342 clix_set_selected( state, x + 1, y);
344 if( (y - 1) >= 0 &&
345 state->board[ XYPOS( x, y - 1)] == current_color &&
346 state->board_selected[ XYPOS(x, y - 1)] == false)
347 clix_set_selected( state, x, y - 1);
350 /* updates "blinking" cells by finding out which one is a valid neighbours */
351 static void clix_update_selected(struct clix_game_state_t* state)
353 int i;
355 for( i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; ++i)
357 state->board_selected[i] = false;
359 state->selected_count = 0;
361 /* recursion starts here */
362 clix_set_selected( state, state->x, state->y);
365 /* inits the board with new random colors according to the level */
366 static void clix_init_new_level(struct clix_game_state_t* state)
368 int i;
369 int r;
371 state->y = BOARD_HEIGHT / 2;
372 state->x = BOARD_WIDTH / 2;
374 rb->srand( *rb->current_tick);
375 /* create a random colored board, according to the current level */
376 for(i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i)
378 r = rb->rand() % (state->level + 1);
379 state->board[i] = r;
383 /* this inits the game state structure */
384 static void clix_init(struct clix_game_state_t* state)
386 state->level = 1;
387 state->score = 0;
388 state->blink = false;
389 state->status = CLIX_CONTINUE;
391 clix_init_new_level(state);
393 clix_update_selected(state);
396 /* Function for drawing a cell */
397 static void clix_draw_cell(struct clix_game_state_t* state, const int x, const int y)
399 int realx = XOFS;
400 int realy = YOFS;
402 realx += x * (CELL_SIZE + 1);
403 realy += y * (CELL_SIZE + 1);
405 if (state->blink && state->board_selected[ XYPOS( x, y)]) {
406 rb->lcd_set_foreground(LCD_WHITE);
407 } else {
408 switch (state->board[ XYPOS( x, y)])
410 case CC_BLUE:
411 rb->lcd_set_foreground( LCD_RGBPACK( 25, 25, 255));
412 break;
413 case CC_GREEN:
414 rb->lcd_set_foreground( LCD_RGBPACK( 25, 255, 25));
415 break;
416 case CC_RED:
417 rb->lcd_set_foreground( LCD_RGBPACK( 255, 25, 25));
418 break;
419 case CC_YELLOW:
420 rb->lcd_set_foreground( LCD_RGBPACK( 225, 225, 25));
421 break;
422 case CC_ORANGE:
423 rb->lcd_set_foreground( LCD_RGBPACK( 230, 140, 15));
424 break;
425 case CC_CYAN:
426 rb->lcd_set_foreground( LCD_RGBPACK( 25, 245, 230));
427 break;
428 case CC_BROWN:
429 rb->lcd_set_foreground( LCD_RGBPACK(139, 69, 19));
430 break;
431 case CC_PINK:
432 rb->lcd_set_foreground( LCD_RGBPACK(255, 105, 180));
433 break;
434 case CC_DARK_GREEN:
435 rb->lcd_set_foreground( LCD_RGBPACK( 0, 100, 0));
436 break;
437 case CC_DARK_BLUE:
438 rb->lcd_set_foreground( LCD_RGBPACK( 280, 32, 144));
439 break;
440 default:
441 rb->lcd_set_foreground( LCD_BLACK);
442 break;
446 rb->lcd_fillrect( realx, realy, CELL_SIZE, CELL_SIZE);
448 /* draw cursor */
449 if ( x == state->x && y == state->y) {
450 rb->lcd_set_foreground( LCD_WHITE);
451 rb->lcd_drawrect( realx - 1, realy - 1, CELL_SIZE + 2, CELL_SIZE + 2);
455 /* main function of drawing the whole board and score... */
456 static void clix_draw(struct clix_game_state_t* state)
458 int i,j;
460 /* Clear screen */
461 rb->lcd_clear_display();
462 rb->lcd_set_foreground( LCD_WHITE);
464 rb->lcd_putsxy( MARGIN, MARGIN, "Score:");
465 rb->lcd_putsxyf( 43, MARGIN, "%d", state->score);
466 #if LCD_WIDTH <= 100
467 rb->lcd_putsxy( 75, MARGIN, "L:");
468 rb->lcd_putsxyf( 90, MARGIN, "%d", state->level);
469 #else
470 rb->lcd_putsxy( 75, MARGIN, "Level:");
471 rb->lcd_putsxyf( 113, MARGIN, "%d", state->level);
472 #endif
473 for( i = 0; i < BOARD_WIDTH; ++i)
475 for( j = 0; j < BOARD_HEIGHT; ++j)
477 clix_draw_cell( state, i, j);
481 rb->lcd_update();
482 rb->lcd_set_foreground(LCD_WHITE);
485 static void clix_move_cursor(struct clix_game_state_t* state, const bool left)
487 int x, y;
489 x = state->x;
492 y = state->y;
493 while(state->board[ XYPOS( x, y)] == CC_BLACK && y < BOARD_HEIGHT) y++;
494 if (y < BOARD_HEIGHT) {
495 state->y = y;
496 state->x = x;
498 else
500 if (left) {
501 if( x >= 0)
502 x--;
503 else
504 y = state->y;
506 else
508 if( x < BOARD_WIDTH - 1)
509 x++;
510 else
511 x = 0;
514 } while ( y != state->y);
517 /* returns the color of the given position, if out of bounds return CC_BLACK */
518 static int clix_get_color(struct clix_game_state_t* state, const int x, const int y)
520 if( x >= 0 && x < BOARD_WIDTH && y >= 0 && y < BOARD_HEIGHT)
521 return state->board[XYPOS( x, y)];
522 else
523 return CC_BLACK;
526 static int clix_clear_selected(struct clix_game_state_t* state)
528 int i, j, x, y;
529 bool found_move;
531 state->status = CLIX_CLEARED;
533 /* clear the selected blocks */
534 for( i = 0; i < BOARD_WIDTH; ++i)
536 for( j = 0; j < BOARD_HEIGHT; ++j)
538 if( state->board_selected[ XYPOS( i, j)] )
540 state->board_selected[ XYPOS( i, j)] = false;
541 state->board[ XYPOS( i, j)] = CC_BLACK;
546 /* count score */
547 state->score += state->selected_count * state->level;
548 state->selected_count = 0;
550 /* let blocks falling down */
551 for( i = BOARD_WIDTH - 1; i >= 0; --i)
553 for( j = BOARD_HEIGHT - 1; j >= 0; --j)
555 y = j;
556 while( (y + 1) < BOARD_HEIGHT &&
557 state->board[ XYPOS( i, y + 1)] == CC_BLACK
559 y++;
561 if (y != j) {
562 state->board[ XYPOS( i, y)] = state->board[ XYPOS( i, j)];
563 state->board[ XYPOS( i, j)] = CC_BLACK;
568 /* move columns to left side */
569 for( i = 0; i < BOARD_WIDTH; ++i)
571 x = i;
572 while( (x - 1) >= 0 &&
573 state->board[ XYPOS( x - 1, BOARD_HEIGHT - 1)] == CC_BLACK
575 x--;
576 if (x != i)
578 for( j = 0; j < BOARD_HEIGHT; ++j)
580 state->board[ XYPOS( x, j)] = state->board[ XYPOS( i, j)];
581 state->board[ XYPOS( i, j)] = CC_BLACK;
586 if(state->board[ XYPOS( 0, BOARD_HEIGHT - 1)] != CC_BLACK)
587 state->status = CLIX_CONTINUE;
589 if (state->status != CLIX_CLEARED) {
590 /* check if a move is still possible, otherwise the game is over.
591 tart from the left bottom, because there are the last fields
592 at the end of the game.
594 found_move = false;
595 for( i = 0; !found_move && i < BOARD_WIDTH; ++i)
597 for( j = BOARD_HEIGHT - 1; !found_move && j >= 0; --j)
599 int color = state->board[ XYPOS( i, j)];
600 if (color != CC_BLACK) {
601 if (color == clix_get_color( state, i - 1, j) ||
602 color == clix_get_color( state, i + 1, j) ||
603 color == clix_get_color( state, i, j - 1) ||
604 color == clix_get_color( state, i, j + 1)
607 found_move = true;
612 /* if the loops ended without a possible move, the game is over */
613 if( !found_move)
614 state->status = CLIX_GAMEOVER;
616 /* set cursor to the right position */
617 if (state->status == CLIX_CONTINUE) {
618 clix_move_cursor( state, true);
619 clix_update_selected( state);
623 return state->status;
626 static bool clix_help(void)
628 static char *help_text[] = {
629 "Clix", "", "Aim", "",
630 "Remove", "all", "blocks", "from", "the", "board", "to", "achieve",
631 "the", "next", "level.", "You", "can", "only", "remove", "blocks,",
632 "if", "at", "least", "two", "blocks", "with", "the", "same", "color",
633 "have", "a", "direct", "connection.", "The", "more", "blocks", "you",
634 "remove", "per", "turn,", "the", "more", "points", "you", "get."
636 static struct style_text formation[]={
637 { 0, TEXT_CENTER|TEXT_UNDERLINE },
638 { 2, C_RED },
639 LAST_STYLE_ITEM
642 rb->lcd_setfont(FONT_UI);
643 rb->lcd_set_foreground(LCD_WHITE);
644 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
645 return true;
646 rb->lcd_setfont(FONT_SYSFIXED);
648 return false;
651 static bool _ingame;
652 static int clix_menu_cb(int action, const struct menu_item_ex *this_item)
654 if(action == ACTION_REQUEST_MENUITEM
655 && !_ingame && ((intptr_t)this_item)==0)
656 return ACTION_EXIT_MENUITEM;
657 return action;
660 static int clix_menu(struct clix_game_state_t* state, bool ingame)
662 rb->button_clear_queue();
663 int choice = 0;
664 bool leave_menu=false;
665 int ret=0;
667 _ingame = ingame;
669 MENUITEM_STRINGLIST (main_menu, "Clix Menu", clix_menu_cb,
670 "Resume Game",
671 "Start New Game",
672 "Help",
673 "High Scores",
674 "Playback Control",
675 "Quit");
677 while (!leave_menu) {
679 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
680 case 0:
681 leave_menu=true;
682 ret = 0;
683 break;
684 case 1:
685 clix_init(state);
686 leave_menu=true;
687 ret = 0;
688 break;
689 case 2:
690 if (clix_help()) {
691 leave_menu=true;
692 ret = 1;
694 break;
695 case 3:
696 highscore_show(-1, highscores, NUM_SCORES, true);
697 break;
698 case 4:
699 playback_control(NULL);
700 break;
701 case 5:
702 case MENU_ATTACHED_USB:
703 leave_menu=true;
704 ret = 1;
705 break;
706 default:
707 break;
711 return ret;
714 static int clix_click(struct clix_game_state_t* state)
716 int position;
717 if (state->selected_count <= 1) {
718 return 0;
720 switch( clix_clear_selected( state))
722 case CLIX_CLEARED:
723 state->score += state->level * 100;
724 clix_draw( state);
725 if (state->level < NUM_LEVELS) {
726 rb->splash(HZ*2, "Great! Next Level!");
727 state->level++;
728 clix_init_new_level( state);
729 clix_update_selected( state);
731 else {
732 rb->splash(HZ*2, "Congratulation!!!");
733 rb->splash(HZ*2, "You have finished the game.");
734 if(clix_menu(state, 0))
735 return 1;
737 break;
738 case CLIX_GAMEOVER:
739 clix_draw( state);
740 rb->splash(HZ*2, "Game Over!");
741 rb->lcd_clear_display();
742 position = highscore_update(state->score, state->level, "",
743 highscores, NUM_SCORES);
744 if (position != -1)
746 if (position == 0)
747 rb->splash(HZ*2, "New High Score");
748 highscore_show(position, highscores, NUM_SCORES, true);
750 if(clix_menu(state, 0))
751 return 1;
752 break;
753 default:
754 rb->sleep(10); /* prevent repeating clicks */
755 break;
757 return 0;
760 static int clix_handle_game(struct clix_game_state_t* state)
762 int button;
763 int blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
765 int time;
766 int start;
767 int end;
768 int oldx, oldy;
770 if (clix_menu(state, 0))
771 return 1;
773 while(true)
775 if (TIME_AFTER(*rb->current_tick, blink_tick)) {
776 state->blink = !state->blink;
777 blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
780 time = 6; /* number of ticks this function will loop reading keys */
781 start = *rb->current_tick;
782 end = start + time;
783 while(TIME_BEFORE(*rb->current_tick, end))
785 oldx = state->x;
786 oldy = state->y;
788 button = rb->button_get_w_tmo(end - *rb->current_tick);
789 #ifdef HAVE_TOUCHSCREEN
790 if(button & BUTTON_TOUCHSCREEN)
792 int x = rb->button_get_data() >> 16;
793 int y = rb->button_get_data() & 0xffff;
795 x -= XOFS;
796 y -= YOFS;
797 if(x >= 0 && y >= 0)
799 x /= CELL_SIZE + 1;
800 y /= CELL_SIZE + 1;
802 if(x < BOARD_WIDTH && y < BOARD_HEIGHT
803 && state->board[XYPOS(x, y)] != CC_BLACK)
805 if(state->x == x && state->y == y && button & BUTTON_REL)
806 button = CLIX_BUTTON_CLICK;
807 else
809 state->x = x;
810 state->y = y;
815 #endif
816 switch( button)
818 #ifndef HAVE_TOUCHSCREEN
819 #ifdef CLIX_BUTTON_SCROLL_BACK
820 case CLIX_BUTTON_SCROLL_BACK:
821 case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
822 #endif
823 case CLIX_BUTTON_UP:
824 case CLIX_BUTTON_UP|BUTTON_REPEAT:
825 if( state->y == 0 ||
826 state->board[ XYPOS( state->x, state->y - 1)] ==
827 CC_BLACK
829 state->y = BOARD_HEIGHT - 1;
830 else
831 state->y--;
833 clix_move_cursor(state, true);
834 break;
836 #ifdef CLIX_BUTTON_SCROLL_FWD
837 case CLIX_BUTTON_SCROLL_FWD:
838 case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
839 #endif
840 case CLIX_BUTTON_DOWN:
841 case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
842 if( state->y == (BOARD_HEIGHT - 1))
843 state->y = 0;
844 else
845 state->y++;
847 clix_move_cursor( state, true);
848 break;
850 case CLIX_BUTTON_RIGHT:
851 case CLIX_BUTTON_RIGHT|BUTTON_REPEAT:
852 if( state->x == (BOARD_WIDTH - 1))
853 state->x = 0;
854 else
855 state->x++;
857 clix_move_cursor(state, false);
858 break;
860 case CLIX_BUTTON_LEFT:
861 case CLIX_BUTTON_LEFT|BUTTON_REPEAT:
862 if( state->x == 0)
863 state->x = BOARD_WIDTH - 1;
864 else
865 state->x--;
867 clix_move_cursor(state, true);
868 break;
869 #endif
870 case CLIX_BUTTON_CLICK:
871 if (clix_click(state) == 1)
872 return 1;
873 break;
875 case CLIX_BUTTON_QUIT:
876 if (clix_menu(state, 1) != 0) {
877 rb->button_clear_queue();
878 return 1;
880 break;
881 default:
883 break;
886 if( (oldx != state->x || oldy != state->y) &&
887 state->board_selected[ XYPOS( oldx, oldy)] !=
888 state->board_selected[ XYPOS( state->x, state->y)]
891 clix_update_selected(state);
893 clix_draw(state);
894 rb->sleep(time);
899 /* this is the plugin entry point */
900 enum plugin_status plugin_start(const void* parameter)
902 (void)parameter;
904 rb->lcd_set_backdrop(NULL);
905 rb->lcd_set_foreground(LCD_WHITE);
906 rb->lcd_set_background(LCD_BLACK);
907 rb->lcd_setfont(FONT_SYSFIXED);
908 #ifdef HAVE_TOUCHSCREEN
909 rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
910 #endif
912 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
914 struct clix_game_state_t state;
915 clix_handle_game( &state);
917 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
919 return PLUGIN_OK;