Index: bindings.doc ================================================================== --- bindings.doc +++ bindings.doc @@ -74,10 +74,13 @@ restarting a level or when accessing a different level. '^I' Toggle the inventory/move-list display. +'^L' + List of levels. + '^M' Set the replay mark position. '^S' If the level is in a won state, record the solution. Index: config.doc ================================================================== --- config.doc +++ config.doc @@ -60,10 +60,19 @@ delay and then the key repeating interval. .level Specifies the 1-based order number of the level to start at. +.listColumns + Number of columns in the wide mode of the list of levels. If zero or not + specified, determines automatically by the screen width. + +.listMode + Mode for list of levels, in hexadecimal, where bit0 is set for tall mode + or clear for wide mode, and bit1 is set for listing the ID numbers or + clear for listing the order numbers. + .margin The X coordinate of the left margin. To the left is the status area, and to the right of the margin is the playfield. This should be at least 64, or the picture size, whichever is greater. Index: default.heromeshrc ================================================================== --- default.heromeshrc +++ default.heromeshrc @@ -106,10 +106,11 @@ ! Game key bindings ?.gameKey.ctrl.D: select '^d',$key_xy; ?.gameKey.ctrl.E: ^E ?.gameKey.ctrl.I: select 'mi',:import_move_list; +?.gameKey.ctrl.L: ^L ?.gameKey.ctrl.X: select 'mx',:export_move_list; ?.gameClick.right: ^o ?.gameClick.shift.right: ^n ?.gameClick.middle: ^d ?.gameKey.kp_enter: =0 Index: function.c ================================================================== --- function.c +++ function.c @@ -1252,10 +1252,11 @@ sqlite3_bind_int(st2,2,level_index[j]); while((i=sqlite3_step(st2))==SQLITE_ROW); if(i!=SQLITE_DONE) goto err; } sqlite3_finalize(st2); + sqlite3_exec(userdb,"CREATE UNIQUE INDEX `LEVELS_ORD` ON `LEVELS`(`ORD`) WHERE `ORD` NOT NULL;",0,0,0); if(!txn) sqlite3_exec(userdb,"COMMIT;",0,0,0); fprintf(stderr,"Done\n"); if(screen) set_cursor(XC_arrow); return SQLITE_SCHEMA; } Index: game.c ================================================================== --- game.c +++ game.c @@ -697,10 +697,192 @@ if(replay_sizew-16)/070; + if(i<1) i=j; + if(i>j) i=j; + if(i>255) i=255; + columns=i; + } + // ID, ORD, CODE, WIDTH, HEIGHT, TITLE, SOLVED, SOLVABLE, ... + if(sqlite3_prepare_v2(userdb,"SELECT * FROM `LEVELS` WHERE `ORD` NOT NULL AND `ORD` >= ?1 ORDER BY `ORD`;",-1,&st,0)) { + screen_message(sqlite3_errmsg(userdb)); + return 0; + } + set_cursor(XC_arrow); + redraw: + if(sel<0) sel=0; + if(sel>=level_nindex) sel=level_nindex-1; + SDL_FillRect(screen,0,0x02); + r.x=r.y=0; + r.w=screen->w; + r.h=24; + SDL_FillRect(screen,&r,0xF7); + SDL_LockSurface(screen); + draw_text(0,0," Select Title Play <0-9> Find Cancel",0xF7,0xF0); + draw_text(0,8," Wide/Tall ID/Ord",0xF7,0xF0); + sqlite3_reset(st); + if(mo&1) { + scrmax=level_nindex; + draw_text(16,16,mo&2?"\xB3 ID \xB3":"\xB3 ORD \xB3",0xF7,0xF1); + if(rescroll) { + if(sel=scroll+screen->h/8-3) scroll=sel+4-screen->h/8; + rescroll=0; + } + sqlite3_bind_int(st,1,scroll+1); + for(y=24;yh-7;y+=8) { + if(sqlite3_step(st)!=SQLITE_ROW) break; + i=sqlite3_column_int(st,1); + if(i-1==sel) { + r.x=16; r.y=y; + r.w=screen->w-16; r.h=8; + SDL_FillRect(screen,&r,0xF8); + draw_text(2*8,y,"\x10",b=0xF8,0xFE); + } else { + draw_text(2*8,y,"\xB3",b=0x02,0xF8); + } + snprintf(buf,6,"%5u",mo&2?sqlite3_column_int(st,0):i); + draw_text(3*8,y,buf,b,sqlite3_column_int(st,6)?0xFA:0xFC); + draw_text(8*8,y,"\xB3",b,b^0xFA); + } + } else { + scrmax=(level_nindex+columns-1)/columns; + draw_text(0,16,mo&2?"(ID)":"(Ord)",0xF7,0xF1); + if(rescroll) { + if(sel(scroll+screen->h/8-3)*columns) scroll=(sel+3-screen->h/8)/columns; + rescroll=0; + } + sqlite3_bind_int(st,1,scroll*columns+1); + for(y=24;yh-7;y+=8) for(x=0;xh-24; + scrollbar(&scroll,screen->h/8-3,scrmax,0,&r); + SDL_Flip(screen); + sqlite3_reset(st); + while(SDL_WaitEvent(&ev)) { + if(ev.type!=SDL_VIDEOEXPOSE && scrollbar(&scroll,screen->h/8-3,scrmax,&ev,&r)) goto redraw; + switch(ev.type) { + case SDL_MOUSEMOTION: + set_cursor(XC_arrow); + break; + case SDL_KEYDOWN: + if((ev.key.keysym.mod&KMOD_NUM) && ev.key.keysym.sym>=SDLK_KP0 && ev.key.keysym.sym<=SDLK_KP9) goto digit; + switch(ev.key.keysym.sym) { + case SDLK_ESCAPE: sqlite3_finalize(st); return 0; + //TODO: Change the scroll to approximately the middle, instead of zero + case SDLK_F1: scroll=0; mo^=1; rescroll=1; goto redraw; + case SDLK_F2: mo^=2; goto redraw; + case SDLK_SPACE: title: + sqlite3_reset(st); + sqlite3_bind_int(st,1,sel+1); + if(sqlite3_step(st)==SQLITE_ROW) { + v=sqlite3_column_text(st,5); // not Unicode, but add the null terminator + if(v) modal_draw_popup(v); + } + goto redraw; + case SDLK_0 ... SDLK_9: digit: + SDL_PushEvent(&ev); + if(v=screen_prompt("Find?")) { + if(!*v) goto redraw; + i=strtol(v,0,10); + if(mo&2) { + for(j=0;j0 && i<=level_nindex) sel=i-1; + } + rescroll=1; + } + goto redraw; + case SDLK_DOWN: case SDLK_KP2: case SDLK_j: + sel+=mo&1?:columns; + rescroll=1; goto redraw; + case SDLK_UP: case SDLK_KP8: case SDLK_k: + sel-=mo&1?:columns; + rescroll=1; goto redraw; + case SDLK_LEFT: case SDLK_KP4: case SDLK_h: + sel--; + rescroll=1; goto redraw; + case SDLK_RIGHT: case SDLK_KP6: case SDLK_l: + sel++; + rescroll=1; goto redraw; + case SDLK_PAGEUP: case SDLK_KP9: + sel-=(screen->h/8-3)*(mo&1?:columns); + scroll-=(screen->h/8-3)*(mo&1?:columns); + rescroll=1; goto redraw; + case SDLK_PAGEDOWN: case SDLK_KP3: + sel+=(screen->h/8-3)*(mo&1?:columns); + scroll+=(screen->h/8-3)*(mo&1?:columns); + rescroll=1; goto redraw; + case SDLK_HOME: case SDLK_KP7: sel=scroll=0; rescroll=1; goto redraw; + case SDLK_END: + sel=level_nindex-1; + rescroll=1; goto redraw; + case SDLK_RETURN: case SDLK_KP_ENTER: play: + sqlite3_finalize(st); + begin_level(~sel); + return 1; + } + break; + case SDL_MOUSEBUTTONDOWN: + if(ev.button.x>=16 && ev.button.y>=24) { + if(mo&1) i=scroll+ev.button.y/8-3; + else i=(scroll+ev.button.y/8-3)*columns+(ev.button.x-16)/070; + if(i>=0 && i