Free Hero Mesh

Check-in [7498f8d701]
Login
This is a mirror of the main repository for Free Hero Mesh. New tickets and changes will not be accepted at this mirror.
Overview
Comment:Implement slow replay mode.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7498f8d701ae64d0187cce7c67bc723c02c30252
User & Date: user on 2021-05-18 05:27:16
Other Links: manifest | tags
Context
2021-05-18
06:25
Always disable buffering for stderr. check-in: 79b85a3d23 user: user tags: trunk
05:27
Implement slow replay mode. check-in: 7498f8d701 user: user tags: trunk
2021-05-16
05:32
Fix some mistakes with counting the number of picture lumps in the picture editor. check-in: 1ce408e53f user: user tags: trunk
Changes

Modified TODO from [c4c8e86a65] to [9feb084340].

28
29
30
31
32
33
34


35
36
37
* Message trace menu to enable/disable
* Bugs
  * Figure out why the $SeekerCloser class doesn't seem to work properly
  * Level 232 of SUPERHRO puzzle set (the Lava shouldn't expand? why?)
* Display solution comments/timestamp
* VCR mode
* Portable mode, not needing installing files in home directory


* Command-line switch for batch import/export levels
* SQL
  * Implement the GROUP column in the CLASSES table







>
>



28
29
30
31
32
33
34
35
36
37
38
39
* Message trace menu to enable/disable
* Bugs
  * Figure out why the $SeekerCloser class doesn't seem to work properly
  * Level 232 of SUPERHRO puzzle set (the Lava shouldn't expand? why?)
* Display solution comments/timestamp
* VCR mode
* Portable mode, not needing installing files in home directory
  * Maybe not needed? You can override the HOME environment variable
  * An alternative is to check argv[0]; if it contains / then don't use HOME
* Command-line switch for batch import/export levels
* SQL
  * Implement the GROUP column in the CLASSES table

Modified bindings.doc from [a7dfb340db] to [5b9a298076].

80
81
82
83
84
85
86



87
88
89
90
91
92
93

'^g'
  Display values of global variables.

'^o' <location>
  List objects at the specified coordinates, to examine their values.




'^s'
  Toggle solution replay.

'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).







>
>
>







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

'^g'
  Display values of global variables.

'^o' <location>
  List objects at the specified coordinates, to examine their values.

'^p'
  Start or stop slow replay. Any move input also interrupts slow replay.

'^s'
  Toggle solution replay.

'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).

Modified config.doc from [2295add04a] to [7ca3d09672].

77
78
79
80
81
82
83




84
85
86
87
88
89
90
  a single line of text, you can push SHIFT+INSERT to paste.

.progress
  If positive, how many steps between dots in the progress report for the
  auto testing most. If negative, number of milliseconds to sleep before
  each level is executed.





.saveSolutions
  If true, then solutions are saved if you solve a level in less moves
  than the currently recorded solution (or if no solution is already
  recorded). This has no effect in read-only mode.

.screenFlags
  SDL flags: d = double buffer, f = full screen, h = use hardware surface,







>
>
>
>







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  a single line of text, you can push SHIFT+INSERT to paste.

.progress
  If positive, how many steps between dots in the progress report for the
  auto testing most. If negative, number of milliseconds to sleep before
  each level is executed.

.replaySpeed
  When the slow replay is used, sets the playback speed; this is a number
  of centiseconds between turns from 0 to 255.

.saveSolutions
  If true, then solutions are saved if you solve a level in less moves
  than the currently recorded solution (or if no solution is already
  recorded). This has no effect in read-only mode.

.screenFlags
  SDL flags: d = double buffer, f = full screen, h = use hardware surface,

Modified default.heromeshrc from [f9d22c5234] to [1fdde48f53].

125
126
127
128
129
130
131

132
133
134
135
136
137
138
?.gameKey.f5: ^M
?.gameKey.f6: ^<
?.gameKey.f7: ^>
?.gameKey.f8: ^s
?.gameKey.tab: ^I
?.gameKey.alt.D: select '^d',$key_xy;
?.gameKey.alt.G: ^g


! 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;







>







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
?.gameKey.f5: ^M
?.gameKey.f6: ^<
?.gameKey.f7: ^>
?.gameKey.f8: ^s
?.gameKey.tab: ^I
?.gameKey.alt.D: select '^d',$key_xy;
?.gameKey.alt.G: ^g
?.gameKey.alt.P: ^p

! 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 [1849237165] to [82e0e69943].

25
26
27
28
29
30
31


32
33
34
35
36
37



38
39
40
41
42
43
44

static volatile Uint8 timerflag;
static int exam_scroll;
static Uint8*inputs;
static int inputs_size,inputs_count;
static Uint8 side_mode=255;
static Uint8 should_record_solution;



static void setup_game(void) {
  const char*v;
  optionquery[1]=Q_showInventory;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"";
  side_mode=boolxrm(v,1);



  if(main_options['r']) {
    should_record_solution=0;
  } else {
    optionquery[1]=Q_saveSolutions;
    v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"";
    should_record_solution=boolxrm(v,0);
  }







>
>






>
>
>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

static volatile Uint8 timerflag;
static int exam_scroll;
static Uint8*inputs;
static int inputs_size,inputs_count;
static Uint8 side_mode=255;
static Uint8 should_record_solution;
static Uint8 replay_speed;
static Uint8 replay_time;

static void setup_game(void) {
  const char*v;
  optionquery[1]=Q_showInventory;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"";
  side_mode=boolxrm(v,1);
  optionquery[1]=Q_replaySpeed;
  v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"";
  replay_speed=strtol(v,0,10)?:16;
  if(main_options['r']) {
    should_record_solution=0;
  } else {
    optionquery[1]=Q_saveSolutions;
    v=xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"";
    should_record_solution=boolxrm(v,0);
  }
285
286
287
288
289
290
291

292
293
294
295
296
297
298
      replay_list=0;
    }
  }
}

static void begin_level(int id) {
  const char*t;

  if(replay_count) save_replay();
  inputs_count=0;
  replay_pos=0;
  t=load_level(id)?:init_level();
  load_replay();
  if(t) {
    gameover=-1;







>







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
      replay_list=0;
    }
  }
}

