Free Hero Mesh

Check-in [6b326d94b8]
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 bizarro editing.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6b326d94b847cd1021057334acb3aef67379cd59
User & Date: user on 2021-09-12 00:57:03
Other Links: manifest | tags
Context
2021-09-12
04:37
Add possibility to adjust replay speed at run time. check-in: 1d89b3ab66 user: user tags: trunk
00:57
Implement bizarro editing. check-in: 6b326d94b8 user: user tags: trunk
2021-09-11
07:00
Implement much more of bizarro world (currently untested, and not implemented in level editor yet). check-in: 2a3e5126fe user: user tags: trunk
Changes

Modified TODO from [9de01445bc] to [d20541f055].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
* Sound effects
  * Wave sounds
    * Standard sounds
    * User sounds
    * Compressed wave sounds (?)
  * MML sounds
  * Numeric sounds (?)
* Game engine features
  * Multiple connected objects moving as a unit
  * Bizarro world
  * Testing the deferred movement
  * String data
  * A ,PopUp command to use a popup with arguments starting from a mark
  * "Goto message" instruction (?)
  * Returning a class from COLLIDE/COLLIDEBY to transform
  * Coordinate input (may be suitable for some kind of games)
* Editor
  * Mouse dragging









<
<







1
2
3
4
5
6
7
8
9


10
11
12
13
14
15
16
* Sound effects
  * Wave sounds
    * Standard sounds
    * User sounds
    * Compressed wave sounds (?)
  * MML sounds
  * Numeric sounds (?)
* Game engine features
  * Multiple connected objects moving as a unit


  * String data
  * A ,PopUp command to use a popup with arguments starting from a mark
  * "Goto message" instruction (?)
  * Returning a class from COLLIDE/COLLIDEBY to transform
  * Coordinate input (may be suitable for some kind of games)
* Editor
  * Mouse dragging
44
45
46
47
48
49
50



* Branching replay recording
* Slow movement displaying state between triggers
* Warning if file changed when uncommited data exists in the cache database
* Composite puzzle set format (in a single file; read-only)
  * Optional hypertext help
  * Compressed class definitions (?)
* Option to auto display level titles










>
>
>
42
43
44
45
46
47
48
49
50
51
* Branching replay recording
* Slow movement displaying state between triggers
* Warning if file changed when uncommited data exists in the cache database
* Composite puzzle set format (in a single file; read-only)
  * Optional hypertext help
  * Compressed class definitions (?)
* Option to auto display level titles
* Testing
  * Bizarro world
  * Deferred movement

Modified bindings.doc from [18a5b1ed51] to [f5d84d82bf].

138
139
140
141
142
143
144



145
146
147
148
149
150
151
'^s'
  Display the list of level strings.

'^u' <location>
  Add an object with the current MRU values to that location, even if
  there is already another object of the same class at that location.




'em' <object>
  Edit the Misc/Dir of an existing object.

'ex' <command>
  Export a level (given the operating system command to receive the
  exported data). See export.doc for details.








>
>
>







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
'^s'
  Display the list of level strings.

'^u' <location>
  Add an object with the current MRU values to that location, even if
  there is already another object of the same class at that location.

'^w'
  Swap the normal world with the bizarro world.

'em' <object>
  Edit the Misc/Dir of an existing object.

'ex' <command>
  Export a level (given the operating system command to receive the
  exported data). See export.doc for details.

Modified default.heromeshrc from [77e855c5b3] to [a8c6aa6da8].

142
143
144
145
146
147
148

149
150
151
152
153
154
155
?.editKey.9: select 'mr',8;
?.editKey.up: select 'mR',-1;
?.editKey.down: select 'mR',+1;
?.editKey.C: select 'lc',:level_code where :level_code=cast(:level_code as int);
?.editKey.E: ^s
?.editKey.F: with n(n) as (select 65 union all select n+1 h from n where h<=64*pfheight()+65) select '^a',n from n;
?.editKey.R: select 're',substr(:resize,1,instr(:resize,'x')-1),substr(:resize,instr(:resize,'x')+1) where length(:resize);

