Overview
Comment: | Implement list of levels (currently the tall mode lacks details; this will be added in future). |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
2b9a22d54116efa18aa0d32846c62c30 |
User & Date: | user on 2021-12-17 03:01:04 |
Other Links: | manifest | tags |
Context
2021-12-21
| ||
05:32 | Improvements to list of levels. check-in: de3c1465df user: user tags: trunk | |
2021-12-17
| ||
03:01 | Implement list of levels (currently the tall mode lacks details; this will be added in future). check-in: 2b9a22d541 user: user tags: trunk | |
2021-12-15
| ||
07:12 | Auto-update the LEVELS table when it is appropriate to do so. check-in: 656b937843 user: user tags: trunk | |
Changes
Modified bindings.doc from [ab94da47f8] to [d9974709a6].
︙ | ︙ | |||
72 73 74 75 76 77 78 79 80 81 82 83 84 85 | '^+' Toggle insertion mode. Insertion mode is automatically reset when restarting a level or when accessing a different level. '^I' Toggle the inventory/move-list display. '^M' Set the replay mark position. '^S' If the level is in a won state, record the solution. '^T' | > > > | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | '^+' Toggle insertion mode. Insertion mode is automatically reset when 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. '^T' |
︙ | ︙ |
Modified config.doc from [f22e7e5fbd] to [834613bb18].
︙ | ︙ | |||
58 59 60 61 62 63 64 65 66 67 68 69 70 71 | .keyRepeat The keyboard repeat rate, which are two numbers, first the key repeating delay and then the key repeating interval. .level Specifies the 1-based order number of the level to start at. .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. .maxTrigger If nonzero, the maximum number of times that the trigger phase may loop | > > > > > > > > > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | .keyRepeat The keyboard repeat rate, which are two numbers, first the key repeating 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. .maxTrigger If nonzero, the maximum number of times that the trigger phase may loop |
︙ | ︙ |
Modified default.heromeshrc from [cf39f14a84] to [f54d91daf2].
︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 | ?.gameKey.alt.T: 'TAB ?.gameKey.alt.Z: 'CTRL ! Game key bindings ?.gameKey.ctrl.D: select '^d',$key_xy; ?.gameKey.ctrl.E: ^E ?.gameKey.ctrl.I: select 'mi',:import_move_list; ?.gameKey.ctrl.X: select 'mx',:export_move_list; ?.gameClick.right: ^o ?.gameClick.shift.right: ^n ?.gameClick.middle: ^d ?.gameKey.kp_enter: =0 ?.gameKey.escape: =0 ?.gameKey.f1: +1 | > | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | ?.gameKey.alt.T: 'TAB ?.gameKey.alt.Z: 'CTRL ! 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 ?.gameKey.escape: =0 ?.gameKey.f1: +1 |
︙ | ︙ |
Modified function.c from [a630f4aa7d] to [29a1dbc515].
︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | sqlite3_reset(st2); sqlite3_bind_int(st2,1,j+1); sqlite3_bind_int(st2,2,level_index[j]); while((i=sqlite3_step(st2))==SQLITE_ROW); if(i!=SQLITE_DONE) goto err; } sqlite3_finalize(st2); if(!txn) sqlite3_exec(userdb,"COMMIT;",0,0,0); fprintf(stderr,"Done\n"); if(screen) set_cursor(XC_arrow); return SQLITE_SCHEMA; } Module(vt_levels, | > | 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 | sqlite3_reset(st2); sqlite3_bind_int(st2,1,j+1); 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; } Module(vt_levels, |
︙ | ︙ |
Modified game.c from [5006bb9149] to [853adbf6a3].
︙ | ︙ | |||
695 696 697 698 699 700 701 702 703 704 705 706 707 708 | int i=sqlite3_column_bytes(st,1); if(i&~0xFFFF) return; if(replay_size<i) replay_list=realloc(replay_list,replay_size=i); if(!replay_list) fatal("Allocation failed"); replay_count=i; if(i) memcpy(replay_list,sqlite3_column_blob(st,1),i); } static int game_command(int prev,int cmd,int number,int argc,sqlite3_stmt*args,void*aux) { switch(cmd) { case '\' ': // Play a move if(replay_time) { replay_time=0; return -3; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 | int i=sqlite3_column_bytes(st,1); if(i&~0xFFFF) return; if(replay_size<i) replay_list=realloc(replay_list,replay_size=i); if(!replay_list) fatal("Allocation failed"); replay_count=i; if(i) memcpy(replay_list,sqlite3_column_blob(st,1),i); } static int list_levels(void) { static Sint8 mo=-1; static Uint8 columns=0; static int scroll=0; sqlite3_stmt*st; SDL_Event ev; SDL_Rect r; const char*v; int scrmax=0; int sel=level_ord-1; int b,i,j,x,y; char buf[256]; char rescroll=1; if(mo<0) { optionquery[1]=Q_listMode; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; mo=strtol(v,0,16); if(mo<0) mo=0; } if(!columns) { optionquery[1]=Q_listColumns; v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:""; i=strtol(v,0,10); j=(screen->w-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,"<LMB/\x18\x19\x1A\x1B> Select <MMB/SP> Title <RMB/RET> Play <0-9> Find <ESC> Cancel",0xF7,0xF0); draw_text(0,8,"<F1> Wide/Tall <F2> 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) scroll=sel; 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;y<screen->h-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*columns) scroll=sel/columns; 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;y<screen->h-7;y+=8) for(x=0;x<columns;x++) { if(sqlite3_step(st)!=SQLITE_ROW) goto done; i=sqlite3_column_int(st,1); if(i-1==sel) { draw_text(x*070+16,y,"\x10",b=0xF8,0xFE); snprintf(buf,128,"[ ID=%u Ord=%u Code=%u Size=%ux%u ]",sqlite3_column_int(st,0),i,sqlite3_column_int(st,2),sqlite3_column_int(st,3),sqlite3_column_int(st,4)); draw_text(80,16,buf,0xF7,0xFE); } else { draw_text(x*070+16,y," ",b=0x02,0x02); } snprintf(buf,7,"%5u ",mo&2?sqlite3_column_int(st,0):i); draw_text(x*070+24,y,buf,b,sqlite3_column_int(st,6)?0xFA:0xFC); if(!sqlite3_column_int(st,7)) draw_text(x*070+64,y,"\a",b,i-1==sel?0xF1:0xF9); } } done: SDL_UnlockSurface(screen); r.x=r.w=0; r.y=24; r.h=screen->h-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;j<level_nindex;j++) if(level_index[j]==i) sel=j; } else { if(i>0 && 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<level_nindex) sel=i; if(ev.button.button==2) goto title; if(ev.button.button==3) goto play; goto redraw; } break; case SDL_VIDEOEXPOSE: goto redraw; case SDL_QUIT: exit(0); break; } } sqlite3_finalize(st); return -1; } static int game_command(int prev,int cmd,int number,int argc,sqlite3_stmt*args,void*aux) { switch(cmd) { case '\' ': // Play a move if(replay_time) { replay_time=0; return -3; |
︙ | ︙ | |||
769 770 771 772 773 774 775 776 777 778 779 780 781 782 | inserting^=1; return 0; case '^E': // Edit return main_options['r']?1:-2; case '^I': // Toggle inventory display side_mode^=1; return prev; case '^M': // Mark replay position replay_mark=replay_pos+inputs_count; return prev; case '^Q': // Quit return -1; case '^S': // Save solution if(gameover==1) record_solution(); | > > | 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 | inserting^=1; return 0; case '^E': // Edit return main_options['r']?1:-2; case '^I': // Toggle inventory display side_mode^=1; return prev; case '^L': // List levels return list_levels(); case '^M': // Mark replay position replay_mark=replay_pos+inputs_count; return prev; case '^Q': // Quit return -1; case '^S': // Save solution if(gameover==1) record_solution(); |
︙ | ︙ |
Modified game.doc from [9d856ff616] to [772a7e8517].
︙ | ︙ | |||
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | IMPORTANT NOTE: Saving the solutions here will only save them in the user cache database, not to the puzzle set file. To save them to the puzzle set file, you must invoke heromesh -f to flush the user cache. The local replay list and mark will not be saved in the puzzle set though; they are local to the user account. If the .autoSave resource is true, then it will do this automatically. === Summary of default key/mouse bindings === (Below, "KP" means the "key pad" or "number pad".) Keyboard: CTRL+D Describe by keyboard CTRL+E Level editor CTRL+G Go to level CTRL+I Import move list CTRL+Q Quit CTRL+S Save solution CTRL+T Display level title CTRL+X Export move list F1 Replay 1 move F2 Replay 10 moves F3 Replay 100 moves | > > > > > > > > > > > > > > > > > > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | IMPORTANT NOTE: Saving the solutions here will only save them in the user cache database, not to the puzzle set file. To save them to the puzzle set file, you must invoke heromesh -f to flush the user cache. The local replay list and mark will not be saved in the puzzle set though; they are local to the user account. If the .autoSave resource is true, then it will do this automatically. === List of levels === You can push CTRL+L to display a list of levels. This list displays the numbers of the levels green if you have solved it or red otherwise. You can also push the following: * F1 = Toggle tall/wide mode. The tall mode lists one level per row, and shows the columns of the details. The wide mode only displays the level numbers but more will fit on the screen at once. * F2 = Toggle displaying order numbers or ID numbers. * Space or middle mouse button = Display title. * Return or right mouse button = Go to selected level. === Summary of default key/mouse bindings === (Below, "KP" means the "key pad" or "number pad".) Keyboard: CTRL+D Describe by keyboard CTRL+E Level editor CTRL+G Go to level CTRL+I Import move list CTRL+L List of levels CTRL+Q Quit CTRL+S Save solution CTRL+T Display level title CTRL+X Export move list F1 Replay 1 move F2 Replay 10 moves F3 Replay 100 moves |
︙ | ︙ |
Modified quarks from [26a85bfadd] to [a64afd9280].
︙ | ︙ | |||
223 224 225 226 227 228 229 230 | progress autoSave maxTrigger pasteCommand codepage replaySpeed autoWin | > > | 223 224 225 226 227 228 229 230 231 232 | progress autoSave maxTrigger pasteCommand codepage replaySpeed autoWin listMode listColumns |
Modified quarks.h from [e74f951205] to [e296cad002].
︙ | ︙ | |||
186 187 188 189 190 191 192 193 194 195 196 197 198 199 | #define Q_progress 187 #define Q_autoSave 188 #define Q_maxTrigger 189 #define Q_pasteCommand 190 #define Q_codepage 191 #define Q_replaySpeed 192 #define Q_autoWin 193 static const char*const global_quarks[]={ "screenWidth", "screenHeight", "margin", "palette", "popupColors", "imageSize", | > > | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | #define Q_progress 187 #define Q_autoSave 188 #define Q_maxTrigger 189 #define Q_pasteCommand 190 #define Q_codepage 191 #define Q_replaySpeed 192 #define Q_autoWin 193 #define Q_listMode 194 #define Q_listColumns 195 static const char*const global_quarks[]={ "screenWidth", "screenHeight", "margin", "palette", "popupColors", "imageSize", |
︙ | ︙ | |||
379 380 381 382 383 384 385 386 387 388 389 390 391 392 | "progress", "autoSave", "maxTrigger", "pasteCommand", "codepage", "replaySpeed", "autoWin", 0}; #ifdef HEROMESH_BINDINGS static const SDLKey quark_to_key[Q_undo+1-Q_backspace]={ SDLK_BACKSPACE, SDLK_TAB, SDLK_CLEAR, SDLK_RETURN, | > > | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | "progress", "autoSave", "maxTrigger", "pasteCommand", "codepage", "replaySpeed", "autoWin", "listMode", "listColumns", 0}; #ifdef HEROMESH_BINDINGS static const SDLKey quark_to_key[Q_undo+1-Q_backspace]={ SDLK_BACKSPACE, SDLK_TAB, SDLK_CLEAR, SDLK_RETURN, |
︙ | ︙ |