static void begin_level(int id) {
  const char*t;
  replay_time=0;
  if(replay_count) save_replay();
  inputs_count=0;
  replay_pos=0;
  t=load_level(id)?:init_level();
  load_replay();
  if(t) {
    gameover=-1;
661
662
663
664
665
666
667




668
669
670
671
672
673
674
675
676
677
678

679
680
681
682
683
684
685
  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(solution_replay) {
        screen_message("You cannot play your own moves during the solution replay");
        return -3;
      }
      if(inputs_count>=inputs_size) {
        inputs=realloc(inputs,inputs_size+=32);
        if(!inputs) fatal("Allocation failed\n");
      }
      inputs[inputs_count++]=number;
      return 0;
    case '+ ': replay: // Replay

      if(number>replay_count-replay_pos) number=replay_count-replay_pos;
      if(number<=0) return prev;
      if(inputs_count+number>=inputs_size) {
        inputs=realloc(inputs,inputs_size+=number+1);
        if(!inputs) fatal("Allocation failed\n");
      }
      memcpy(inputs+inputs_count,replay_list+replay_pos,number);







>
>
>
>











>







667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  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;
      }
      if(solution_replay) {
        screen_message("You cannot play your own moves during the solution replay");
        return -3;
      }
      if(inputs_count>=inputs_size) {
        inputs=realloc(inputs,inputs_size+=32);
        if(!inputs) fatal("Allocation failed\n");
      }
      inputs[inputs_count++]=number;
      return 0;
    case '+ ': replay: // Replay
      replay_time=0;
      if(number>replay_count-replay_pos) number=replay_count-replay_pos;
      if(number<=0) return prev;
      if(inputs_count+number>=inputs_size) {
        inputs=realloc(inputs,inputs_size+=number+1);
        if(!inputs) fatal("Allocation failed\n");
      }
      memcpy(inputs+inputs_count,replay_list+replay_pos,number);
725
726
727
728
729
730
731



732
733
734
735
736
737
738
      return prev;
    case '^g': // Display global variables
      global_examine();
      return prev;
    case '^o': // List objects
      list_objects_at(number-65);
      return prev;



    case '^s': // Toggle solution replay
      if(replay_count) save_replay();
      solution_replay^=1;
      if(replay_count) replay_count=0,begin_level(level_id); else load_replay();
      return 1;
    case 'go': // Select level
      begin_level(number);







>
>
>







736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
      return prev;
    case '^g': // Display global variables
      global_examine();
      return prev;
    case '^o': // List objects
      list_objects_at(number-65);
      return prev;
    case '^p': // Slow replay
      replay_time=replay_time?0:1;
      return 0;
    case '^s': // Toggle solution replay
      if(replay_count) save_replay();
      solution_replay^=1;
      if(replay_count) replay_count=0,begin_level(level_id); else load_replay();
      return 1;
    case 'go': // Select level
      begin_level(number);
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
}