?.editKey.ctrl.N: ^N
?.editKey.ctrl.P: ^P
?.editKey.ctrl.X: select 're',pfwidth(),pfheight();
?.editKey.space: ^c
?.editKey.return: ^e
?.editKey.f1: select 'im',:Import_Level;
?.editKey.f2: select 'ex',:Export_Level;







>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
?.editKey.9: select 'mr',8;
?.editKey.up: select 'mR',-1;
?.editKey.down: select 'mR',+1;
?.editKey.C: select 'lc',:level_code where :level_code=cast(:level_code as int);
?.editKey.E: ^s
?.editKey.F: with n(n) as (select 65 union all select n+1 h from n where h<=64*pfheight()+65) select '^a',n from n;
?.editKey.R: select 're',substr(:resize,1,instr(:resize,'x')-1),substr(:resize,instr(:resize,'x')+1) where length(:resize);
?.editKey.W: ^w
?.editKey.ctrl.N: ^N
?.editKey.ctrl.P: ^P
?.editKey.ctrl.X: select 're',pfwidth(),pfheight();
?.editKey.space: ^c
?.editKey.return: ^e
?.editKey.f1: select 'im',:Import_Level;
?.editKey.f2: select 'ex',:Export_Level;

Modified edit.c from [3589f5cda0] to [10af50702a].

203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
239









240
241
242
243
244
245
246
       bit7-bit6 = How many (0=has Misc2 and Misc3, not Misc1)
       bit5-bit4 = Misc3 type
       bit3-bit2 = Misc2 type
       bit1-bit0 = Misc1 type
     * misc data (variable size)
   Store/use MRU slot 0 if any bits of 0x70 set in flag byte; slot 1 otherwise
  */

  Uint8 x=0;
  Uint8 y=1;
  const Object*m[2]={0,0};
  sqlite3_str*str=sqlite3_str_new(0);
  Uint32 n;
  long sz;
  char*data;
  int i;
  // Header
  if(level_changed) version_change();
  level_changed=0;
  sqlite3_str_appendchar(str,1,level_version&255);
  sqlite3_str_appendchar(str,1,level_version>>8);
  sqlite3_str_appendchar(str,1,level_code&255);
  sqlite3_str_appendchar(str,1,level_code>>8);
  sqlite3_str_appendchar(str,1,pfwidth-1);
  sqlite3_str_appendchar(str,1,pfheight-1);
  if(level_title) sqlite3_str_appendall(str,level_title);
  sqlite3_str_appendchar(str,1,0);
  // Objects

  for(i=0;i<64*64;i++) {
    n=playfield[i];
    while(n!=VOIDLINK) {
      save_obj(str,objects[n],m,x,y);
      x=objects[n]->x;
      y=objects[n]->y;
      n=objects[n]->up;
    }
  }
  save_obj(str,0,m,x,y);









  sqlite3_str_appendchar(str,1,0xFF);
  // Level strings
  for(i=0;i<nlevelstrings;i++) {
    sqlite3_str_appendall(str,levelstrings[i]);
    sqlite3_str_appendchar(str,1,0);
  }
  // Done







>




















>

|








>
>
>
>
>
>
>
>
>







