Fix warning about missing newline at the EOF
[maemo-rb.git] / apps / plugins / clix.c
blob06fe0d84e03c1a29f674cd68026a164f08f60340
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
165 #elif (CONFIG_KEYPAD == ONDAVX777_PAD)
166 #define CLIX_BUTTON_QUIT BUTTON_POWER
168 #elif (CONFIG_KEYPAD == MROBE500_PAD)
169 #define CLIX_BUTTON_QUIT BUTTON_POWER
171 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
172 #define CLIX_BUTTON_QUIT BUTTON_REC
173 #define CLIX_BUTTON_LEFT BUTTON_LEFT
174 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
175 #define CLIX_BUTTON_CLICK BUTTON_PLAY
176 #define CLIX_BUTTON_UP BUTTON_UP
177 #define CLIX_BUTTON_DOWN BUTTON_DOWN
179 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
180 #define CLIX_BUTTON_QUIT BUTTON_REC
181 #define CLIX_BUTTON_UP BUTTON_UP
182 #define CLIX_BUTTON_DOWN BUTTON_DOWN
183 #define CLIX_BUTTON_SCROLL_FWD BUTTON_PLAY
184 #define CLIX_BUTTON_SCROLL_BACK BUTTON_MENU
185 #define CLIX_BUTTON_LEFT BUTTON_PREV
186 #define CLIX_BUTTON_RIGHT BUTTON_NEXT
187 #define CLIX_BUTTON_CLICK BUTTON_OK
189 #elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
190 #define CLIX_BUTTON_QUIT BUTTON_POWER
191 #define CLIX_BUTTON_UP BUTTON_UP
192 #define CLIX_BUTTON_DOWN BUTTON_DOWN
193 #define CLIX_BUTTON_SCROLL_FWD BUTTON_BACK
194 #define CLIX_BUTTON_SCROLL_BACK BUTTON_PLAYPAUSE
195 #define CLIX_BUTTON_LEFT BUTTON_LEFT
196 #define CLIX_BUTTON_RIGHT BUTTON_RIGHT
197 #define CLIX_BUTTON_CLICK BUTTON_SELECT
199 #else
200 #error "no keymap"
201 #endif
203 #ifndef CLIX_BUTTON_CLICK
204 #define CLIX_BUTTON_CLICK BUTTON_CENTER
205 #endif
207 #define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/clix.score"
208 #define NUM_SCORES 5
209 struct highscore highscores[NUM_SCORES];
211 #define NUM_LEVELS 9
212 #define BLINK_TICKCOUNT 25
213 #define MARGIN 5
215 #if (LCD_WIDTH >= LCD_HEIGHT)
216 #define BOARD_WIDTH 18
217 #define BOARD_HEIGHT 12
218 #else
219 #define BOARD_WIDTH 12
220 #define BOARD_HEIGHT 18
221 #endif
223 #if (LCD_WIDTH>=480)
224 #if (LCD_WIDTH/BOARD_WIDTH) > (LCD_HEIGHT/BOARD_HEIGHT)
225 #define CELL_SIZE (LCD_HEIGHT/BOARD_HEIGHT)
226 #else
227 #define CELL_SIZE (LCD_WIDTH/BOARD_WIDTH)
228 #endif
230 #elif (LCD_WIDTH >= 306 && LCD_HEIGHT>= 204)
231 #define CELL_SIZE 16
233 #elif (LCD_WIDTH >= 270 && LCD_HEIGHT>= 180)
234 #define CELL_SIZE 14
236 #elif (LCD_WIDTH >= 234 && LCD_HEIGHT>= 156)
237 #define CELL_SIZE 12
239 #elif (LCD_WIDTH >= 198 && LCD_HEIGHT>= 132)
240 #define CELL_SIZE 10
242 #elif (LCD_WIDTH >= 162 && LCD_HEIGHT>= 108)
243 #define CELL_SIZE 8
245 #elif (LCD_WIDTH >= 126 && LCD_HEIGHT>= 84)
246 #define CELL_SIZE 6
248 #elif (LCD_WIDTH >= 60)
249 #define CELL_SIZE 4
250 #endif
252 #define XYPOS(x,y) ((y) * BOARD_WIDTH + x)
253 #define XOFS LCD_WIDTH/2-(BOARD_WIDTH * (CELL_SIZE + 1)/2)
254 #define YOFS (LCD_HEIGHT+10)/2-(BOARD_HEIGHT * (CELL_SIZE + 1)/2)
257 struct clix_game_state_t {
258 int level; /* current level */
259 int score; /* current game score */
260 int x,y; /* current positions of the cursor */
261 int board[BOARD_WIDTH * BOARD_HEIGHT]; /* play board*/
262 /* state of selected fields,maybe we can store this in the play board too */
263 bool board_selected[ BOARD_WIDTH * BOARD_HEIGHT];
264 int selected_count;
265 int status;
266 bool blink; /* true if selected CELLS are currently white */
269 /* game state enum */
270 enum {
271 CLIX_GAMEOVER = -1,
272 CLIX_CONTINUE,
273 CLIX_CLEARED
276 /* cell color enum */
277 enum {
278 CC_BLACK = -1,
279 CC_BLUE,
280 CC_GREEN,
281 CC_RED,
282 CC_YELLOW,
283 CC_ORANGE,
284 CC_CYAN,
285 CC_BROWN,
286 CC_PINK,
287 CC_DARK_BLUE,
288 CC_DARK_GREEN
291 /* recursive function to check if a neighbour cell is of the same color
292 if so call the function with the neighbours position
294 static void clix_set_selected(struct clix_game_state_t* state,
295 const int x, const int y)
297 state->selected_count++;
298 state->board_selected[ XYPOS( x, y)] = true;
299 int current_color = state->board[ XYPOS( x, y)];
301 if( (x - 1) >= 0 &&
302 state->board[ XYPOS( x - 1, y)] == current_color &&
303 state->board_selected[ XYPOS(x - 1, y)] == false)
304 clix_set_selected( state, x - 1, y);
306 if( (y + 1) < BOARD_HEIGHT &&
307 state->board[ XYPOS( x, y + 1)] == current_color &&
308 state->board_selected[ XYPOS(x, y + 1)] == false)
309 clix_set_selected( state, x, y + 1);
311 if( (x + 1) < BOARD_WIDTH &&
312 state->board[ XYPOS( x + 1, y)] == current_color &&
313 state->board_selected[ XYPOS(x + 1, y)] == false)
314 clix_set_selected( state, x + 1, y);
316 if( (y - 1) >= 0 &&
317 state->board[ XYPOS( x, y - 1)] == current_color &&
318 state->board_selected[ XYPOS(x, y - 1)] == false)
319 clix_set_selected( state, x, y - 1);
322 /* updates "blinking" cells by finding out which one is a valid neighbours */
323 static void clix_update_selected(struct clix_game_state_t* state)
325 int i;
327 for( i = 0; i < BOARD_WIDTH * BOARD_HEIGHT; ++i)
329 state->board_selected[i] = false;
331 state->selected_count = 0;
333 /* recursion starts here */
334 clix_set_selected( state, state->x, state->y);
337 /* inits the board with new random colors according to the level */
338 static void clix_init_new_level(struct clix_game_state_t* state)
340 int i;
341 int r;
343 state->y = BOARD_HEIGHT / 2;
344 state->x = BOARD_WIDTH / 2;
346 rb->srand( *rb->current_tick);
347 /* create a random colored board, according to the current level */
348 for(i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i)
350 r = rb->rand() % (state->level + 1);
351 state->board[i] = r;
355 /* this inits the game state structure */
356 static void clix_init(struct clix_game_state_t* state)
358 state->level = 1;
359 state->score = 0;
360 state->blink = false;
361 state->status = CLIX_CONTINUE;
363 clix_init_new_level(state);
365 clix_update_selected(state);
368 /* Function for drawing a cell */
369 static void clix_draw_cell(struct clix_game_state_t* state, const int x, const int y)
371 int realx = XOFS;
372 int realy = YOFS;
374 realx += x * (CELL_SIZE + 1);
375 realy += y * (CELL_SIZE + 1);
377 if (state->blink && state->board_selected[ XYPOS( x, y)]) {
378 rb->lcd_set_foreground(LCD_WHITE);
379 } else {
380 switch (state->board[ XYPOS( x, y)])
382 case CC_BLUE:
383 rb->lcd_set_foreground( LCD_RGBPACK( 25, 25, 255));
384 break;
385 case CC_GREEN:
386 rb->lcd_set_foreground( LCD_RGBPACK( 25, 255, 25));
387 break;
388 case CC_RED:
389 rb->lcd_set_foreground( LCD_RGBPACK( 255, 25, 25));
390 break;
391 case CC_YELLOW:
392 rb->lcd_set_foreground( LCD_RGBPACK( 225, 225, 25));
393 break;
394 case CC_ORANGE:
395 rb->lcd_set_foreground( LCD_RGBPACK( 230, 140, 15));
396 break;
397 case CC_CYAN:
398 rb->lcd_set_foreground( LCD_RGBPACK( 25, 245, 230));
399 break;
400 case CC_BROWN:
401 rb->lcd_set_foreground( LCD_RGBPACK(139, 69, 19));
402 break;
403 case CC_PINK:
404 rb->lcd_set_foreground( LCD_RGBPACK(255, 105, 180));
405 break;
406 case CC_DARK_GREEN:
407 rb->lcd_set_foreground( LCD_RGBPACK( 0, 100, 0));
408 break;
409 case CC_DARK_BLUE:
410 rb->lcd_set_foreground( LCD_RGBPACK( 280, 32, 144));
411 break;
412 default:
413 rb->lcd_set_foreground( LCD_BLACK);
414 break;
418 rb->lcd_fillrect( realx, realy, CELL_SIZE, CELL_SIZE);
420 /* draw cursor */
421 if ( x == state->x && y == state->y) {
422 rb->lcd_set_foreground( LCD_WHITE);
423 rb->lcd_drawrect( realx - 1, realy - 1, CELL_SIZE + 2, CELL_SIZE + 2);
427 /* main function of drawing the whole board and score... */
428 static void clix_draw(struct clix_game_state_t* state)
430 int i,j;
432 /* Clear screen */
433 rb->lcd_clear_display();
434 rb->lcd_set_foreground( LCD_WHITE);
436 rb->lcd_putsxy( MARGIN, MARGIN, "Score:");
437 rb->lcd_putsxyf( 43, MARGIN, "%d", state->score);
438 #if LCD_WIDTH <= 100
439 rb->lcd_putsxy( 75, MARGIN, "L:");
440 rb->lcd_putsxyf( 90, MARGIN, "%d", state->level);
441 #else
442 rb->lcd_putsxy( 75, MARGIN, "Level:");
443 rb->lcd_putsxyf( 113, MARGIN, "%d", state->level);
444 #endif
445 for( i = 0; i < BOARD_WIDTH; ++i)
447 for( j = 0; j < BOARD_HEIGHT; ++j)
449 clix_draw_cell( state, i, j);
453 rb->lcd_update();
454 rb->lcd_set_foreground(LCD_WHITE);
457 static void clix_move_cursor(struct clix_game_state_t* state, const bool left)
459 int x, y;
461 x = state->x;
464 y = state->y;
465 while(state->board[ XYPOS( x, y)] == CC_BLACK && y < BOARD_HEIGHT) y++;
466 if (y < BOARD_HEIGHT) {
467 state->y = y;
468 state->x = x;
470 else
472 if (left) {
473 if( x >= 0)
474 x--;
475 else
476 y = state->y;
478 else
480 if( x < BOARD_WIDTH - 1)
481 x++;
482 else
483 x = 0;
486 } while ( y != state->y);
489 /* returns the color of the given position, if out of bounds return CC_BLACK */
490 static int clix_get_color(struct clix_game_state_t* state, const int x, const int y)
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;
498 static int clix_clear_selected(struct clix_game_state_t* state)
500 int i, j, x, y;
501 bool found_move;
503 state->status = CLIX_CLEARED;
505 /* clear the selected blocks */
506 for( i = 0; i < BOARD_WIDTH; ++i)
508 for( j = 0; j < BOARD_HEIGHT; ++j)
510 if( state->board_selected[ XYPOS( i, j)] )
512 state->board_selected[ XYPOS( i, j)] = false;
513 state->board[ XYPOS( i, j)] = CC_BLACK;
518 /* count score */
519 state->score += state->selected_count * state->level;
520 state->selected_count = 0;
522 /* let blocks falling down */
523 for( i = BOARD_WIDTH - 1; i >= 0; --i)
525 for( j = BOARD_HEIGHT - 1; j >= 0; --j)
527 y = j;
528 while( (y + 1) < BOARD_HEIGHT &&
529 state->board[ XYPOS( i, y + 1)] == CC_BLACK
531 y++;
533 if (y != j) {
534 state->board[ XYPOS( i, y)] = state->board[ XYPOS( i, j)];
535 state->board[ XYPOS( i, j)] = CC_BLACK;
540 /* move columns to left side */
541 for( i = 0; i < BOARD_WIDTH; ++i)
543 x = i;
544 while( (x - 1) >= 0 &&
545 state->board[ XYPOS( x - 1, BOARD_HEIGHT - 1)] == CC_BLACK
547 x--;
548 if (x != i)
550 for( j = 0; j < BOARD_HEIGHT; ++j)
552 state->board[ XYPOS( x, j)] = state->board[ XYPOS( i, j)];
553 state->board[ XYPOS( i, j)] = CC_BLACK;
558 if(state->board[ XYPOS( 0, BOARD_HEIGHT - 1)] != CC_BLACK)
559 state->status = CLIX_CONTINUE;
561 if (state->status != CLIX_CLEARED) {
562 /* check if a move is still possible, otherwise the game is over.
563 tart from the left bottom, because there are the last fields
564 at the end of the game.
566 found_move = false;
567 for( i = 0; !found_move && i < BOARD_WIDTH; ++i)
569 for( j = BOARD_HEIGHT - 1; !found_move && j >= 0; --j)
571 int color = state->board[ XYPOS( i, j)];
572 if (color != CC_BLACK) {
573 if (color == clix_get_color( state, i - 1, j) ||
574 color == clix_get_color( state, i + 1, j) ||
575 color == clix_get_color( state, i, j - 1) ||
576 color == clix_get_color( state, i, j + 1)
579 found_move = true;
584 /* if the loops ended without a possible move, the game is over */
585 if( !found_move)
586 state->status = CLIX_GAMEOVER;
588 /* set cursor to the right position */
589 if (state->status == CLIX_CONTINUE) {
590 clix_move_cursor( state, true);
591 clix_update_selected( state);
595 return state->status;
598 static bool clix_help(void)
600 static char *help_text[] = {
601 "Clix", "", "Aim", "",
602 "Remove", "all", "blocks", "from", "the", "board", "to", "achieve",
603 "the", "next", "level.", "You", "can", "only", "remove", "blocks,",
604 "if", "at", "least", "two", "blocks", "with", "the", "same", "color",
605 "have", "a", "direct", "connection.", "The", "more", "blocks", "you",
606 "remove", "per", "turn,", "the", "more", "points", "you", "get."
608 static struct style_text formation[]={
609 { 0, TEXT_CENTER|TEXT_UNDERLINE },
610 { 2, C_RED },
611 LAST_STYLE_ITEM
614 rb->lcd_setfont(FONT_UI);
615 rb->lcd_set_foreground(LCD_WHITE);
616 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
617 return true;
618 rb->lcd_setfont(FONT_SYSFIXED);
620 return false;
623 static bool _ingame;
624 static int clix_menu_cb(int action, const struct menu_item_ex *this_item)
626 if(action == ACTION_REQUEST_MENUITEM
627 && !_ingame && ((intptr_t)this_item)==0)
628 return ACTION_EXIT_MENUITEM;
629 return action;
632 static int clix_menu(struct clix_game_state_t* state, bool ingame)
634 rb->button_clear_queue();
635 int choice = 0;
636 bool leave_menu=false;
637 int ret=0;
639 _ingame = ingame;
641 MENUITEM_STRINGLIST (main_menu, "Clix Menu", clix_menu_cb,
642 "Resume Game",
643 "Start New Game",
644 "Help",
645 "High Scores",
646 "Playback Control",
647 "Quit");
649 #ifdef HAVE_TOUCHSCREEN
650 /* Entering Menu, set the touchscreen to the global setting */
651 rb->touchscreen_set_mode(rb->global_settings->touch_mode);
652 #endif
654 while (!leave_menu) {
656 switch (rb->do_menu(&main_menu, &choice, NULL, false)) {
657 case 0:
658 leave_menu=true;
659 ret = 0;
660 break;
661 case 1:
662 clix_init(state);
663 leave_menu=true;
664 ret = 0;
665 break;
666 case 2:
667 if (clix_help()) {
668 leave_menu=true;
669 ret = 1;
671 break;
672 case 3:
673 highscore_show(-1, highscores, NUM_SCORES, true);
674 break;
675 case 4:
676 playback_control(NULL);
677 break;
678 case 5:
679 case MENU_ATTACHED_USB:
680 leave_menu=true;
681 ret = 1;
682 break;
683 default:
684 break;
688 #ifdef HAVE_TOUCHSCREEN
689 /* Leaving the menu, set back to pointer mode */
690 rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
691 #endif
693 return ret;
696 static int clix_click(struct clix_game_state_t* state)
698 int position;
699 if (state->selected_count <= 1) {
700 return 0;
702 switch( clix_clear_selected( state))
704 case CLIX_CLEARED:
705 state->score += state->level * 100;
706 clix_draw( state);
707 if (state->level < NUM_LEVELS) {
708 rb->splash(HZ*2, "Great! Next Level!");
709 state->level++;
710 clix_init_new_level( state);
711 clix_update_selected( state);
713 else {
714 rb->splash(HZ*2, "Congratulation!!!");
715 rb->splash(HZ*2, "You have finished the game.");
716 if(clix_menu(state, 0))
717 return 1;
719 break;
720 case CLIX_GAMEOVER:
721 clix_draw( state);
722 rb->splash(HZ*2, "Game Over!");
723 rb->lcd_clear_display();
724 position = highscore_update(state->score, state->level, "",
725 highscores, NUM_SCORES);
726 if (position != -1)
728 if (position == 0)
729 rb->splash(HZ*2, "New High Score");
730 highscore_show(position, highscores, NUM_SCORES, true);
732 if(clix_menu(state, 0))
733 return 1;
734 break;
735 default:
736 rb->sleep(10); /* prevent repeating clicks */
737 break;
739 return 0;
742 static int clix_handle_game(struct clix_game_state_t* state)
744 int button;
745 int blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
747 int time;
748 int start;
749 int end;
750 int oldx, oldy;
752 if (clix_menu(state, 0))
753 return 1;
755 while(true)
757 if (TIME_AFTER(*rb->current_tick, blink_tick)) {
758 state->blink = !state->blink;
759 blink_tick = *rb->current_tick + BLINK_TICKCOUNT;
762 time = 6; /* number of ticks this function will loop reading keys */
763 start = *rb->current_tick;
764 end = start + time;
765 while(TIME_BEFORE(*rb->current_tick, end))
767 oldx = state->x;
768 oldy = state->y;
770 button = rb->button_get_w_tmo(end - *rb->current_tick);
771 #ifdef HAVE_TOUCHSCREEN
772 if(button & BUTTON_TOUCHSCREEN)
774 int x = rb->button_get_data() >> 16;
775 int y = rb->button_get_data() & 0xffff;
777 x -= XOFS;
778 y -= YOFS;
779 if(x >= 0 && y >= 0)
781 x /= CELL_SIZE + 1;
782 y /= CELL_SIZE + 1;
784 if(x < BOARD_WIDTH && y < BOARD_HEIGHT
785 && state->board[XYPOS(x, y)] != CC_BLACK)
787 if(state->x == x && state->y == y)
788 button = CLIX_BUTTON_CLICK;
789 else
791 state->x = x;
792 state->y = y;
797 #endif
798 switch( button)
800 #ifndef HAVE_TOUCHSCREEN
801 #ifdef CLIX_BUTTON_SCROLL_BACK
802 case CLIX_BUTTON_SCROLL_BACK:
803 case CLIX_BUTTON_SCROLL_BACK|BUTTON_REPEAT:
804 #endif
805 case CLIX_BUTTON_UP:
806 case CLIX_BUTTON_UP|BUTTON_REPEAT:
807 if( state->y == 0 ||
808 state->board[ XYPOS( state->x, state->y - 1)] ==
809 CC_BLACK
811 state->y = BOARD_HEIGHT - 1;
812 else
813 state->y--;
815 clix_move_cursor(state, true);
816 break;
818 #ifdef CLIX_BUTTON_SCROLL_FWD
819 case CLIX_BUTTON_SCROLL_FWD:
820 case CLIX_BUTTON_SCROLL_FWD|BUTTON_REPEAT:
821 #endif
822 case CLIX_BUTTON_DOWN:
823 case CLIX_BUTTON_DOWN|BUTTON_REPEAT:
824 if( state->y == (BOARD_HEIGHT - 1))
825 state->y = 0;
826 else
827 state->y++;
829 clix_move_cursor( state, true);
830 break;
832 case CLIX_BUTTON_RIGHT:
833 case CLIX_BUTTON_RIGHT|BUTTON_REPEAT:
834 if( state->x == (BOARD_WIDTH - 1))
835 state->x = 0;
836 else
837 state->x++;
839 clix_move_cursor(state, false);
840 break;
842 case CLIX_BUTTON_LEFT:
843 case CLIX_BUTTON_LEFT|BUTTON_REPEAT:
844 if( state->x == 0)
845 state->x = BOARD_WIDTH - 1;
846 else
847 state->x--;
849 clix_move_cursor(state, true);
850 break;
851 #endif
852 case CLIX_BUTTON_CLICK:
853 if (clix_click(state) == 1)
854 return 1;
855 break;
857 case CLIX_BUTTON_QUIT:
858 if (clix_menu(state, 1) != 0) {
859 rb->button_clear_queue();
860 return 1;
862 break;
863 default:
865 break;
868 if( (oldx != state->x || oldy != state->y) &&
869 state->board_selected[ XYPOS( oldx, oldy)] !=
870 state->board_selected[ XYPOS( state->x, state->y)]
873 clix_update_selected(state);
875 clix_draw(state);
876 rb->sleep(time);
881 /* this is the plugin entry point */
882 enum plugin_status plugin_start(const void* parameter)
884 (void)parameter;
886 rb->lcd_set_backdrop(NULL);
887 rb->lcd_set_foreground(LCD_WHITE);
888 rb->lcd_set_background(LCD_BLACK);
889 rb->lcd_setfont(FONT_SYSFIXED);
890 #ifdef HAVE_TOUCHSCREEN
891 rb->touchscreen_set_mode(TOUCHSCREEN_POINT);
892 #endif
894 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
896 struct clix_game_state_t state;
897 clix_handle_game( &state);
899 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
901 return PLUGIN_OK;