Overview
Comment: | Implement save state slots, including loading personal best solutions. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
cc0df8402fc3a994a9c58d7f98e86686 |
User & Date: | user on 2023-10-16 00:12:09 |
Other Links: | manifest | tags |
Context
2023-10-17
| ||
01:33 | Implement SHIFT+DELETE to delete moves up to the mark. check-in: 5c766650a2 user: user tags: trunk | |
2023-10-16
| ||
00:12 | Implement save state slots, including loading personal best solutions. check-in: cc0df8402f user: user tags: trunk | |
2023-10-13
| ||
04:11 | Add the ^? command (which is used only for debugging and may be changed in future). check-in: 8ad7607451 user: user tags: trunk | |
Changes
Modified bindings.doc from [2d7260727a] to [be07d45d1e].
︙ | |||
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 143 144 145 146 147 148 149 | 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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | + + + + + + + + + | '^x' Cancel dead animation. 'lo' <location> Flash the specified location briefly. 'ls' <slot> Load save state slot (0 to 7). 'mi' <command> Import a move list. The argument is a operating system command, that when executed will write the move list to stdout, with one byte per move (the Hero Mesh key codes). 'ml' <blob> Load a move list from a SQL blob. 'mx' <command> Export a move list. The argument is a operating system command that will receive the move list (in the same format as above) on stdin. 'rS' <number> Set replay speed to the specified number (1 to 255). 'rs' <number> Adjust replay speed by the specified number (negative to make faster, or positive to make slower; zero leaves it unchanged). 'ss' <slot> Save save state slot (0 to 7). 'xs' <slot> Exchange save state slot (0 to 7). 'xy' <x> <y> Input a move using coordinate input. Coordinates are 1-based. === Editor commands === '^I' |
︙ |
Modified default.heromeshrc from [5051924df1] to [d8800e43cf].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | + | ! Hero Mesh configuration settings ?.screenWidth: 800 ?.screenHeight: 600 ?.imageSize: 24 ?.traceAll: false ?.showInventory: 0 ?.maxTrigger: 32767 ?.pasteCommand: xclip -o ?.codepage: /home/user/freeheromesh/codepage.har ?.saveSolutions.private: true ! Game inputs ?.gameKey.A: 'A ?.gameKey.B: 'B ?.gameKey.C: 'C ?.gameKey.D: 'D ?.gameKey.E: 'E |
︙ | |||
89 90 91 92 93 94 95 | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | - - + + | ?.gameKey.slash: 'SLASH ?.gameKey.space: 'SPACE ?.gameKey.up: 'UP ! Game inputs with alt ?.gameKey.alt.9: 'F9 ?.gameKey.alt.0: 'F10 |
︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | + + + + + + + + + + + + + + + + + + + + + + + + + | ?.gameKey.shift.f3: -100 ?.gameKey.shift.f4: -1000 ?.gameKey.f5: ^M ?.gameKey.f6: ^< ?.gameKey.f7: ^> ?.gameKey.f8: ^s ?.gameKey.shift.f8: select 'ml',solution_move_list(1); ?.gameKey.ctrl.f8: select 'ml',best_move_list(); ?.gameKey.f9: select 'lo',xy(o.x,o.y) from objects o,classes c on(o.class=c.id) where c.player; ?.gameKey.tab: ^I ?.gameKey.alt.G: ^g ?.gameKey.alt.P: ^p ?.gameKey.alt.R: select 'go',id from levels where not solved order by random() limit 1; ?.gameKey.alt.X: ^x ?.gameKey.alt.leftbracket: select 'rs',-5; ?.gameKey.alt.rightbracket: select 'rs',+5; ?.gameKey.delete: ^- ?.gameKey.ctrl.delete: ^D ?.gameKey.insert: ^+ ?.gameKey.alt.kp_minus: select 'go',-ord from levels where ord<$level and not solved order by ord desc limit 1; ?.gameKey.alt.kp_plus: select 'go',-ord from levels where ord>$level and not solved order by ord asc limit 1; ?.gameKey.shift.1: select 'ss',0; ?.gameKey.shift.2: select 'ss',1; ?.gameKey.shift.3: select 'ss',2; ?.gameKey.shift.4: select 'ss',3; ?.gameKey.shift.5: select 'ss',4; ?.gameKey.shift.6: select 'ss',5; ?.gameKey.shift.7: select 'ss',6; ?.gameKey.shift.8: select 'ss',7; ?.gameKey.ctrl.1: select 'ls',0; ?.gameKey.ctrl.2: select 'ls',1; ?.gameKey.ctrl.3: select 'ls',2; ?.gameKey.ctrl.4: select 'ls',3; ?.gameKey.ctrl.5: select 'ls',4; ?.gameKey.ctrl.6: select 'ls',5; ?.gameKey.ctrl.7: select 'ls',6; ?.gameKey.ctrl.8: select 'ls',7; ?.gameKey.alt.1: select 'xs',0; ?.gameKey.alt.2: select 'xs',1; ?.gameKey.alt.3: select 'xs',2; ?.gameKey.alt.4: select 'xs',3; ?.gameKey.alt.5: select 'xs',4; ?.gameKey.alt.6: select 'xs',5; ?.gameKey.alt.7: select 'xs',6; ?.gameKey.alt.8: select 'xs',7; ! Editor key bindings ?.editKey.1: select 'mr',0; ?.editKey.2: select 'mr',1; ?.editKey.3: select 'mr',2; ?.editKey.4: select 'mr',3; ?.editKey.5: select 'mr',4; |
︙ |
Modified game.c from [8b747a8794] to [1d7efeafa0].
︙ | |||
37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | + + + + + + + + + | static Uint8 replay_speed; static Uint8 replay_time; static Uint8 solved; static Uint8 inserting,saved_inserting; static sqlite3_stmt*autowin; static size_t dum_size; // not used by Free Hero Mesh, but needed by some C library functions. typedef struct { char*data; size_t size; Uint16 mark,pos; } SaveState; static SaveState savestates[8]; static Uint8 has_savestates; int encode_move(FILE*fp,MoveItem v) { // Encodes a single move and writes the encoded move to the file. // Returns the number of bytes of the encoded move. if(v>=8 && v<256) { fputc(v,fp); return 1; } else if(v>=0x8000 && v<=0x8FFF) { |
︙ | |||
330 331 332 333 334 335 336 337 338 339 340 341 342 343 | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | + | SDL_LockSurface(screen); draw_text(0,40,buf,0xF0,0xF1); SDL_UnlockSurface(screen); SDL_Flip(screen); } static void save_replay(void) { int i; unsigned char*buf=0; size_t sz=0; FILE*fp; if(solution_replay || !replay_list || !replay_count) return; if(gameover==1) solved=1; fp=open_memstream((char**)&buf,&sz); if(!fp) fatal("Allocation failed\n"); |
︙ | |||
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | + + + + + + + + + + + + + + + + + + + + + | fputc(best_score>>24,fp); } } if(replay_mark) { fputc(0x42,fp); fputc(replay_mark,fp); fputc(replay_mark>>8,fp); } if(has_savestates) for(i=0;i<8;i++) if(savestates[i].data) { fputc(i+0x30,fp); fputs(savestates[i].data,fp); fputc(0,fp); if(savestates[i].mark || savestates[i].pos) { fputc(i+0xB0,fp); fputc(savestates[i].mark,fp); fputc(savestates[i].mark>>8,fp); fputc(savestates[i].pos,fp); fputc(savestates[i].pos>>8,fp); } } fclose(fp); if(!buf) fatal("Allocation failed\n"); write_userstate(FIL_LEVEL,level_id,sz,buf); free(buf); } static void load_replay(void) { FILE*fp=0; unsigned char*buf=0; long sz; int i,j; free(replay_list); replay_list=0; replay_count=replay_mark=replay_size=0; free(best_list); best_list=0; if(has_savestates) { for(i=0;i<8;i++) { free(savestates[i].data); savestates[i].data=0; savestates[i].size=0; savestates[i].mark=savestates[i].pos=0; } has_savestates=0; } if(solution_replay) { gameover_score=NO_SCORE; if(buf=read_lump(FIL_SOLUTION,level_id,&sz)) { fp=fmemopen(buf,sz,"r"); if(!fp) fatal("Allocation failed\n"); // Solution format: version (16-bits), flag (8-bits), score (32-bits), user name (null-terminated), timestamp (64-bits), move list if(sz>3) { |
︙ | |||
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | + + + + + + + + + + + | if(replay_list) goto skip; decode_move_list(fp); break; case 0x02: // Best list if(best_list) goto skip; dum_size=0; getdelim(&best_list,&dum_size,0,fp); break; case 0x30 ... 0x37: // Save states if(savestates[i&7].data) goto skip; getdelim(&savestates[i&7].data,&savestates[i&7].size,0,fp); has_savestates=1; break; case 0x41: // Solved version i=fgetc(fp); i|=fgetc(fp)<<8; if(i==level_version) solved=1; break; case 0x42: // Mark replay_mark=fgetc(fp); replay_mark|=fgetc(fp)<<8; break; case 0x81: // Best score best_score=fgetc(fp); best_score|=fgetc(fp)<<8; best_score|=fgetc(fp)<<16; best_score|=fgetc(fp)<<24; break; case 0xB0 ... 0xB7: // Save states savestates[i&7].mark=fgetc(fp); savestates[i&7].mark|=fgetc(fp)<<8; savestates[i&7].pos=fgetc(fp); savestates[i&7].pos|=fgetc(fp)<<8; break; default: skip: if(i<0x40) { while(fgetc(fp)>0); } else if(i<0x80) { fgetc(fp); fgetc(fp); } else if(i<0xC0) { for(i=0;i<4;i++) fgetc(fp); |
︙ | |||
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | best_score=NO_SCORE; } } } if(fp) fclose(fp); free(buf); } static int exchange_state(int slot,int how) { // Return value is replay position of save state (-1 if error) // slot = 0 to 7 // how = 'l' (load), 's' (save), 'x' (exchange) SaveState*ss=savestates+slot; SaveState v=*ss; FILE*fp; if(how!='s' && !v.data) { screen_message("Nonexisting save state"); return -1; } if(how!='l') { if(ss->data) { if(how=='s') free(ss->data); ss->data=0; ss->size=0; } if(replay_count) { has_savestates=1; fp=open_memstream(&ss->data,&ss->size); if(!fp) fatal("Allocation failed\n"); encode_move_list(fp); fputc(0,fp); fclose(fp); } ss->mark=replay_mark; ss->pos=replay_pos; } if(how!='s') { fp=fmemopen(v.data,v.size,"r"); if(!fp) fatal("Allocation failed\n"); decode_move_list(fp); fclose(fp); if(how=='x') free(v.data); replay_mark=v.mark; } return v.pos; } static void begin_level(int id) { const char*t; replay_time=0; if(replay_count) save_replay(); inputs_count=0; replay_pos=0; |
︙ | |||
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 | + + + + + + + + | return prev; case 'go': // Select level begin_level(number); return 1; case 'lo': // Locate me locate_me(number&63?:64,number/64?:64); return prev; case 'ls': // Load state if(solution_replay) { screen_message("You cannot load states during solution replay"); return -3; } number=exchange_state(number&7,'l'); if(number<0) return -3; goto restart; case 'mi': // Move list import if(argc<2 || solution_replay) break; if(replay_pos) begin_level(level_id); do_import_moves(sqlite3_column_text(args,1)); return 1; case 'ml': // Move list load if(argc<2 || solution_replay) break; |
︙ | |||
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 | + + + + + + + + + + + | case 'rs': // Replay speed number+=replay_speed; // fall through case 'rS': // Replay speed (absolute) if(number<1) number=1; else if(number>255) number=255; replay_speed=number; return prev; case 'ss': // Save state exchange_state(number&7,'s'); return prev; case 'xs': // Exchange state if(solution_replay) { screen_message("You cannot load states during solution replay"); return -3; } number=exchange_state(number&7,'x'); if(number<0) return -3; goto restart; case 'xy': // Coordinate input if(argc<3 || !has_xy_input) break; argc=sqlite3_column_int(args,1); number=sqlite3_column_int(args,2); if(argc<1 || argc>pfwidth || number<1 || number>pfheight) return 0; number=(number-1)|((argc-1)<<6)|0x8000; goto play; |
︙ |
Modified game.doc from [f62970e1c0] to [6b233e488f].
︙ | |||
92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | + + + + | by pushing ALT+P again, and you can push ALT+[ and ALT+] to adjust speed. If you solve a level yourself, you can push CTRL+S to record the solution. The solution will be recorded in the puzzle set once the user cache is flushed as described below. If the .saveSolution resource is true, then it will automatically save the solution (although not necessarily to the puzzle set files). There are also save state slots, which are separate for each level; you can have up to eight such save states recorded at once. Use SHIFT to save, CTRL to load, or ALT to exchange, together with a number 1 to 8. 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. |
︙ | |||
153 154 155 156 157 158 159 | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | - + + + + + | SHIFT+F2 Rewind 10 moves SHIFT+F3 Rewind 100 moves SHIFT+F4 Rewind 1000 moves F5 Set mark F6 Rewind to mark F7 Replay to mark F8 Toggle solution replay |
Modified internals.doc from [e83e2923f9] to [3374ee89ff].
︙ | |||
195 196 197 198 199 200 201 202 203 204 205 206 207 208 | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | + + + + + | The new format is always small-endian. Record types: * 0x01 = Replay list * 0x02 = Best personal solution * 0x30 to 0x37 = Replay list of save state * 0x41 = Level version, if solved (omitted otherwise) * 0x42 = Mark position * 0x81 = Best personal score * 0xB0 to 0xB7 = Save state data; low 16-bits is mark position and high 16-bits is current replay position |