203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
       bit7-bit6 = How many (0=has Misc2 and Misc3, not Misc1)
       bit5-bit4 = Misc3 type
       bit3-bit2 = Misc2 type
       bit1-bit0 = Misc1 type
     * misc data (variable size)
   Store/use MRU slot 0 if any bits of 0x70 set in flag byte; slot 1 otherwise
  */
  Uint32*p=playfield;
  Uint8 x=0;
  Uint8 y=1;
  const Object*m[2]={0,0};
  sqlite3_str*str=sqlite3_str_new(0);
  Uint32 n;
  long sz;
  char*data;
  int i;
  // Header
  if(level_changed) version_change();
  level_changed=0;
  sqlite3_str_appendchar(str,1,level_version&255);
  sqlite3_str_appendchar(str,1,level_version>>8);
  sqlite3_str_appendchar(str,1,level_code&255);
  sqlite3_str_appendchar(str,1,level_code>>8);
  sqlite3_str_appendchar(str,1,pfwidth-1);
  sqlite3_str_appendchar(str,1,pfheight-1);
  if(level_title) sqlite3_str_appendall(str,level_title);
  sqlite3_str_appendchar(str,1,0);
  // Objects
  again:
  for(i=0;i<64*64;i++) {
    n=p[i];
    while(n!=VOIDLINK) {
      save_obj(str,objects[n],m,x,y);
      x=objects[n]->x;
      y=objects[n]->y;
      n=objects[n]->up;
    }
  }
  save_obj(str,0,m,x,y);
  if(p==playfield) {
    p=bizplayfield;
    for(i=0;i<64*64;i++) if(p[i]!=VOIDLINK) {
      sqlite3_str_appendchar(str,1,0xFE);
      x=0;
      y=1;
      goto again;
    }
  }
  sqlite3_str_appendchar(str,1,0xFF);
  // Level strings
  for(i=0;i<nlevelstrings;i++) {
    sqlite3_str_appendall(str,levelstrings[i]);
    sqlite3_str_appendchar(str,1,0);
  }
  // Done
744
745
746
747
748
749
750

751
752
753
754
755
756
757
      n=objects[n]->up;
    }
  }
  generation_number_inc=0;
  n=objalloc(m->class);
  if(n==VOIDLINK) return;
  level_changed=1;

  objects[n]->x=x;
  objects[n]->y=y;
  objects[n]->image=m->img;
  objects[n]->dir=m->dir;
  objects[n]->misc1=m->misc1;
  objects[n]->misc2=m->misc2;
  objects[n]->misc3=m->misc3;







>







755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
      n=objects[n]->up;
    }
  }
  generation_number_inc=0;
  n=objalloc(m->class);
  if(n==VOIDLINK) return;
  level_changed=1;
  objects[n]->oflags&=~OF_BIZARRO;
  objects[n]->x=x;
  objects[n]->y=y;
  objects[n]->image=m->img;
  objects[n]->dir=m->dir;
  objects[n]->misc1=m->misc1;
  objects[n]->misc2=m->misc2;
  objects[n]->misc3=m->misc3;
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
    case TY_MESSAGE: fprintf(fp," %s%s",v.u<256?"":"#",v.u<256?standard_message_names[v.u]:messages[v.u-256]);
    case TY_LEVELSTRING: fprintf(fp," %%%u",(int)v.u); break;
    default: fprintf(fp," ???"); break;
  }
}

static void export_level(const char*cmd) {

  int i;
  Uint32 n;
  Object*o;
  FILE*fp;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"w");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }
  fprintf(fp,"; Free Hero Mesh exported level ID=%d ORD=%d\n",level_id,level_ord);
  fprint_esc(fp,'@',level_title);
  fprintf(fp,"C %d\nD %d %d\n",level_code,pfwidth,pfheight);

  for(i=0;i<64*64;i++) {
    n=playfield[i];
    while(n!=VOIDLINK) {
      o=objects[n];
      fprintf(fp,"%d %d $%s %d",o->x,o->y,classes[o->class]->name,o->image);
      fprint_misc(fp,o->misc1);
      fprint_misc(fp,o->misc2);
      fprint_misc(fp,o->misc3);
      fprintf(fp," %d\n",o->dir);
      n=o->up;
    }
  }









  for(i=0;i<nlevelstrings;i++) fprint_esc(fp,'%',levelstrings[i]);
  pclose(fp);
}

static char*import_numbers(char*p,int*x,int*y) {
  if(!p) return 0;
  while(*p==' ' || *p=='\t') p++;







>













>

|










>
>
>
>
>
>
>
>
>







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
    case TY_MESSAGE: fprintf(fp," %s%s",v.u<256?"":"#",v.u<256?standard_message_names[v.u]:messages[v.u-256]);
    case TY_LEVELSTRING: fprintf(fp," %%%u",(int)v.u); break;
    default: fprintf(fp," ???"); break;
  }
}

