00001
00039 #include <gfx/gfx.h>
00040 #include <gfx/win.h>
00041 #include <gfx/wtk.h>
00042 #include <gfx/sysfont.h>
00043 #include <stream.h>
00044 #include <string.h>
00045 #include <timer.h>
00046
00047 #include "app_desktop.h"
00048 #include "app_memgame.h"
00049 #include "file_loader.h"
00050
00063
00064 #define NR_OF_PIECE_PAIRS 6
00065
00066 #define NR_OF_PIECES (2 * NR_OF_PIECE_PAIRS)
00067
00068 #define NR_OF_BOARD_COLUMNS 4
00069
00070 #define BOARD_POS_X 30
00071
00072 #define BOARD_POS_Y 5
00073
00074 #define BACKGROUND_COLOR GFX_COLOR(0, 0, 0)
00075
00076 #define WIDGET_FONT_SCALE 2
00077
00079
00086
00087 #define PIECE_PAIR_FILE_0 "i_fonts"
00088
00089 #define PIECE_PAIR_FILE_1 "i_pics"
00090
00091 #define PIECE_PAIR_FILE_2 "i_avr"
00092
00093 #define PIECE_PAIR_FILE_3 "i_clock"
00094
00095 #define PIECE_PAIR_FILE_4 "i_files"
00096
00097 #define PIECE_PAIR_FILE_5 "i_games"
00098
00099 #define PIECE_BACK_FILE "cardback"
00100
00101 #define PIECE_SIZE_X 57
00102
00103 #define PIECE_SIZE_Y PIECE_SIZE_X
00104
00105 #define PIECE_SPACING_X 10
00106
00107 #define PIECE_SPACING_Y PIECE_SPACING_X
00108
00109 #define PIECE_INVALID_INDEX 0xFF
00110
00112
00119
00120 #define BTN_NEW_GAME_TEXT "New"
00121
00122 #define BTN_NEW_GAME_POS_X (APP_EXIT_BUTTON_POS_X - \
00123 2 * APP_EXIT_BUTTON_SIZE_X)
00124
00125 #define BTN_NEW_GAME_POS_Y APP_EXIT_BUTTON_POS_Y
00126
00127 #define BTN_NEW_GAME_SIZE_X (2 * APP_EXIT_BUTTON_SIZE_X)
00128
00129 #define BTN_NEW_GAME_SIZE_Y APP_EXIT_BUTTON_SIZE_Y
00130
00132
00139
00140 #define MSG_TRIES_TEXT "Tries: %i"
00141
00142 #define MSG_TRIES_LENGTH 10
00143
00144 #define MSG_TRIES_POS_X 0
00145
00146 #define MSG_TRIES_POS_Y (APP_EXIT_BUTTON_POS_Y + \
00147 (APP_EXIT_BUTTON_SIZE_Y / 2) - \
00148 (gfx_font_get_height(&sysfont) / 2))
00149
00150 #define MSG_TRIES_SIZE_X (gfx_get_width() - \
00151 APP_EXIT_BUTTON_SIZE_X - BTN_NEW_GAME_SIZE_X)
00152
00153 #define MSG_TRIES_SIZE_Y gfx_font_get_height(&sysfont)
00154
00156
00163
00164 #define MSG_GAME_OVER_TEXT "Well done!"
00165
00166 #define MSG_GAME_OVER_SCALE 4
00167
00168 #define MSG_GAME_OVER_POS_X 45
00169
00170 #define MSG_GAME_OVER_POS_Y 105
00171
00172 #define MSG_GAME_OVER_GLOW_OFFSET 1
00173
00174 #define MSG_GAME_OVER_COLOR GFX_COLOR(255, 160, 0)
00175
00176 #define MSG_GAME_OVER_GLOW_COLOR GFX_COLOR(250, 250, 250)
00177
00179
00186
00187 #define TIMER_PAUSE_HALF_SECONDS 3
00188
00199 #define TIMER_CLOCK_RATE (0x1ffffUL / 8)
00200
00202
00204 enum memgame_command_id {
00206 CMD_NONE,
00208 CMD_NEW_GAME,
00210 CMD_EXIT,
00211 };
00212
00214 enum memgame_state {
00216 DRAW_ALL_PIECES,
00218 SELECT_FIRST_PIECE,
00220 SHOWN_FIRST_PIECE,
00222 SELECT_SECOND_PIECE,
00224 SHOWN_SECOND_PIECE,
00226 HIDE_FIRST_PIECE,
00228 HIDE_SECOND_PIECE,
00230 HIDDEN_BOTH_PIECES,
00232 GAME_OVER,
00233 };
00234
00236 struct memgame_piece {
00238 uint8_t pair;
00240 bool found;
00241 };
00242
00249 struct memgame_context {
00251 struct win_window *win;
00253 struct gfx_bitmap bitmap;
00255 struct workqueue_task *task;
00257 struct font old_sysfont;
00259 struct timer timer;
00261 uint16_t timer_delay;
00263 uint8_t ticks_to_go;
00265 struct memgame_piece pieces[NR_OF_PIECES];
00267 enum memgame_state state;
00269 uint8_t piece_1;
00271 uint8_t piece_2;
00273 uint8_t pairs_left;
00275 uint8_t tries;
00277 struct wtk_label *tries_label;
00279 bool busy;
00280 };
00281
00289 static struct memgame_context *game_ctx;
00290
00296 static uint16_t memgame_rand(void)
00297 {
00298 static uint16_t seed = 12345;
00299 seed = (25037 * seed) + 1;
00300 return seed;
00301 }
00302
00314 #define memgame_print_helper(str, x, y, color) \
00315 gfx_draw_string(str, x, y, &sysfont, color, GFX_COLOR_TRANSPARENT)
00316
00324 static void memgame_print_game_over(void)
00325 {
00326 #ifdef CONFIG_GFX_USE_CLIPPING
00327 gfx_set_clipping(0, 0, gfx_get_width(), gfx_get_height());
00328 #endif
00329
00330
00331 sysfont.scale = MSG_GAME_OVER_SCALE;
00332
00333
00334 memgame_print_helper(MSG_GAME_OVER_TEXT,
00335 MSG_GAME_OVER_POS_X + MSG_GAME_OVER_GLOW_OFFSET,
00336 MSG_GAME_OVER_POS_Y + MSG_GAME_OVER_GLOW_OFFSET,
00337 MSG_GAME_OVER_GLOW_COLOR);
00338
00339 memgame_print_helper(MSG_GAME_OVER_TEXT,
00340 MSG_GAME_OVER_POS_X - MSG_GAME_OVER_GLOW_OFFSET,
00341 MSG_GAME_OVER_POS_Y - MSG_GAME_OVER_GLOW_OFFSET,
00342 MSG_GAME_OVER_GLOW_COLOR);
00343
00344 memgame_print_helper(MSG_GAME_OVER_TEXT,
00345 MSG_GAME_OVER_POS_X + MSG_GAME_OVER_GLOW_OFFSET,
00346 MSG_GAME_OVER_POS_Y - MSG_GAME_OVER_GLOW_OFFSET,
00347 MSG_GAME_OVER_GLOW_COLOR);
00348
00349 memgame_print_helper(MSG_GAME_OVER_TEXT,
00350 MSG_GAME_OVER_POS_X - MSG_GAME_OVER_GLOW_OFFSET,
00351 MSG_GAME_OVER_POS_Y + MSG_GAME_OVER_GLOW_OFFSET,
00352 MSG_GAME_OVER_GLOW_COLOR);
00353
00354
00355 memgame_print_helper(MSG_GAME_OVER_TEXT, MSG_GAME_OVER_POS_X,
00356 MSG_GAME_OVER_POS_Y, MSG_GAME_OVER_COLOR);
00357
00358
00359 sysfont.scale = WIDGET_FONT_SCALE;
00360 }
00361
00368 static void memgame_print_tries(void)
00369 {
00370 char str[MSG_TRIES_LENGTH + 1];
00371
00372
00373 snprintf(str, MSG_TRIES_LENGTH + 1, MSG_TRIES_TEXT, game_ctx->tries);
00374 wtk_label_change(game_ctx->tries_label, str);
00375 }
00376
00390 static void memgame_get_piece_coordinates(uint8_t position, gfx_coord_t *pos_x,
00391 gfx_coord_t *pos_y)
00392 {
00393 *pos_x = BOARD_POS_X + (position % NR_OF_BOARD_COLUMNS) *
00394 (PIECE_SIZE_X + PIECE_SPACING_X);
00395
00396 *pos_y = BOARD_POS_Y + (position / NR_OF_BOARD_COLUMNS) *
00397 (PIECE_SIZE_Y + PIECE_SPACING_Y);
00398 }
00399
00412 static void memgame_draw_piece(uint8_t index, bool show)
00413 {
00414 gfx_coord_t pos_x;
00415 gfx_coord_t pos_y;
00416 char *filename;
00417 enum status_code result;
00418
00419
00420 if (show) {
00421 switch (game_ctx->pieces[index].pair) {
00422 case 0:
00423 filename = PIECE_PAIR_FILE_0;
00424 break;
00425
00426 case 1:
00427 filename = PIECE_PAIR_FILE_1;
00428 break;
00429
00430 case 2:
00431 filename = PIECE_PAIR_FILE_2;
00432 break;
00433
00434 case 3:
00435 filename = PIECE_PAIR_FILE_3;
00436 break;
00437
00438 case 4:
00439 filename = PIECE_PAIR_FILE_4;
00440 break;
00441
00442 case 5:
00443 filename = PIECE_PAIR_FILE_5;
00444 break;
00445
00446 default:
00447 filename = PIECE_BACK_FILE;
00448 break;
00449 }
00450 } else {
00451 filename = PIECE_BACK_FILE;
00452 }
00453 memgame_get_piece_coordinates(index, &pos_x, &pos_y);
00454
00455
00456 result = load_file_to_screen(filename, pos_x, pos_y, PIECE_SIZE_X,
00457 PIECE_SIZE_Y, game_ctx->task);
00458
00459
00460
00461
00462 if (result != STATUS_OK) {
00463 win_destroy(game_ctx->win);
00464 memcpy(&sysfont, &game_ctx->old_sysfont, sizeof(struct font));
00465 membag_free(game_ctx);
00466 game_ctx = NULL;
00467 app_desktop_restart();
00468 };
00469 }
00470
00478 static uint8_t memgame_get_random_index(uint8_t indexes[])
00479 {
00480 uint8_t array_index;
00481 uint8_t piece_index;
00482
00483 piece_index = PIECE_INVALID_INDEX;
00484
00485
00486 do {
00487 array_index = memgame_rand() % NR_OF_PIECES;
00488 piece_index = indexes[array_index];
00489 } while (piece_index == PIECE_INVALID_INDEX);
00490
00491
00492 indexes[array_index] = PIECE_INVALID_INDEX;
00493
00494 return piece_index;
00495 }
00496
00504 static void memgame_start_new_game(void)
00505 {
00506 struct memgame_piece *p1;
00507 struct memgame_piece *p2;
00508 uint8_t indexes[NR_OF_PIECES];
00509 uint8_t i;
00510
00511
00512 win_redraw(game_ctx->win);
00513
00514
00515 for (i = 0; i < NR_OF_PIECES; i++) {
00516 indexes[i] = i;
00517 }
00518
00519
00520 for (i = 0; i < NR_OF_PIECE_PAIRS; i++) {
00521 p1 = &game_ctx->pieces[memgame_get_random_index(indexes)];
00522 p1->pair = i;
00523 p1->found = false;
00524
00525 p2 = &game_ctx->pieces[memgame_get_random_index(indexes)];
00526 p2->pair = i;
00527 p2->found = false;
00528 }
00529
00530
00531 game_ctx->tries = 0;
00532 game_ctx->busy = true;
00533 game_ctx->piece_1 = 0;
00534 game_ctx->pairs_left = NR_OF_PIECE_PAIRS;
00535 game_ctx->state = DRAW_ALL_PIECES;
00536 workqueue_add_task(&main_workqueue, game_ctx->task);
00537 }
00538
00555 static void memgame_update(uint8_t new_piece)
00556 {
00557 struct memgame_piece *pieces = game_ctx->pieces;
00558 uint8_t p_1 = game_ctx->piece_1;
00559 uint8_t p_2 = game_ctx->piece_2;
00560
00561
00562 switch(game_ctx->state) {
00563 case DRAW_ALL_PIECES:
00564
00565 if (p_1 < NR_OF_PIECES) {
00566 memgame_draw_piece(p_1, false);
00567 p_1++;
00568 } else {
00569 memgame_print_tries();
00570 game_ctx->state = SELECT_FIRST_PIECE;
00571 game_ctx->busy = false;
00572 }
00573 game_ctx->piece_1 = p_1;
00574 break;
00575
00576 case SELECT_FIRST_PIECE:
00577
00578 if (game_ctx->pieces[new_piece].found == false) {
00579 game_ctx->piece_1 = new_piece;
00580 memgame_draw_piece(new_piece, true);
00581 game_ctx->state = SHOWN_FIRST_PIECE;
00582 game_ctx->busy = true;
00583 }
00584 break;
00585
00586 case SHOWN_FIRST_PIECE:
00587
00588 game_ctx->state = SELECT_SECOND_PIECE;
00589 game_ctx->busy = false;
00590 break;
00591
00592 case SELECT_SECOND_PIECE:
00593
00594
00595
00596 if ((new_piece != p_1) && (game_ctx->pieces[new_piece].found
00597 == false)) {
00598 game_ctx->piece_2 = new_piece;
00599 memgame_draw_piece(new_piece, true);
00600 game_ctx->tries++;
00601 memgame_print_tries();
00602 game_ctx->state = SHOWN_SECOND_PIECE;
00603 game_ctx->busy = true;
00604 }
00605 break;
00606
00607 case SHOWN_SECOND_PIECE:
00608
00609 if (pieces[p_1].pair == pieces[p_2].pair) {
00610
00611 if (--game_ctx->pairs_left == 0) {
00612 memgame_print_game_over();
00613 game_ctx->state = GAME_OVER;
00614 } else {
00615 pieces[p_1].found = true;
00616 pieces[p_2].found = true;
00617 game_ctx->state = SELECT_FIRST_PIECE;
00618 }
00619 game_ctx->busy = false;
00620 } else {
00621
00622 game_ctx->ticks_to_go = TIMER_PAUSE_HALF_SECONDS;
00623 timer_start(CONFIG_TIMER_ID, &game_ctx->timer);
00624 timer_set_alarm(CONFIG_TIMER_ID, &game_ctx->timer,
00625 game_ctx->timer_delay);
00626 game_ctx->state = HIDE_FIRST_PIECE;
00627 }
00628 break;
00629
00630 case HIDE_FIRST_PIECE:
00631
00632 memgame_draw_piece(p_1, false);
00633 game_ctx->state = HIDE_SECOND_PIECE;
00634 break;
00635
00636 case HIDE_SECOND_PIECE:
00637
00638 memgame_draw_piece(p_2, false);
00639 game_ctx->state = HIDDEN_BOTH_PIECES;
00640 break;
00641
00642 case HIDDEN_BOTH_PIECES:
00643
00644 game_ctx->state = SELECT_FIRST_PIECE;
00645 game_ctx->busy = false;
00646 break;
00647
00648 case GAME_OVER:
00649
00650 break;
00651
00652 default:
00653 break;
00654 }
00655 }
00656
00665 static void memgame_handle_command_event(win_command_t data)
00666 {
00667 switch ((enum memgame_command_id)(uintptr_t)data) {
00668 case CMD_NEW_GAME:
00669 memgame_start_new_game();
00670 break;
00671
00672 case CMD_EXIT:
00673 win_destroy(game_ctx->win);
00674 memcpy(&sysfont, &game_ctx->old_sysfont, sizeof(struct font));
00675 membag_free(game_ctx);
00676 game_ctx = NULL;
00677 app_desktop_restart();
00678 break;
00679
00680 default:
00681 break;
00682 }
00683 }
00684
00694 static void memgame_handle_pointer_event(struct win_pointer_event const *data)
00695 {
00696 gfx_coord_t x;
00697 gfx_coord_t y;
00698 uint8_t i;
00699
00700
00701 if (data->type != WIN_POINTER_PRESS) {
00702 return;
00703 }
00704
00705
00706 for (i = 0; i < NR_OF_PIECES; i++) {
00707 memgame_get_piece_coordinates(i, &x, &y);
00708
00709
00710 if ((data->pos.x >= x) && (data->pos.y >= y) &&
00711 (data->pos.x < (x + PIECE_SIZE_X)) &&
00712 (data->pos.y < (y + PIECE_SIZE_Y))) {
00713 memgame_update(i);
00714 break;
00715 }
00716 }
00717 }
00718
00733 static bool memgame_window_handler(struct win_window *win,
00734 enum win_event_type type, void const *data)
00735 {
00736
00737 if (!game_ctx->busy) {
00738 switch (type) {
00739 case WIN_EVENT_COMMAND:
00740 memgame_handle_command_event((win_command_t)data);
00741 break;
00742
00743 case WIN_EVENT_POINTER:
00744 memgame_handle_pointer_event(
00745 (struct win_pointer_event const *)data);
00746 break;
00747
00748 default:
00749 break;
00750 }
00751 }
00752
00753 return true;
00754 }
00755
00765 static void memgame_worker(struct workqueue_task *task)
00766 {
00767 memgame_update(PIECE_INVALID_INDEX);
00768 }
00769
00778 static void memgame_timer_callback(struct timer *timer)
00779 {
00780 if (--game_ctx->ticks_to_go > 0) {
00781 timer_set_alarm(CONFIG_TIMER_ID, &game_ctx->timer,
00782 game_ctx->timer_delay);
00783 } else {
00784 timer_stop(CONFIG_TIMER_ID, &game_ctx->timer);
00785 workqueue_add_task(&main_workqueue, game_ctx->task);
00786 }
00787 }
00788
00800 void app_memgame_launch(struct workqueue_task *task)
00801 {
00802 struct win_attributes attr;
00803 struct win_window *win;
00804 struct wtk_button *button;
00805 struct wtk_label *label;
00806 struct timer *timer;
00807 timer_res_t timer_res;
00808 uint32_t timer_clk;
00809 gfx_coord_t width = gfx_get_width();
00810 gfx_coord_t height = gfx_get_height();
00811
00812
00813 #ifdef CONFIG_GFX_USE_CLIPPING
00814 gfx_set_clipping(0, 0, width, height);
00815 #endif
00816 gfx_draw_filled_rect(0, 0, width, height, BACKGROUND_COLOR);
00817
00818
00819 game_ctx = membag_alloc(sizeof(struct memgame_context));
00820 if(!game_ctx){
00821 goto exit_no_context;
00822 }
00823
00824 assert(task);
00825 workqueue_task_set_work_func(task, memgame_worker);
00826 game_ctx->task = task;
00827
00828
00829 memcpy(&game_ctx->old_sysfont, &sysfont, sizeof(struct font));
00830 sysfont.scale = WIDGET_FONT_SCALE;
00831
00832
00833 timer = &game_ctx->timer;
00834 timer_init(CONFIG_TIMER_ID, timer, memgame_timer_callback);
00835 timer_res = timer_set_resolution(CONFIG_TIMER_ID, timer,
00836 TIMER_CLOCK_RATE);
00837 timer_write_resolution(CONFIG_TIMER_ID, timer, timer_res);
00838 timer_clk = timer_get_resolution(CONFIG_TIMER_ID, timer, timer_res);
00839
00840 assert(timer_clk <= (uint32_t)0x1ffff);
00841 game_ctx->timer_delay = timer_clk / 2;
00842
00843
00844 game_ctx->bitmap.type = BITMAP_SOLID;
00845 game_ctx->bitmap.data.color = BACKGROUND_COLOR;
00846
00847
00848 attr.area.pos.x = 0;
00849 attr.area.pos.y = 0;
00850 attr.area.size.x = width;
00851 attr.area.size.y = height;
00852 attr.background = &game_ctx->bitmap;
00853 attr.event_handler = memgame_window_handler;
00854 win = win_create(win_get_root(), &attr);
00855 if (!win) {
00856 goto exit_no_window;
00857 }
00858 game_ctx->win = win;
00859
00860
00861 attr.area.pos.x = APP_EXIT_BUTTON_POS_X;
00862 attr.area.pos.y = APP_EXIT_BUTTON_POS_Y;
00863 attr.area.size.x = APP_EXIT_BUTTON_SIZE_X;
00864 attr.area.size.y = APP_EXIT_BUTTON_SIZE_Y;
00865 button = wtk_button_create(win, &attr.area, APP_EXIT_BUTTON_TEXT,
00866 (win_command_t)CMD_EXIT);
00867 if (!button) {
00868 goto exit_no_widget;
00869 }
00870 win_show(wtk_button_as_child(button));
00871
00872
00873 attr.area.pos.x = BTN_NEW_GAME_POS_X;
00874 attr.area.pos.y = BTN_NEW_GAME_POS_Y;
00875 attr.area.size.x = BTN_NEW_GAME_SIZE_X;
00876 attr.area.size.y = BTN_NEW_GAME_SIZE_Y;
00877 button = wtk_button_create(win, &attr.area, BTN_NEW_GAME_TEXT,
00878 (win_command_t)CMD_NEW_GAME);
00879 if (!button) {
00880 goto exit_no_widget;
00881 }
00882 win_show(wtk_button_as_child(button));
00883
00884
00885 attr.area.pos.x = MSG_TRIES_POS_X;
00886 attr.area.pos.y = MSG_TRIES_POS_Y;
00887 attr.area.size.x = MSG_TRIES_SIZE_X;
00888 attr.area.size.y = MSG_TRIES_SIZE_Y;
00889 label = wtk_label_create(win, &attr.area, " ", false);
00890 if (!label) {
00891 goto exit_no_widget;
00892 }
00893 game_ctx->tries_label = label;
00894 win_show(wtk_label_as_child(label));
00895
00896
00897 win_show(win);
00898 memgame_start_new_game();
00899 return;
00900
00901
00902 exit_no_widget:
00903 win_destroy(game_ctx->win);
00904 exit_no_window:
00905 memcpy(&sysfont, &game_ctx->old_sysfont, sizeof(struct font));
00906 membag_free(game_ctx);
00907 game_ctx = NULL;
00908 exit_no_context:
00909 app_desktop_restart();
00910 return;
00911 }
00912