This source file includes following definitions.
- sokoban_undo_add
- sokoban_undo
- sokoban_redo
- sokoban_undo_reset
- sokoban_set_level
- sokoban_finished
- sokoban_next_level
- sokoban_move
- sokoban_draw_box
- gui_sokoban_init
- gui_sokoban_kbd_process
- gui_sokoban_draw
- gui_module_menu_kbd_process
- _run
- _module_loader
- _module_unloader
- _module_can_unload
- _module_exit_alt
1 #include "camera_info.h"
2 #include "keyboard.h"
3 #include "modes.h"
4 #include "lang.h"
5 #include "conf.h"
6 #include "gui.h"
7 #include "gui_draw.h"
8 #include "gui_lang.h"
9 #include "gui_batt.h"
10 #include "gui_mbox.h"
11
12 #include "module_def.h"
13 #include "simple_module.h"
14
15
16
17 typedef struct
18 {
19 int sokoban_level;
20 } SokobanConf;
21
22 SokobanConf sconf;
23
24 static ConfInfo conf_info[] = {
25 CONF_INFO( 1, sconf.sokoban_level, CONF_DEF_VALUE, i:0),
26 {0}
27 };
28
29 void gui_module_menu_kbd_process();
30 int gui_sokoban_kbd_process();
31 void gui_sokoban_draw();
32
33 gui_handler GUI_MODE_SOKOBAN =
34 { GUI_MODE_MODULE, gui_sokoban_draw, gui_sokoban_kbd_process, gui_module_menu_kbd_process, 0, GUI_MODE_FLAG_NODRAWRESTORE };
35
36
37 #define FIELD_WIDTH 15
38 #define FIELD_HEIGHT 15
39
40 #define WALL_COLOR_1 COLOR_GREY
41 #define WALL_COLOR_2 COLOR_BLACK
42 #define BOX_COLOR_1 COLOR_RED
43 #define BOX_COLOR_2 COLOR_BLACK
44 #define BOX_COLOR_3 COLOR_YELLOW
45 #define PLACE_COLOR_1 COLOR_BLUE
46 #define PLACE_COLOR_2 COLOR_BLACK
47 #define PLAYER_COLOR_1 COLOR_GREEN
48 #define PLAYER_COLOR_2 COLOR_BLACK
49
50 #define MARKER_WALL '#'
51 #define MARKER_BOX '$'
52 #define MARKER_PLACE '.'
53 #define MARKER_BOX_PLACE '*'
54 #define MARKER_PLAYER '@'
55 #define MARKER_PLAYER_PLACE '+'
56 #define MARKER_EMPTY '_'
57 #define MARKER_LINE_END '\n'
58 #define MARKER_LEVEL_END '!'
59
60 #define LEVEL_CHARS "#$.*@+_"
61
62 #define UNDO_SIZE 1000
63
64
65 static const char *level_file_name="A/CHDK/GAMES/SOKOBAN.LEV";
66 #define MAX_LEVELS 200
67 static unsigned short level_start_list[MAX_LEVELS];
68 static unsigned char level_length_list[MAX_LEVELS];
69 static int num_levels;
70
71 static int need_redraw;
72 static int need_redraw_all;
73 static int moves;
74 static char field[FIELD_HEIGHT][FIELD_WIDTH];
75
76 static int cell_size;
77 static int xPl, yPl;
78
79 static int undo[UNDO_SIZE/10];
80 static int undo_begin, undo_end, undo_curr;
81
82
83 static void sokoban_undo_add(int dx, int dy, int box) {
84 int offs, bits, value;
85
86 value = ((box)?1:0)<<2;
87 if (dx) {
88 value |= ((dx<0)?1:0)<<1;
89 } else {
90 value |= (((dy<0)?1:0)<<1)|1;
91 }
92
93 offs = undo_curr/10;
94 bits = (undo_curr%10)*3;
95 undo[offs] &= ~(7<<bits);
96 undo[offs] |= (value&7)<<bits;
97
98 if (++undo_curr==UNDO_SIZE) undo_curr=0;
99 if (undo_curr==undo_begin) {
100 if (++undo_begin==UNDO_SIZE) undo_begin=0;
101 }
102 undo_end=undo_curr;
103 }
104
105
106 static void sokoban_undo() {
107 int dx=0, dy=0, value;
108
109 if (undo_curr!=undo_begin) {
110 if (undo_curr==0) undo_curr=UNDO_SIZE;
111 --undo_curr;
112
113 value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
114 if (value&1) dy=1; else dx=1;
115 if (value&2) {dy=-dy; dx=-dx;}
116
117 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
118 if (value&4) {
119 field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
120 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
121 }
122 xPl-=dx; yPl-=dy;
123 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
124 --moves;
125 }
126 }
127
128
129 static void sokoban_redo() {
130 int dx=0, dy=0, value;
131
132 if (undo_curr!=undo_end) {
133 value = (undo[undo_curr/10]>>((undo_curr%10)*3))&7;
134 if (value&1) dy=1; else dx=1;
135 if (value&2) {dy=-dy; dx=-dx;}
136
137 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
138 xPl+=dx; yPl+=dy;
139 if (value&4) {
140 field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
141 field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
142 }
143 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
144 ++moves;
145
146 ++undo_curr;
147 if (undo_curr==UNDO_SIZE) undo_curr=0;
148 }
149 }
150
151
152 static void sokoban_undo_reset() {
153 undo_begin=undo_end=undo_curr=0;
154 }
155
156
157 static void sokoban_set_level(int lvl) {
158 int x=0, y, w=0, h=0;
159 const char *p;
160 char *buf;
161 FILE *fd;
162 int start,len;
163
164 len=level_length_list[lvl];
165 start=level_start_list[lvl];
166 fd=fopen(level_file_name,"rb");
167 if(!fd) {
168 num_levels=0;
169 return;
170 }
171
172 buf=malloc(len+1);
173 if(!buf) {
174 fclose(fd);
175 return;
176 }
177
178 if(fseek(fd,start,SEEK_SET) != 0) {
179 fclose(fd);
180 free(buf);
181 return;
182 }
183 fread(buf,1,len,fd);
184 buf[len]=0;
185 fclose(fd);
186
187 p=buf;
188
189
190 while (*p) {
191 if (*p==MARKER_LINE_END) {
192 ++h;
193 if (x>w) w=x;
194 x=0;
195 } else {
196 ++x;
197 }
198 ++p;
199 }
200 if (x>w) w=x;
201 h-=1;
202
203
204 for (y=0; y<FIELD_HEIGHT; ++y)
205 for (x=0; x<FIELD_WIDTH; ++x)
206 field[y][x]=MARKER_EMPTY;
207
208
209 p=buf;
210 for (y=(FIELD_HEIGHT-h)/2; y<FIELD_HEIGHT; ++y, ++p) {
211 for (x=(FIELD_WIDTH-w)/2; x<FIELD_WIDTH && *p && *p!=MARKER_LINE_END; ++x, ++p) {
212 field[y][x]=*p;
213 if (field[y][x] == MARKER_PLAYER || field[y][x] == MARKER_PLAYER_PLACE) {
214 xPl = x; yPl = y;
215 }
216 }
217 if (!*p || (*p == MARKER_LINE_END && !*(p+1))) break;
218 }
219
220 free(buf);
221 sconf.sokoban_level = lvl;
222 moves = 0;
223 sokoban_undo_reset();
224 }
225
226
227 static int sokoban_finished() {
228 int x, y;
229
230 for (y=0; y<FIELD_HEIGHT; ++y)
231 for (x=0; x<FIELD_WIDTH; ++x)
232 if (field[y][x]==MARKER_BOX)
233 return 0;
234 return 1;
235 }
236
237
238 static void sokoban_next_level() {
239 if (++sconf.sokoban_level >= num_levels) sconf.sokoban_level = 0;
240 sokoban_set_level(sconf.sokoban_level);
241 need_redraw_all = 1;
242 }
243
244
245 static int sokoban_move(int dx, int dy) {
246 switch (field[yPl+dy][xPl+dx]) {
247 case MARKER_WALL:
248 return 0;
249 break;
250 case MARKER_BOX:
251 case MARKER_BOX_PLACE:
252 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)
253 return 0;
254 break;
255 case MARKER_PLACE:
256 case MARKER_EMPTY:
257 break;
258 }
259 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLAYER_PLACE)?MARKER_PLACE:MARKER_EMPTY;
260 xPl+=dx; yPl+=dy;
261 if (field[yPl][xPl]==MARKER_BOX || field[yPl][xPl]==MARKER_BOX_PLACE) {
262 field[yPl][xPl]=(field[yPl][xPl]==MARKER_BOX_PLACE)?MARKER_PLACE:MARKER_EMPTY;
263 field[yPl+dy][xPl+dx]=(field[yPl+dy][xPl+dx]==MARKER_PLACE)?MARKER_BOX_PLACE:MARKER_BOX;
264 sokoban_undo_add(dx, dy, 1);
265 } else {
266 sokoban_undo_add(dx, dy, 0);
267 }
268 field[yPl][xPl]=(field[yPl][xPl]==MARKER_PLACE)?MARKER_PLAYER_PLACE:MARKER_PLAYER;
269 return 1;
270 }
271
272
273 static void sokoban_draw_box(int x, int y, twoColors cl) {
274 draw_rectangle(camera_screen.disp_left+x*cell_size, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1, y*cell_size+cell_size-1, cl, RECT_BORDER1|DRAW_FILLED);
275 draw_line(camera_screen.disp_left+x*cell_size+2, y*cell_size, camera_screen.disp_left+x*cell_size+2, y*cell_size+cell_size-1, FG_COLOR(cl));
276 draw_line(camera_screen.disp_left+x*cell_size+cell_size-1-2, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1-2, y*cell_size+cell_size-1, FG_COLOR(cl));
277 draw_line(camera_screen.disp_left+x*cell_size+2, y*cell_size+2, camera_screen.disp_left+x*cell_size+cell_size-1-2, y*cell_size+2, FG_COLOR(cl));
278 draw_line(camera_screen.disp_left+x*cell_size+2, y*cell_size+cell_size-1-2, camera_screen.disp_left+x*cell_size+cell_size-1-2, y*cell_size+cell_size-1-2, FG_COLOR(cl));
279 }
280
281
282 int gui_sokoban_init() {
283
284
285
286
287 if(!num_levels) {
288 char *buf,*p;
289 FILE *fd;
290 struct stat st;
291
292 if (stat((char *)level_file_name,&st) != 0 || st.st_size==0)
293 return 0;
294
295 fd=fopen(level_file_name,"rb");
296 if(!fd)
297 return 0;
298
299 buf=malloc(st.st_size+1);
300 if(!buf) {
301 fclose(fd);
302 return 0;
303 }
304
305 fread(buf,1,st.st_size,fd);
306 buf[st.st_size]=0;
307 fclose(fd);
308 p = buf;
309 do {
310
311 p = strpbrk(p,LEVEL_CHARS);
312
313 if (p) {
314 unsigned pos = p - buf;
315 if ( pos > 65535 ) {
316 break;
317 }
318 level_start_list[num_levels] = (unsigned short)pos;
319 p=strchr(p,MARKER_LEVEL_END);
320
321 if(p) {
322 unsigned len = p - (buf + level_start_list[num_levels]);
323
324 if ( len > 255 ) {
325 break;
326 }
327 level_length_list[num_levels] = (unsigned char)len;
328 ++num_levels;
329 }
330 }
331 } while(p && num_levels < MAX_LEVELS);
332 free(buf);
333 }
334 if(!num_levels) {
335 return 0;
336 }
337 else if(sconf.sokoban_level >= num_levels) {
338 sconf.sokoban_level = 0;
339 }
340 if (camera_screen.height*3 > camera_screen.width*2) {
341
342 cell_size = 8*camera_screen.height/(9*FIELD_HEIGHT);
343 }
344 else {
345 cell_size = camera_screen.height/FIELD_HEIGHT;
346 }
347 sokoban_set_level(sconf.sokoban_level);
348
349 if(!num_levels) {
350 return 0;
351 }
352 need_redraw_all = 1;
353
354 gui_set_mode(&GUI_MODE_SOKOBAN);
355 return 1;
356 }
357
358
359 int gui_sokoban_kbd_process() {
360 switch (kbd_get_autoclicked_key()) {
361 case KEY_UP:
362 moves+=sokoban_move(0, -1);
363 need_redraw = 1;
364 break;
365 case KEY_DOWN:
366 moves+=sokoban_move(0, +1);
367 need_redraw = 1;
368 break;
369 case KEY_LEFT:
370 moves+=sokoban_move(-1, 0);
371 need_redraw = 1;
372 break;
373 case KEY_RIGHT:
374 moves+=sokoban_move(+1, 0);
375 need_redraw = 1;
376 break;
377 case KEY_SET:
378 if (moves == 0) {
379 sokoban_next_level();
380 }
381 break;
382 case KEY_ZOOM_OUT:
383 sokoban_undo();
384 need_redraw = 1;
385 break;
386 case KEY_ZOOM_IN:
387 sokoban_redo();
388 need_redraw = 1;
389 break;
390 case KEY_ERASE:
391 case KEY_DISPLAY:
392 sokoban_set_level(sconf.sokoban_level);
393 need_redraw_all = 1;
394 break;
395 }
396 return 0;
397 }
398
399
400 void gui_sokoban_draw() {
401 int y, x;
402 static char str[16];
403
404 if (need_redraw_all) {
405 draw_rectangle(camera_screen.disp_left, 0, camera_screen.disp_right, camera_screen.height-1, MAKE_COLOR(COLOR_BLACK, COLOR_BLACK), RECT_BORDER0|DRAW_FILLED);
406 need_redraw_all = 0;
407 need_redraw = 1;
408 }
409
410 if (need_redraw) {
411 need_redraw = 0;
412 for (y=0; y<FIELD_HEIGHT; ++y) {
413 for (x=0; x<FIELD_WIDTH; ++x) {
414 switch (field[y][x]) {
415 case MARKER_WALL:
416 draw_rectangle(camera_screen.disp_left+x*cell_size, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(WALL_COLOR_1, WALL_COLOR_2), RECT_BORDER1|DRAW_FILLED);
417 break;
418 case MARKER_BOX:
419 sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_1, BOX_COLOR_2));
420 break;
421 case MARKER_PLACE:
422 draw_rectangle(camera_screen.disp_left+x*cell_size, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(COLOR_BLACK, COLOR_BLACK), RECT_BORDER0|DRAW_FILLED);
423 draw_rectangle(camera_screen.disp_left+x*cell_size+4, y*cell_size+4, camera_screen.disp_left+x*cell_size+cell_size-1-4, y*cell_size+cell_size-1-4, MAKE_COLOR(PLACE_COLOR_1, PLACE_COLOR_2), RECT_BORDER1|DRAW_FILLED);
424 break;
425 case MARKER_BOX_PLACE:
426 sokoban_draw_box(x, y, MAKE_COLOR(BOX_COLOR_3, BOX_COLOR_2));
427 break;
428 case MARKER_PLAYER:
429 case MARKER_PLAYER_PLACE:
430 draw_rectangle(camera_screen.disp_left+x*cell_size, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(COLOR_BLACK, COLOR_BLACK), RECT_BORDER0|DRAW_FILLED);
431 draw_ellipse(camera_screen.disp_left+x*cell_size+(cell_size>>1)-1, y*cell_size+(cell_size>>1)-1, (cell_size>>1)-3, (cell_size>>1)-3, PLAYER_COLOR_1, DRAW_FILLED);
432 break;
433 case MARKER_EMPTY:
434 default:
435 draw_rectangle(camera_screen.disp_left+x*cell_size, y*cell_size, camera_screen.disp_left+x*cell_size+cell_size-1, y*cell_size+cell_size-1, MAKE_COLOR(COLOR_BLACK, COLOR_BLACK), RECT_BORDER0|DRAW_FILLED);
436 break;
437 }
438 }
439 }
440
441 draw_line(camera_screen.disp_left+cell_size*FIELD_WIDTH, 0, camera_screen.disp_left+cell_size*FIELD_WIDTH, camera_screen.height-1, COLOR_WHITE);
442 draw_line(camera_screen.disp_left+cell_size*FIELD_WIDTH+1, 0, camera_screen.disp_left+cell_size*FIELD_WIDTH+1, camera_screen.height-1, COLOR_BLACK);
443
444 sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_LEVEL), sconf.sokoban_level+1);
445 draw_string(camera_screen.disp_left+cell_size*FIELD_WIDTH+2, 8, str, MAKE_COLOR(COLOR_BLACK, COLOR_WHITE));
446 sprintf(str, "%s: %-6d", lang_str(LANG_SOKOBAN_TEXT_MOVES), moves);
447 draw_string(camera_screen.disp_left+cell_size*FIELD_WIDTH+2, 8+FONT_HEIGHT, str, MAKE_COLOR(COLOR_BLACK, COLOR_WHITE));
448
449 if (sokoban_finished()) {
450 gui_mbox_init(LANG_SOKOBAN_MSG_FINISH_TITLE, LANG_SOKOBAN_MSG_FINISH_TEXT, MBOX_TEXT_CENTER, NULL);
451 sokoban_next_level();
452 }
453 }
454
455 sprintf(str, "Batt:%3d%%", get_batt_perc());
456 draw_string_justified(camera_screen.disp_left, camera_screen.height-FONT_HEIGHT,
457 str, MAKE_COLOR(COLOR_BLACK, COLOR_WHITE), 0, camera_screen.disp_width-FONT_WIDTH, TEXT_RIGHT);
458 }
459
460
461 static int running = 0;
462
463 void gui_module_menu_kbd_process()
464 {
465 running = 0;
466 gui_default_kbd_process_menu_btn();
467 }
468
469
470
471
472
473
474
475 int _run()
476 {
477 if (!camera_info.state.mode_play)
478 {
479 gui_mbox_init(LANG_MSG_INFO_TITLE, LANG_MSG_SWITCH_TO_PLAY_MODE, MBOX_FUNC_RESTORE|MBOX_TEXT_CENTER, 0);
480 }
481 else
482 {
483 running = 1;
484 gui_sokoban_init();
485 }
486
487 return 0;
488 }
489
490
491
492
493
494 int _module_loader( __attribute__ ((unused))unsigned int* chdk_export_list )
495 {
496 sconf.sokoban_level = 0;
497 config_restore(&conf_info[0], "A/CHDK/MODULES/CFG/sokoban.cfg", 0);
498 return 0;
499 }
500
501
502
503
504
505 int _module_unloader()
506 {
507 config_save(&conf_info[0], "A/CHDK/MODULES/CFG/sokoban.cfg", 99000);
508 return 0;
509 }
510
511 int _module_can_unload()
512 {
513 return running == 0;
514 }
515
516 int _module_exit_alt()
517 {
518 running = 0;
519 return 0;
520 }
521
522
523
524 libsimple_sym _librun =
525 {
526 {
527 0, _module_unloader, _module_can_unload, _module_exit_alt, _run
528 }
529 };
530
531 ModuleInfo _module_info =
532 {
533 MODULEINFO_V1_MAGICNUM,
534 sizeof(ModuleInfo),
535 SIMPLE_MODULE_VERSION,
536
537 ANY_CHDK_BRANCH, 0, OPT_ARCHITECTURE,
538 ANY_PLATFORM_ALLOWED,
539
540 -LANG_MENU_GAMES_SOKOBAN,
541 MTYPE_GAME,
542
543 &_librun.base,
544
545 ANY_VERSION,
546 CAM_SCREEN_VERSION,
547 ANY_VERSION,
548 ANY_VERSION,
549
550 0,
551 };
552
553