static void export_level(const char*cmd) {
  Uint32*p=playfield;
  int i;
  Uint32 n;
  Object*o;
  FILE*fp;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"w");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }
  fprintf(fp,"; Free Hero Mesh exported level ID=%d ORD=%d\n",level_id,level_ord);
  fprint_esc(fp,'@',level_title);
  fprintf(fp,"C %d\nD %d %d\n",level_code,pfwidth,pfheight);
  again:
  for(i=0;i<64*64;i++) {
    n=p[i];
    while(n!=VOIDLINK) {
      o=objects[n];
      fprintf(fp,"%d %d $%s %d",o->x,o->y,classes[o->class]->name,o->image);
      fprint_misc(fp,o->misc1);
      fprint_misc(fp,o->misc2);
      fprint_misc(fp,o->misc3);
      fprintf(fp," %d\n",o->dir);
      n=o->up;
    }
  }
  if(p==playfield) {
    p=bizplayfield;
    for(i=0;i<64*64;i++) if(p[i]!=VOIDLINK) {
      fprintf(fp,"W\n");
      goto again;
    }
  } else {
    fprintf(fp,"W\n");
  }
  for(i=0;i<nlevelstrings;i++) fprint_esc(fp,'%',levelstrings[i]);
  pclose(fp);
}

