source: trunk/core/gui_sokoban.c @ 1323

Revision 1323, 14.3 KB checked in by philmoz, 3 years ago (diff)

Fix for game colors (from CHDK-DE changeset 781, thx msl).

  • 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//-------------------------------------------------------------------
15#define FIELD_WIDTH             15
16#define FIELD_HEIGHT            15
17
18#define WALL_COLOR_1            COLOR_GREY
19#define WALL_COLOR_2            COLOR_BLACK
20#define BOX_COLOR_1             COLOR_RED
21#define BOX_COLOR_2             COLOR_BLACK
22#define BOX_COLOR_3             COLOR_YELLOW
23#define PLACE_COLOR_1           COLOR_BLUE
24#define PLACE_COLOR_2           COLOR_BLACK
25#define PLAYER_COLOR_1          COLOR_GREEN
26#define PLAYER_COLOR_2          COLOR_BLACK
27
28#define MARKER_WALL             '#'
29#define MARKER_BOX              '$'
30#define MARKER_PLACE            '.'
31#define MARKER_BOX_PLACE        '*'
32#define MARKER_PLAYER           '@'
33#define MARKER_PLAYER_PLACE     '+'
34#define MARKER_EMPTY            '_' // was space
35#define MARKER_LINE_END        '\n' // was |
36#define MARKER_LEVEL_END        '!'
37
38#define LEVEL_CHARS "#$.*@+_"
39
40#define UNDO_SIZE               1000
41
42//-------------------------------------------------------------------
43static const char *level_file_name="A/CHDK/GAMES/SOKOBAN.LEV";
44#define MAX_LEVELS 200
45static unsigned short level_start_list[MAX_LEVELS];
46static unsigned char level_length_list[MAX_LEVELS];
47static unsigned num_levels;
48
49static int need_redraw;
50static int  moves;
51static char field[FIELD_HEIGHT][FIELD_WIDTH];
52
53static int cell_size;
54static int xPl, yPl;
55
56static int undo[UNDO_SIZE/10];
57static int undo_begin, undo_end, undo_curr;
58
59//-------------------------------------------------------------------
60static void sokoban_undo_add(int dx, int dy, int box) {
61    int offs, bits, value;
62
63    value = ((box)?1:0)<<2;
64    if (dx) {
65        value |= ((dx<0)?1:0)<<1;
66    } else {
67        value |= (((dy<0)?1:0)<<1)|1;
68    }
69   
70    offs = undo_curr/10;
71    bits = (undo_curr%10)*3;
72    undo[offs] &= ~(7<<bits);
73    undo[offs] |= (value&7)<<bits;
74
75    if (++undo_curr==UNDO_SIZE) undo_curr=0;
76    if (undo_curr==undo_begin) {
77        if (++undo_begin==UNDO_SIZE) undo_begin=0;
78    }
79    undo_end=undo_curr;
80}
81
82//-------------------------------------------------------------------
83static void sokoban_undo() {
84    int dx=0, dy=0, value;
85   
86    if (undo_curr!=undo_begin) {
87        if (undo_curr==0) undo_curr=UNDO_SIZE;
88        --undo_curr;
89       
90        value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
91        if (value&1) dy=1; else dx=1;
92        if (value&2) {dy=-dy; dx=-dx;}
93
94        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
95        if (value&4) {
96            field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
97            field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
98        }
99        xPl-=dx; yPl-=dy;
100        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
101        --moves;
102    }
103}
104
105//-------------------------------------------------------------------
106static void sokoban_redo() {
107    int dx=0, dy=0, value;
108   
109    if (undo_curr!=undo_end) {
110        value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
111        if (value&1) dy=1; else dx=1;
112        if (value&2) {dy=-dy; dx=-dx;}
113
114        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
115        xPl+=dx; yPl+=dy;
116        if (value&4) {
117            field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
118            field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
119        }
120        field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
121        ++moves;
122
123        ++undo_curr;
124        if (undo_curr==UNDO_SIZE) undo_curr=0;
125    }
126}
127
128//-------------------------------------------------------------------
129static void sokoban_undo_reset() {
130    undo_begin=undo_end=undo_curr=0;
131}
132
133//-------------------------------------------------------------------
134static void sokoban_set_level(int lvl) {
135    int x=0, y, w=0, h=0;
136    const char *p;
137    char *buf;
138    FILE *fd;   
139    int start,len;
140
141    len=level_length_list[lvl];
142    start=level_start_list[lvl];
143    fd=fopen(level_file_name,"rb");
144    if(!fd) {
145        num_levels=0;
146        return;
147    }
148
149    buf=malloc(len+1);
150    if(!buf) {
151        fclose(fd);
152        return;
153    }
154
155    if(fseek(fd,start,SEEK_SET) != 0) {
156        fclose(fd);
157        free(buf);
158        return;
159    }
160    fread(buf,1,len,fd);
161    buf[len]=0;
162    fclose(fd);
163
164    p=buf;
165
166    // determine dimensions
167    while (*p) {
168      if (*p==MARKER_LINE_END) {
169          ++h;
170          if (x>w) w=x;
171          x=0;
172      } else {
173          ++x;
174      }
175      ++p;
176    }
177    if (x>w) w=x;
178    h-=1; //the last line didn't previously have an end marker
179
180    // clear field
181    for (y=0; y<FIELD_HEIGHT; ++y)
182        for (x=0; x<FIELD_WIDTH; ++x)
183            field[y][x]=MARKER_EMPTY;
184   
185    // place maze at the center
186    p=buf;
187    for (y=(FIELD_HEIGHT-h)/2; y<FIELD_HEIGHT; ++y, ++p) {
188        for (x=(FIELD_WIDTH-w)/2; x<FIELD_WIDTH && *p && *p!=MARKER_LINE_END; ++x, ++p) {
189            field[y][x]=*p;
190            if (field[y][x] == MARKER_PLAYER || field[y][x] == MARKER_PLAYER_PLACE) {
191              xPl = x; yPl = y;
192            }
193        }
194        if (!*p || (*p == MARKER_LINE_END && !*(p+1))) break;
195    }
196   
197    free(buf);
198    conf.sokoban_level = lvl;
199    moves = 0;
200    sokoban_undo_reset();
201}
202
203//-------------------------------------------------------------------
204static int sokoban_finished() {
205    int x, y;
206
207    for (y=0; y<FIELD_HEIGHT; ++y)
208        for (x=0; x<FIELD_WIDTH; ++x)
209            if (field[y][x]==MARKER_BOX)
210                return 0;
211    return 1;
212}
213
214//-------------------------------------------------------------------
215static void sokoban_next_level() {
216    if (++conf.sokoban_level >= num_levels) conf.sokoban_level = 0;
217    sokoban_set_level(conf.sokoban_level);
218    need_redraw = 1;
219}
220
221//-------------------------------------------------------------------
222static int sokoban_move(int dx, int dy) {
223    switch (field[yPl+dy][xPl+dx]) {
224        case MARKER_WALL:
225            return 0;
226            break;
227        case MARKER_BOX:
228        case MARKER_BOX_PLACE:
229            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)
230                return 0;
231            break;
232        case MARKER_PLACE:
233        case MARKER_EMPTY:
234            break;
235    }
236    field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
237    xPl+=dx; yPl+=dy;
238    if (field[yPl][xPl]==MARKER_BOX || field[yPl][xPl]==MARKER_BOX_PLACE) {
239        field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
240        field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
241        sokoban_undo_add(dx, dy, 1);
242    } else {
243        sokoban_undo_add(dx, dy, 0);
244    }
245    field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
246    return 1;
247}
248
249//-------------------------------------------------------------------
250static void sokoban_draw_box(int x, int y, color cl) {
251    draw_filled_rect(x*cell_size, y*cell_size, x*cell_size+cell_size-1, y*cell_size+cell_size-1, cl);
252    draw_line(x*cell_size+2, y*cell_size, x*cell_size+2, y*cell_size+cell_size-1, cl);
253    draw_line(x*cell_size+cell_size-1-2, y*cell_size, x*cell_size+cell_size-1-2, y*cell_size+cell_size-1, cl);
254    draw_line(x*cell_size+2, y*cell_size+2, x*cell_size+cell_size-1-2, y*cell_size+2, cl);
255    draw_line(x*cell_size+2, y*cell_size+cell_size-1-2, x*cell_size+cell_size-1-2, y*cell_size+cell_size-1-2, cl);
256}
257
258//-------------------------------------------------------------------
259int gui_sokoban_init() {
260    /* first time through, load the file and make an index
261     if would could tell when the user left sokoban,
262     we could avoid this and malloc all the data structures
263     unfortunately, gui_mode gets set all over the place */
264    if(!num_levels) {
265        char *buf,*p;
266        FILE *fd;   
267        struct stat st;
268
269        if (stat((char *)level_file_name,&st) != 0 || st.st_size==0)
270            return 0;
271
272        fd=fopen(level_file_name,"rb");
273        if(!fd)
274            return 0;
275
276        buf=malloc(st.st_size+1);
277        if(!buf) {
278            fclose(fd);
279            return 0;
280        }
281
282        fread(buf,1,st.st_size,fd);
283        buf[st.st_size]=0;
284        fclose(fd);
285        p = buf;
286        do {
287            // skip to the first level char
288            p = strpbrk(p,LEVEL_CHARS);
289            // found a level char, store the start
290            if (p) {
291                unsigned pos = p - buf;
292                if ( pos > 65535 ) {
293                    break;
294                }
295                level_start_list[num_levels] = (unsigned short)pos;
296                p=strchr(p,MARKER_LEVEL_END);
297                // found the end char, store the end
298                if(p) {
299                    unsigned len = p - (buf + level_start_list[num_levels]);
300                    // bail on invalid level
301                    if ( len > 255 ) {
302                        break;
303                    }
304                    level_length_list[num_levels] = (unsigned char)len;
305                    ++num_levels;
306                }
307            }
308        } while(p && num_levels < MAX_LEVELS);
309        free(buf);
310    }
311    if(!num_levels) {
312        return 0;
313    }
314    else if(conf.sokoban_level >= num_levels) {
315        conf.sokoban_level = 0;
316    }
317    cell_size = screen_height/FIELD_HEIGHT;
318    sokoban_set_level(conf.sokoban_level);
319        // if the file is no longer readable, set_level will set this
320    if(!num_levels) {
321        return 0;
322    }
323    need_redraw = 1;
324    return 1;
325}
326
327//-------------------------------------------------------------------
328void gui_sokoban_kbd_process() {
329    switch (kbd_get_autoclicked_key()) {
330        case KEY_UP:
331            moves+=sokoban_move(0, -1);
332            need_redraw = 1;
333            break;
334        case KEY_DOWN:
335            moves+=sokoban_move(0, +1);
336            need_redraw = 1;
337            break;
338        case KEY_LEFT:
339            moves+=sokoban_move(-1, 0);
340            need_redraw = 1;
341            break;
342        case KEY_RIGHT:
343            moves+=sokoban_move(+1, 0);
344            need_redraw = 1;
345            break;
346        case KEY_SET:
347            if (moves == 0) {
348                sokoban_next_level();
349            }
350            break;
351        case KEY_ZOOM_OUT:
352            sokoban_undo();
353            need_redraw = 1;
354            break;
355        case KEY_ZOOM_IN:
356            sokoban_redo();
357            need_redraw = 1;
358            break;
359      #if CAM_HAS_ERASE_BUTTON
360        case KEY_ERASE:
361      #else
362        case KEY_DISPLAY:
363      #endif
364            sokoban_set_level(conf.sokoban_level);
365            need_redraw = 1;
366            break;
367      #if CAM_HAS_ERASE_BUTTON
368        case KEY_DISPLAY:
369            gui_mbox_init(LANG_MBOX_ABOUT_TITLE, (int)"SOKOBAN\n(c) GrAnd, 2007", MBOX_TEXT_CENTER, NULL);
370            need_redraw = 1;
371            break;
372     #endif
373    }
374}
375
376//-------------------------------------------------------------------
377void gui_sokoban_draw() {
378    int y, x;
379    static char str[16];
380
381    if (need_redraw) {
382        need_redraw = 0;
383        for (y=0; y<FIELD_HEIGHT; ++y) {
384            for (x=0; x<FIELD_WIDTH; ++x) {
385                switch (field[y][x]) {
386                    case MARKER_WALL:
387                        draw_filled_rect(x*cell_size, y*cell_size, x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(WALL_COLOR_1, WALL_COLOR_2));
388                        break;
389                    case MARKER_BOX:
390                        sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_1, BOX_COLOR_2));
391                        break;
392                    case MARKER_PLACE:
393                        draw_filled_rect(x*cell_size, y*cell_size, x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
394                        draw_filled_rect(x*cell_size+4, y*cell_size+4, x*cell_size+cell_size-1-4, y*cell_size+cell_size-1-4, MAKE_COLOR(PLACE_COLOR_1, PLACE_COLOR_2));
395                        break;
396                    case MARKER_BOX_PLACE:
397                        sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_3, BOX_COLOR_2));
398                        break;
399                    case MARKER_PLAYER:
400                    case MARKER_PLAYER_PLACE:
401                        draw_filled_rect(x*cell_size, y*cell_size, x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
402                        draw_filled_ellipse(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));
403                        break;
404                    case MARKER_EMPTY:
405                    default:
406                        draw_filled_rect(x*cell_size, y*cell_size, x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
407                        break;
408                }
409            }
410        }
411
412        draw_line(cell_size*FIELD_WIDTH, 0, cell_size*FIELD_WIDTH, screen_height-1, COLOR_WHITE);
413        draw_line(cell_size*FIELD_WIDTH+1, 0, cell_size*FIELD_WIDTH+1, screen_height-1, COLOR_BLACK);
414
415        //draw_filled_rect(cell_size*FIELD_WIDTH+2, 0, screen_width-1, 7, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
416        draw_filled_rect(cell_size*FIELD_WIDTH+2, 0, screen_width-1, screen_height-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
417
418        sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_LEVEL), conf.sokoban_level+1);
419        draw_string(cell_size*FIELD_WIDTH+2, 8, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
420        sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_MOVES), moves);
421        draw_string(cell_size*FIELD_WIDTH+2, 8+FONT_HEIGHT, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
422
423        //draw_filled_rect(cell_size*FIELD_WIDTH+2, 8+FONT_HEIGHT*2, screen_width-1, screen_height-1, MAKE_COLOR(SCREEN_COLOR, SCREEN_COLOR));
424
425        if (sokoban_finished()) {
426            gui_mbox_init(LANG_SOKOBAN_MSG_FINISH_TITLE, LANG_SOKOBAN_MSG_FINISH_TEXT, MBOX_TEXT_CENTER, NULL);
427            sokoban_next_level();
428        }
429    }
430
431    sprintf(str, "Batt:%3d%%", get_batt_perc());
432    draw_txt_string(screen_width/FONT_WIDTH-2-9, screen_height/FONT_HEIGHT-1, str, MAKE_COLOR(SCREEN_COLOR, COLOR_WHITE));
433}
434
Note: See TracBrowser for help on using the repository browser.