void run_game(void) {
  int i;
  SDL_Event ev;
  set_caption();
  replay_count=0;

  if(side_mode==255) setup_game();
  begin_level(level_id);
  redraw_game();
  timerflag=0;
  SDL_SetTimer(10,timer_callback);
  while(SDL_WaitEvent(&ev)) {
    switch(ev.type) {
      case SDL_VIDEOEXPOSE:
        redraw_game();
        break;
      case SDL_QUIT:
        goto quit;
        break;
      case SDL_MOUSEMOTION:
        show_mouse_xy(&ev);
        break;
      case SDL_USEREVENT:
        if(!gameover && !quiz_text) continue_animation();
        timerflag=0;




        break;
      case SDL_MOUSEBUTTONDOWN:
        if(ev.button.x<left_margin) {
          if(ev.button.y<48) break;
          if(side_mode) {
            // Inventory
            //TODO







>



















>
>
>
>







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
891
892
893
894
895
896
897
898
899
900
}

void run_game(void) {
  int i;
  SDL_Event ev;
  set_caption();
  replay_count=0;
  replay_time=0;
  if(side_mode==255) setup_game();
  begin_level(level_id);
  redraw_game();
  timerflag=0;
  SDL_SetTimer(10,timer_callback);
  while(SDL_WaitEvent(&ev)) {
    switch(ev.type) {
      case SDL_VIDEOEXPOSE:
        redraw_game();
        break;
      case SDL_QUIT:
        goto quit;
        break;
      case SDL_MOUSEMOTION:
        show_mouse_xy(&ev);
        break;
      case SDL_USEREVENT:
        if(!gameover && !quiz_text) continue_animation();
        timerflag=0;
        if(replay_time && !--replay_time && !game_command(1,'+ ',1,0,0,0)) {
          replay_time=replay_speed;
          goto replay;
        }
        break;
      case SDL_MOUSEBUTTONDOWN:
        if(ev.button.x<left_margin) {
          if(ev.button.y<48) break;
          if(side_mode) {
            // Inventory
            //TODO

Modified quarks from [6347a65352] to [31753fde64].

217
218
219
220
221
222
223

224
traceObject
showInventory
progress
autoSave
maxTrigger
pasteCommand
codepage









>

217
218
219
220
221
222
223
224
225
traceObject
showInventory
progress
autoSave
maxTrigger
pasteCommand
codepage
replaySpeed

Modified quarks.h from [93005ce16b] to [8c36a586cf].

182
183
184
185
186
187
188

189
190
191
192
193
194
195
#define Q_traceObject 183
#define Q_showInventory 184
#define Q_progress 185
#define Q_autoSave 186
#define Q_maxTrigger 187
#define Q_pasteCommand 188
#define Q_codepage 189

static const char*const global_quarks[]={
  "screenWidth",
  "screenHeight",
  "margin",
  "palette",
  "popupColors",
  "imageSize",







>







182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#define Q_traceObject 183
#define Q_showInventory 184
#define Q_progress 185
#define Q_autoSave 186
#define Q_maxTrigger 187
#define Q_pasteCommand 188
#define Q_codepage 189
#define Q_replaySpeed 190
static const char*const global_quarks[]={
  "screenWidth",
  "screenHeight",
  "margin",
  "palette",
  "popupColors",
  "imageSize",
371
372
373
374
375
376
377

378
379
380
381
382
383
384
  "traceObject",
  "showInventory",
  "progress",
  "autoSave",
  "maxTrigger",
  "pasteCommand",
  "codepage",

0};
#ifdef HEROMESH_BINDINGS
static const SDLKey quark_to_key[Q_undo+1-Q_backspace]={
SDLK_BACKSPACE,
SDLK_TAB,
SDLK_CLEAR,
SDLK_RETURN,







>







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  "traceObject",
  "showInventory",
  "progress",
  "autoSave",
  "maxTrigger",
  "pasteCommand",
  "codepage",
  "replaySpeed",
0};
#ifdef HEROMESH_BINDINGS
static const SDLKey quark_to_key[Q_undo+1-Q_backspace]={
SDLK_BACKSPACE,
SDLK_TAB,
SDLK_CLEAR,
SDLK_RETURN,