static char*import_numbers(char*p,int*x,int*y) {
  if(!p) return 0;
  while(*p==' ' || *p=='\t') p++;
1016
1017
1018
1019
1020
1021
1022




1023
1024
1025
1026
1027
1028
1029
        break;
      case 'V':
        p=import_numbers(p+1,&x,0);
        if(!p || *p) goto bad;
        level_version=x;
        level_changed=0;
        break;




      case '@':
        free(level_title);
        level_title=import_string(p+1);
        break;
      case '%':
        if(nlevelstrings>0x2000) {
          screen_message("Too many level strings");







>
>
>
>







1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
        break;
      case 'V':
        p=import_numbers(p+1,&x,0);
        if(!p || *p) goto bad;
        level_version=x;
        level_changed=0;
        break;
      case 'W':
        if(!d) goto missd;
        swap_world();
        break;
      case '@':
        free(level_title);
        level_title=import_string(p+1);
        break;
      case '%':
        if(nlevelstrings>0x2000) {
          screen_message("Too many level strings");
1449
1450
1451
1452
1453
1454
1455




1456
1457
1458
1459
1460
1461
1462
      return 0;
    case '^s': // String list/edit
      string_list();
      return 0;
    case '^u': // Add object (allow duplicates)
      if(prev) return prev;
      add_object_at(number&63?:64,number/64?:64,mru+curmru,0);




      return 0;
    case '^N': // New level
      if(level_nindex>0xFFFE) return 0;
      new_level();
      return 1;
    case '^P': // Play
      return -2;







>
>
>
>







1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
      return 0;
    case '^s': // String list/edit
      string_list();
      return 0;
    case '^u': // Add object (allow duplicates)
      if(prev) return prev;
      add_object_at(number&63?:64,number/64?:64,mru+curmru,0);
      return 0;
    case '^w': // Swap world
      swap_world();
      level_changed=1;
      return 0;
    case '^N': // New level
      if(level_nindex>0xFFFE) return 0;
      new_level();
      return 1;
    case '^P': // Play
      return -2;

Modified export.doc from [c550d0d7d1] to [39ee704f6d].

1
2
3
4
5
6
7
8
9
10
11
12




13
14
15
16
17
18
19
This document describes the level export format of Free Hero Mesh.

You can use this format for the following purposes:

* To copy a level in Free Hero Mesh, within the same puzzle set or from
one puzzle set to another one.

* To export a level, to be parsed by an external program.

* To produce by an external program and then import into Free Hero Mesh.

* To edit it in a text editor.





Each line is one record, and is terminated by a line feed. Everything in
this file is case-sensitive.


=== Types of records ===













>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
This document describes the level export format of Free Hero Mesh.

You can use this format for the following purposes:

* To copy a level in Free Hero Mesh, within the same puzzle set or from
one puzzle set to another one.

* To export a level, to be parsed by an external program.

* To produce by an external program and then import into Free Hero Mesh.

* To edit it in a text editor.

(In the first case, when duplicating the level within the same puzzle set,
you can also just copy the .LVL lump. However, that will not work if you
are copying between puzzle sets.)

Each line is one record, and is terminated by a line feed. Everything in
this file is case-sensitive.


=== Types of records ===

33
34
35
36
37
38
39




40
41
42
43
44
45
46
  line must come before any objects are listed, and is mandatory.

V version
  This command never occurs in levels exported from Free Hero Mesh,
  although it can optionally occur in imported levels. If it is specified,
  then it sets the level version number.





x y $class image misc1 misc2 misc3 dir
  Specify an object. Objects are listed starting from the top left corner,
  going right, and within each cell the objects are listed from bottom to
  top. Coordinates are one-based. The image is a number (0 for the first
  image), misc1 to misc3 are misc values (see below), and dir is also a
  number (0 for east, up to 7 for southeast). When the data is being
  imported, it will accept objects listed in any order, but when being







>
>
>
>







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  line must come before any objects are listed, and is mandatory.

V version
  This command never occurs in levels exported from Free Hero Mesh,
  although it can optionally occur in imported levels. If it is specified,
  then it sets the level version number.

W
  After all objects in the normal world, use this command to start the
  objects in the bizarro world; you then need another W at the end.

x y $class image misc1 misc2 misc3 dir
  Specify an object. Objects are listed starting from the top left corner,
  going right, and within each cell the objects are listed from bottom to
  top. Coordinates are one-based. The image is a number (0 for the first
  image), misc1 to misc3 are misc values (see below), and dir is also a
  number (0 for east, up to 7 for southeast). When the data is being
  imported, it will accept objects listed in any order, but when being

Modified main.c from [66efb586aa] to [2fa2744cde].

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  level_title=0;
  annihilate();
  generation_number=TY_MAXTYPE+1;
  generation_number_inc=0;
  level_version=p[0]|(p[1]<<8);
  level_code=p[2]|(p[3]<<8);
  p+=4;
  if(*p&0x80) of=OF_BIZARRO;
  pfwidth=(*p++&63)+1;
  pfheight=(*p++&63)+1;
  while(*p && p<end) p++; // skip text for now
  p++; // skip null terminator
  if(p>=end) goto bad1;
  level_title=strdup(buf+6);
  if(!level_title) fatal("Allocation failed\n");







<







234
235
236
237
238
239
240

241
242
243
244
245
246
247
  level_title=0;
  annihilate();
  generation_number=TY_MAXTYPE+1;
  generation_number_inc=0;
  level_version=p[0]|(p[1]<<8);
  level_code=p[2]|(p[3]<<8);
  p+=4;

  pfwidth=(*p++&63)+1;
  pfheight=(*p++&63)+1;
  while(*p && p<end) p++; // skip text for now
  p++; // skip null terminator
  if(p>=end) goto bad1;
  level_title=strdup(buf+6);
  if(!level_title) fatal("Allocation failed\n");
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
      objects[o]->y=y;
      if(x>pfwidth) goto bad2;
      pflink(o);
      --n;
    } else {
      if(p>=end) goto bad1;
      z=*p++;
      if(z==0xFF) {
        if(!of) break;
        of=0;
        goto restart;
      }
      if(z&0x20) x=*p++;
      if(z&0x10) y=*p++;
      if(z&0x40) x++;
      if(!x || !y || x>pfwidth || y>pfheight) goto bad2;
      n=z&0x70?0:1;







|
|
|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
      objects[o]->y=y;
      if(x>pfwidth) goto bad2;
      pflink(o);
      --n;
    } else {
      if(p>=end) goto bad1;
      z=*p++;
      if(z==0xFF) break;
      if(z==0xFE) {
        of^=OF_BIZARRO;
        goto restart;
      }
      if(z&0x20) x=*p++;
      if(z&0x10) y=*p++;
      if(z&0x40) x++;
      if(!x || !y || x>pfwidth || y>pfheight) goto bad2;
      n=z&0x70?0:1;