source: branches/reyalp-flt/core/gui_sokoban.c @ 1513

Revision 1513, 17.5 KB checked in by philmoz, 2 years ago (diff)

Cleanup of code for handling CHDK 'gui' mode.

  • Property svn:eol-style set to native
Line 
1#include "stdlib.h"
2#include "keyboard.h"
3#include "platform.h"
4#include "core.h"
5#include "lang.h"
6#include "conf.h"
7#include "gui.h"
8#include "gui_draw.h"
9#include "gui_lang.h"
10#include "gui_batt.h"
11#include "gui_mbox.h"
12#include "gui_sokoban.h"
13
14#include "module_load.h"
15
16void gui_module_menu_kbd_process();
17void gui_sokoban_kbd_process();
18void gui_sokoban_draw(int enforce_redraw);
19
20int *conf_sokoban_level;
21
22gui_handler GUI_MODE_SOKOBAN =
23    /*GUI_MODE_SOKOBAN*/    { GUI_MODE_MODULE,   gui_sokoban_draw,     gui_sokoban_kbd_process,    gui_module_menu_kbd_process, GUI_MODE_FLAG_NODRAWRESTORE, GUI_MODE_MAGICNUM };
24
25
26//-------------------------------------------------------------------
27#define FIELD_WIDTH             15
28#define FIELD_HEIGHT            15
29
30#define WALL_COLOR_1            COLOR_GREY
31#define WALL_COLOR_2            COLOR_BLACK
32#define BOX_COLOR_1             COLOR_RED
33#define BOX_COLOR_2             COLOR_BLACK
34#define BOX_COLOR_3             COLOR_YELLOW
35#define PLACE_COLOR_1           COLOR_BLUE
36#define PLACE_COLOR_2           COLOR_BLACK
37#define PLAYER_COLOR_1          COLOR_GREEN
38#define PLAYER_COLOR_2          COLOR_BLACK
39
40#define MARKER_WALL             '#'
41#define MARKER_BOX              '$'
42#define MARKER_PLACE            '.'
43#define MARKER_BOX_PLACE        '*'
44#define MARKER_PLAYER           '@'
45#define MARKER_PLAYER_PLACE     '+'
46#define MARKER_EMPTY            '_' // was space
47#define MARKER_LINE_END        '\n' // was |
48#define MARKER_LEVEL_END        '!'
49
50#define LEVEL_CHARS "#$.*@+_"
51
52#define UNDO_SIZE               1000
53
54//-------------------------------------------------------------------
55static const char *level_file_name="A/CHDK/GAMES/SOKOBAN.LEV";
56#define MAX_LEVELS 200
57static unsigned short level_start_list[MAX_LEVELS];
58static unsigned char level_length_list[MAX_LEVELS];
59static unsigned num_levels;
60
61static int need_redraw;
62static int need_redraw_all;
63static int  moves;
64static char field[FIELD_HEIGHT][FIELD_WIDTH];
65
66static int cell_size;
67static int xPl, yPl;
68
69static int undo[UNDO_SIZE/10];
70static int undo_begin, undo_end, undo_curr;
71
72//-------------------------------------------------------------------
73static void sokoban_undo_add(int dx, int dy, int box) {
74    int offs, bits, value;
75
76    value = ((box)?1:0)<<2;
77    if (dx) {
78        value |= ((dx<0)?1:0)<<1;
79    } else {
80        value |= (((dy<0)?1:0)<<1)|1;
81    }
82   
83    offs = undo_curr/10;
84    bits = (undo_curr%10)*3;
85    undo[offs] &= ~(7<<bits);
86    undo[offs] |= (value&7)<<bits;
87
88    if (++undo_curr==UNDO_SIZE) undo_curr=0;
89    if (undo_curr==undo_begin) {
90        if (++undo_begin==UNDO_SIZE) undo_begin=0;
91    }
92    undo_end=undo_curr;
93}
94
95//-------------------------------------------------------------------
96static void sokoban_undo() {
97    int dx=0, dy=0, value;
98   
99    if (undo_curr!=undo_begin) {
100        if (undo_curr==0) undo_curr=UNDO_SIZE;
101        --undo_curr;
102       
103        value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
104        if (value&1) dy=1; else dx=1;
105        if (value&2) {dy=-dy; dx=-dx;}
106
107        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
108        if (value&4) {
109            field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
110            field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
111        }
112        xPl-=dx; yPl-=dy;
113        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
114        --moves;
115    }
116}
117
118//-------------------------------------------------------------------
119static void sokoban_redo() {
120    int dx=0, dy=0, value;
121   
122    if (undo_curr!=undo_end) {
123        value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
124        if (value&1) dy=1; else dx=1;
125        if (value&2) {dy=-dy; dx=-dx;}
126
127        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
128        xPl+=dx; yPl+=dy;
129        if (value&4) {
130            field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
131            field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
132        }
133        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
134        ++moves;
135
136        ++undo_curr;
137        if (undo_curr==UNDO_SIZE) undo_curr=0;
138    }
139}
140
141//-------------------------------------------------------------------
142static void sokoban_undo_reset() {
143    undo_begin=undo_end=undo_curr=0;
144}
145
146//-------------------------------------------------------------------
147static void sokoban_set_level(int lvl) {
148    int x=0, y, w=0, h=0;
149    const char *p;
150    char *buf;
151    FILE *fd;   
152    int start,len;
153
154    len=level_length_list[lvl];
155    start=level_start_list[lvl];
156    fd=fopen(level_file_name,"rb");
157    if(!fd) {
158        num_levels=0;
159        return;
160    }
161
162    buf=malloc(len+1);
163    if(!buf) {
164        fclose(fd);
165        return;
166    }
167
168    if(fseek(fd,start,SEEK_SET) != 0) {
169        fclose(fd);
170        free(buf);
171        return;
172    }
173    fread(buf,1,len,fd);
174    buf[len]=0;
175    fclose(fd);
176
177    p=buf;
178
179    // determine dimensions
180    while (*p) {
181      if (*p==MARKER_LINE_END) {
182          ++h;
183          if (x>w) w=x;
184          x=0;
185      } else {
186          ++x;
187      }
188      ++p;
189    }
190    if (x>w) w=x;
191    h-=1; //the last line didn't previously have an end marker
192
193    // clear field
194    for (y=0; y<FIELD_HEIGHT; ++y)
195        for (x=0; x<FIELD_WIDTH; ++x)
196            field[y][x]=MARKER_EMPTY;
197   
198    // place maze at the center
199    p=buf;
200    for (y=(FIELD_HEIGHT-h)/2; y<FIELD_HEIGHT; ++y, ++p) {
201        for (x=(FIELD_WIDTH-w)/2; x<FIELD_WIDTH && *p && *p!=MARKER_LINE_END; ++x, ++p) {
202            field[y][x]=*p;
203            if (field[y][x] == MARKER_PLAYER || field[y][x] == MARKER_PLAYER_PLACE) {
204              xPl = x; yPl = y;
205            }
206        }
207        if (!*p || (*p == MARKER_LINE_END && !*(p+1))) break;
208    }
209   
210    free(buf);
211    *conf_sokoban_level = lvl;
212    moves = 0;
213    sokoban_undo_reset();
214}
215
216//-------------------------------------------------------------------
217static int sokoban_finished() {
218    int x, y;
219
220    for (y=0; y<FIELD_HEIGHT; ++y)
221        for (x=0; x<FIELD_WIDTH; ++x)
222            if (field[y][x]==MARKER_BOX)
223                return 0;
224    return 1;
225}
226
227//-------------------------------------------------------------------
228static void sokoban_next_level() {
229    if (++*conf_sokoban_level >= num_levels) *conf_sokoban_level = 0;
230    sokoban_set_level(*conf_sokoban_level);
231    need_redraw_all = 1;
232}
233
234//-------------------------------------------------------------------
235static int sokoban_move(int dx, int dy) {
236    switch (field[yPl+dy][xPl+dx]) {
237        case MARKER_WALL:
238            return 0;
239            break;
240        case MARKER_BOX:
241        case MARKER_BOX_PLACE:
242            if (field[yPl+dy*2][xPl+dx*2]==MARKER_WALL || field[yPl+dy*2][xPl+dx*2]==MARKER_BOX || field[yPl+dy*2][xPl+dx*2]==MARKER_BOX_PLACE)
243                return 0;
244            break;
245        case MARKER_PLACE:
246        case MARKER_EMPTY:
247            break;
248    }
249    field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
250    xPl+=dx; yPl+=dy;
251    if (field[yPl][xPl]==MARKER_BOX || field[yPl][xPl]==MARKER_BOX_PLACE) {
252        field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
253        field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
254        sokoban_undo_add(dx, dy, 1);
255    } else {
256        sokoban_undo_add(dx, dy, 0);
257    }
258    field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
259    return 1;
260}
261
262//-------------------------------------------------------------------
263static void sokoban_draw_box(int x, int y, color cl) {
264    draw_filled_rect(camera_info.ts_button_border+x*cell_size, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1, y*cell_size+cell_size-1, cl);
265    draw_line(camera_info.ts_button_border+x*cell_size+2, y*cell_size, camera_info.ts_button_border+x*cell_size+2, y*cell_size+cell_size-1, cl);
266    draw_line(camera_info.ts_button_border+x*cell_size+cell_size-1-2, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1-2, y*cell_size+cell_size-1, cl);
267    draw_line(camera_info.ts_button_border+x*cell_size+2, y*cell_size+2, camera_info.ts_button_border+x*cell_size+cell_size-1-2, y*cell_size+2, cl);
268    draw_line(camera_info.ts_button_border+x*cell_size+2, y*cell_size+cell_size-1-2, camera_info.ts_button_border+x*cell_size+cell_size-1-2, y*cell_size+cell_size-1-2, cl);
269}
270
271//-------------------------------------------------------------------
272int gui_sokoban_init() {
273    /* first time through, load the file and make an index
274     if would could tell when the user left sokoban,
275     we could avoid this and malloc all the data structures
276     unfortunately, gui_mode gets set all over the place */
277    if(!num_levels) {
278        char *buf,*p;
279        FILE *fd;   
280        struct STD_stat st;
281
282        if (safe_stat((char *)level_file_name,&st) != 0 || st.st_size==0)
283            return 0;
284
285        fd=fopen(level_file_name,"rb");
286        if(!fd)
287            return 0;
288
289        buf=malloc(st.st_size+1);
290        if(!buf) {
291            fclose(fd);
292            return 0;
293        }
294
295        fread(buf,1,st.st_size,fd);
296        buf[st.st_size]=0;
297        fclose(fd);
298        p = buf;
299        do {
300            // skip to the first level char
301            p = strpbrk(p,LEVEL_CHARS);
302            // found a level char, store the start
303            if (p) {
304                unsigned pos = p - buf;
305                if ( pos > 65535 ) {
306                    break;
307                }
308                level_start_list[num_levels] = (unsigned short)pos;
309                p=strchr(p,MARKER_LEVEL_END);
310                // found the end char, store the end
311                if(p) {
312                    unsigned len = p - (buf + level_start_list[num_levels]);
313                    // bail on invalid level
314                    if ( len > 255 ) {
315                        break;
316                    }
317                    level_length_list[num_levels] = (unsigned char)len;
318                    ++num_levels;
319                }
320            }
321        } while(p && num_levels < MAX_LEVELS);
322        free(buf);
323    }
324    if(!num_levels) {
325        return 0;
326    }
327    else if(*conf_sokoban_level >= num_levels) {
328        *conf_sokoban_level = 0;
329    }
330    cell_size = screen_height/FIELD_HEIGHT;
331    sokoban_set_level(*conf_sokoban_level);
332        // if the file is no longer readable, set_level will set this
333    if(!num_levels) {
334        return 0;
335    }
336    need_redraw_all = 1;
337
338    gui_set_mode(&GUI_MODE_SOKOBAN);
339    return 1;
340}
341
342//-------------------------------------------------------------------
343void gui_sokoban_kbd_process() {
344    switch (kbd_get_autoclicked_key()) {
345        case KEY_UP:
346            moves+=sokoban_move(0, -1);
347            need_redraw = 1;
348            break;
349        case KEY_DOWN:
350            moves+=sokoban_move(0, +1);
351            need_redraw = 1;
352            break;
353        case KEY_LEFT:
354            moves+=sokoban_move(-1, 0);
355            need_redraw = 1;
356            break;
357        case KEY_RIGHT:
358            moves+=sokoban_move(+1, 0);
359            need_redraw = 1;
360            break;
361        case KEY_SET:
362            if (moves == 0) {
363                sokoban_next_level();
364            }
365            break;
366        case KEY_ZOOM_OUT:
367            sokoban_undo();
368            need_redraw = 1;
369            break;
370        case KEY_ZOOM_IN:
371            sokoban_redo();
372            need_redraw = 1;
373            break;
374        case KEY_ERASE:
375        case KEY_DISPLAY:
376            sokoban_set_level(*conf_sokoban_level);
377            need_redraw_all = 1;
378            break;
379    }
380}
381
382//-------------------------------------------------------------------
383void gui_sokoban_draw(int enforce_redraw) {
384    int y, x;
385    static char str[16];
386
387    if (need_redraw_all) {
388        draw_filled_rect(0, 0, screen_width-1, screen_height-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
389        need_redraw_all = 0;
390        need_redraw = 1;
391    }
392
393    if (need_redraw) {
394        need_redraw = 0;
395        for (y=0; y<FIELD_HEIGHT; ++y) {
396            for (x=0; x<FIELD_WIDTH; ++x) {
397                switch (field[y][x]) {
398                    case MARKER_WALL:
399                        draw_filled_rect(camera_info.ts_button_border+x*cell_size, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(WALL_COLOR_1, WALL_COLOR_2));
400                        break;
401                    case MARKER_BOX:
402                        sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_1, BOX_COLOR_2));
403                        break;
404                    case MARKER_PLACE:
405                        draw_filled_rect(camera_info.ts_button_border+x*cell_size, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
406                        draw_filled_rect(camera_info.ts_button_border+x*cell_size+4, y*cell_size+4, camera_info.ts_button_border+x*cell_size+cell_size-1-4, y*cell_size+cell_size-1-4, MAKE_COLOR(PLACE_COLOR_1, PLACE_COLOR_2));
407                        break;
408                    case MARKER_BOX_PLACE:
409                        sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_3, BOX_COLOR_2));
410                        break;
411                    case MARKER_PLAYER:
412                    case MARKER_PLAYER_PLACE:
413                        draw_filled_rect(camera_info.ts_button_border+x*cell_size, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
414                        draw_filled_ellipse(camera_info.ts_button_border+x*cell_size+(cell_size>>1)-1, y*cell_size+(cell_size>>1)-1, (cell_size>>1)-3, (cell_size>>1)-3, MAKE_COLOR(PLAYER_COLOR_1, PLAYER_COLOR_2));
415                        break;
416                    case MARKER_EMPTY:
417                    default:
418                        draw_filled_rect(camera_info.ts_button_border+x*cell_size, y*cell_size, camera_info.ts_button_border+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
419                        break;
420                }
421            }
422        }
423
424        draw_line(camera_info.ts_button_border+cell_size*FIELD_WIDTH, 0, camera_info.ts_button_border+cell_size*FIELD_WIDTH, screen_height-1, COLOR_WHITE);
425        draw_line(camera_info.ts_button_border+cell_size*FIELD_WIDTH+1, 0, camera_info.ts_button_border+cell_size*FIELD_WIDTH+1, screen_height-1, COLOR_BLACK);
426
427        sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_LEVEL), *conf_sokoban_level+1);
428        draw_string(camera_info.ts_button_border+cell_size*FIELD_WIDTH+2, 8, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
429        sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_MOVES), moves);
430        draw_string(camera_info.ts_button_border+cell_size*FIELD_WIDTH+2, 8+FONT_HEIGHT, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
431
432        //draw_filled_rect(cell_size*FIELD_WIDTH+2, 8+FONT_HEIGHT*2, screen_width-1, screen_height-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
433
434        if (sokoban_finished()) {
435            gui_mbox_init(LANG_SOKOBAN_MSG_FINISH_TITLE, LANG_SOKOBAN_MSG_FINISH_TEXT, MBOX_TEXT_CENTER, NULL);
436            sokoban_next_level();
437        }
438    }
439
440    sprintf(str, "Batt:%3d%%", get_batt_perc());
441    draw_txt_string((screen_width-camera_info.ts_button_border)/FONT_WIDTH-2-9, screen_height/FONT_HEIGHT-1, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
442}
443
444
445extern int module_idx;
446void gui_module_menu_kbd_process() {
447        gui_default_kbd_process_menu_btn();
448        module_async_unload(module_idx);
449}
450
451
452// =========  MODULE INIT =================
453#include "module_load.h"
454int module_idx=-1;
455
456/***************** BEGIN OF AUXILARY PART *********************
457  ATTENTION: DO NOT REMOVE OR CHANGE SIGNATURES IN THIS SECTION
458 **************************************************************/
459
460void* MODULE_EXPORT_LIST[] = {
461        /* 0 */ (void*)EXPORTLIST_MAGIC_NUMBER,
462        /* 1 */ (void*)0
463                };
464
465
466//---------------------------------------------------------
467// PURPOSE:   Bind module symbols with chdk.
468//              Required function
469// PARAMETERS: pointer to chdk list of export
470// RETURN VALUE: 1 error, 0 ok
471//---------------------------------------------------------
472int _module_loader( void** chdk_export_list )
473{
474  if ( (unsigned int)chdk_export_list[0] != EXPORTLIST_MAGIC_NUMBER )
475     return 1;
476
477  // Safe bind of conf.
478  tConfigVal configVal;
479  CONF_BIND_INT(40, conf_sokoban_level );
480
481  return 0;
482}
483
484
485
486//---------------------------------------------------------
487// PURPOSE: Finalize module operations (close allocs, etc)
488// RETURN VALUE: 0-ok, 1-fail
489//---------------------------------------------------------
490int _module_unloader()
491{
492  return 0;
493}
494
495
496//---------------------------------------------------------
497// PURPOSE: Default action for simple modules (direct run)
498// NOTE: Please comment this function if no default action and this library module
499//---------------------------------------------------------
500int _module_run(int moduleidx, int argn, int* arguments)
501{
502  module_idx=moduleidx;
503
504  int rv = gui_sokoban_init();
505  if ( ! rv )
506        module_async_unload(moduleidx);         // fail to init - "unload me"
507
508  return 0;
509}
510
511
512/******************** Module Information structure ******************/
513
514struct ModuleInfo _module_info = {      MODULEINFO_V1_MAGICNUM,
515                                                                        sizeof(struct ModuleInfo),
516
517                                                                        ANY_CHDK_BRANCH, 0,                     // Requirements of CHDK version
518                                                                        ANY_PLATFORM_ALLOWED,           // Specify platform dependency
519                                                                        0,                                                      // flag
520                                                                        -LANG_MENU_GAMES_SOKOBAN,       // Module name
521                                                                        1, 0,                                           // Module version
522                                                                        (int32_t)"Game"
523                                                                 };
524
525/*************** END OF AUXILARY PART *******************/
Note: See TracBrowser for help on using the repository browser.