Free Hero Mesh

Check-in [aeb7bb0218]
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 importing solutions when importing the level data.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: aeb7bb0218a8655ca71a541d1635ec41fe67530e
User & Date: user on 2021-11-06 03:28:16
Other Links: manifest | tags
Context
2021-11-07
19:59
Remote note at top of document about being incomplete; it does not seem to be incomplete. check-in: ab03b51d6f user: user tags: trunk
2021-11-06
03:28
Implement importing solutions when importing the level data. check-in: aeb7bb0218 user: user tags: trunk
2021-11-01
05:42
In misc/vtower.class, ensure that you cannot stop flying while over a non-walkable tile. check-in: fbaada037f user: user tags: trunk
Changes

Modified TODO from [8bd371145f] to [991048a78f].

61
62
63
64
65
66
67

  * Escape
  * Brain N Bells
  * Chroma
  * Xsok
  * PuzzleScript (limited; not everything is or will be capable)
  * Sokoban
  * Puzzle Boy








>
61
62
63
64
65
66
67
68
  * Escape
  * Brain N Bells
  * Chroma
  * Xsok
  * PuzzleScript (limited; not everything is or will be capable)
  * Sokoban
  * Puzzle Boy
  * Pitman/Catrap

Modified edit.c from [f2c9f9d8a1] to [cca5f42c2a].

24
25
26
27
28
29
30










31
32
33
34
35
36
37
  Uint8 img,dir;
  Value misc1,misc2,misc3;
} MRU;

#define MRUCOUNT 32
static MRU mru[MRUCOUNT];
static int curmru;











static void rewrite_class_def(void) {
  Uint32 i,n;
  Uint8 cu[0x4000/8];
  Uint8 mu[0x4000/8];
  Object*o;
  long size=0;







>
>
>
>
>
>
>
>
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  Uint8 img,dir;
  Value misc1,misc2,misc3;
} MRU;

#define MRUCOUNT 32
static MRU mru[MRUCOUNT];
static int curmru;
static char*solution_data;
static int solution_length;

static inline void discard_solution(void) {
  if(solution_data) {
    sqlite3_free(solution_data);
    solution_data=0;
    solution_length=0;
  }
}

static void rewrite_class_def(void) {
  Uint32 i,n;
  Uint8 cu[0x4000/8];
  Uint8 mu[0x4000/8];
  Object*o;
  long size=0;
262
263
264
265
266
267
268






269
270
271
272
273
274
275
  data=sqlite3_str_finish(str);
  if(!data) fatal("Allocation failed\n");
  sqlite3_exec(userdb,"BEGIN;",0,0,0);
  write_lump(FIL_LEVEL,level_id,sz,data);
  sqlite3_free(data);
  if(level_ord==level_nindex+1) update_level_index();
  rewrite_class_def();






  sqlite3_exec(userdb,"COMMIT;",0,0,0);
}

static void redraw_editor(void) {
  char buf[32];
  SDL_Rect r;
  int x,y;







>
>
>
>
>
>







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  data=sqlite3_str_finish(str);
  if(!data) fatal("Allocation failed\n");
  sqlite3_exec(userdb,"BEGIN;",0,0,0);
  write_lump(FIL_LEVEL,level_id,sz,data);
  sqlite3_free(data);
  if(level_ord==level_nindex+1) update_level_index();
  rewrite_class_def();
  if(solution_data) {
    solution_data[0]=level_version&255;
    solution_data[1]=level_version>>8;
    write_lump(FIL_SOLUTION,level_id,solution_length,solution_data);
    discard_solution();
  }
  sqlite3_exec(userdb,"COMMIT;",0,0,0);
}

static void redraw_editor(void) {
  char buf[32];
  SDL_Rect r;
  int x,y;
971
972
973
974
975
976
977

978
979
980
981
982
983
984

985
986
987
988
989
990
991
  char d=0;
  int x,y;
  Object*o;
  FILE*fp;
  size_t size=0;
  char*buf=0;
  char*p;

  Value v;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"r");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }

  level_changed=1;
  while(getline(&buf,&size,fp)>0) {
    p=buf;
    p[strcspn(p,"\r\n\f")]=0;
    p+=strspn(p,"\t ");
    switch(*p) {
      case '0' ... '9':







>







>







987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
  char d=0;
  int x,y;
  Object*o;
  FILE*fp;
  size_t size=0;
  char*buf=0;
  char*p;
  sqlite3_str*sol=0;
  Value v;
  if(!cmd || !*cmd) return;
  fp=popen(cmd,"r");
  if(!fp) {
    screen_message("Cannot open pipe");
    return;
  }
  discard_solution();
  level_changed=1;
  while(getline(&buf,&size,fp)>0) {
    p=buf;
    p[strcspn(p,"\r\n\f")]=0;
    p+=strspn(p,"\t ");
    switch(*p) {
      case '0' ... '9':
1059
1060
1061
1062
1063
1064
1065





















1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078





1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096

1097
1098
1099
1100
1101
1102
1103
          screen_message("Too many level strings");
        } else {
          levelstrings=realloc(levelstrings,(nlevelstrings+1)*sizeof(unsigned char*));
          if(!levelstrings) fatal("Allocation failed\n");
          levelstrings[nlevelstrings++]=import_string(p+1);
        }
        break;





















      default:
        if(*p && *p!=';') {
          bad:
          fprintf(stderr,"Invalid record in imported data:  %s\n",buf);
          screen_message("Invalid record");
          goto done;
        }
    }
  }
  done:
  free(buf);
  pclose(fp);
  generation_number_inc=0;





}

static void new_level(void) {
  sqlite3_stmt*st;
  int i;
  if(i=sqlite3_prepare_v2(userdb,"SELECT COALESCE(MAX(`LEVEL`),-1)+1 FROM `USERCACHEDATA` WHERE `FILE` = LEVEL_CACHEID();",-1,&st,0)) {
    screen_message(sqlite3_errmsg(userdb));
    return;
  }
  i=sqlite3_step(st);
  if(i!=SQLITE_ROW) {
    sqlite3_finalize(st);
    screen_message(sqlite3_errmsg(userdb));
    return;
  }
  i=sqlite3_column_int(st,0);
  sqlite3_finalize(st);
  if(i>0xFFFE) return;

  annihilate();
  level_id=i;
  free(level_title);
  level_title=strdup("New Level");
  if(!level_title) fatal("Allocation failed\n");
  level_changed=level_version=level_code=0;
  level_ord=level_nindex+1;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













>
>
>
>
>


















>







1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
          screen_message("Too many level strings");
        } else {
          levelstrings=realloc(levelstrings,(nlevelstrings+1)*sizeof(unsigned char*));
          if(!levelstrings) fatal("Allocation failed\n");
          levelstrings[nlevelstrings++]=import_string(p+1);
        }
        break;
      case '\'':
        if(!sol) {
          sol=sqlite3_str_new(userdb);
          sqlite3_str_appendchar(sol,3,0); // append level version (will be overwritten later) and flags
        }
        p=strchr(buf,' ');
        if(p) {
          *p=0;
          p=import_numbers(p+1,&y,0);
          if(!p || *p) goto bad;
        } else {
          y=1;
        }
        for(x=8;x<256;x++) {
          if(heromesh_key_names[x] && !strcmp(buf+1,heromesh_key_names[x])) {
            sqlite3_str_appendchar(sol,y,x);
            break;
          }
        }
        if(x==256) goto bad;
        break;
      default:
        if(*p && *p!=';') {
          bad:
          fprintf(stderr,"Invalid record in imported data:  %s\n",buf);
          screen_message("Invalid record");
          goto done;
        }
    }
  }
  done:
  free(buf);
  pclose(fp);
  generation_number_inc=0;
  if(sol) {
    solution_length=sqlite3_str_length(sol);
    solution_data=sqlite3_str_finish(sol);
    if(!solution_data) fatal("Allocation failed");
  }
}

static void new_level(void) {
  sqlite3_stmt*st;
  int i;
  if(i=sqlite3_prepare_v2(userdb,"SELECT COALESCE(MAX(`LEVEL`),-1)+1 FROM `USERCACHEDATA` WHERE `FILE` = LEVEL_CACHEID();",-1,&st,0)) {
    screen_message(sqlite3_errmsg(userdb));
    return;
  }
  i=sqlite3_step(st);
  if(i!=SQLITE_ROW) {
    sqlite3_finalize(st);
    screen_message(sqlite3_errmsg(userdb));
    return;
  }
  i=sqlite3_column_int(st,0);
  sqlite3_finalize(st);
  if(i>0xFFFE) return;
  discard_solution();
  annihilate();
  level_id=i;
  free(level_title);
  level_title=strdup("New Level");
  if(!level_title) fatal("Allocation failed\n");
  level_changed=level_version=level_code=0;
  level_ord=level_nindex+1;
1503
1504
1505
1506
1507
1508
1509

1510
1511
1512
1513
1514
1515
1516
      level_changed=1;
      return 0;
    case '^N': // New level
      if(level_nindex>0xFFFE) return 0;
      new_level();
      return 1;
    case '^P': // Play

      return -2;
    case '^Q': // Quit
      return -1;
    case '^S': // Save level
      save_level();
      return 1;
    case '^T': // Level title







>







1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
      level_changed=1;
      return 0;
    case '^N': // New level
      if(level_nindex>0xFFFE) return 0;
      new_level();
      return 1;
    case '^P': // Play
      discard_solution();
      return -2;
    case '^Q': // Quit
      return -1;
    case '^S': // Save level
      save_level();
      return 1;
    case '^T': // Level title
1550
1551
1552
1553
1554
1555
1556

1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573

1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589

1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
      if(argc>1 && sqlite3_column_type(args,1)!=SQLITE_NULL) mru_edit_obj(sqlite3_column_int64(args,1));
      return 0;
    case 'ex': // Export level
      if(argc<2) return prev;
      export_level(sqlite3_column_text(args,1));
      return prev;
    case 'go': // Select level

      load_level(number);
      return 1;
    case 'im': // Import level
      if(argc<2) return prev;
      import_level(sqlite3_column_text(args,1));
      return 0;
    case 'lc': // Set level code
      level_code=number;
      level_changed=1;
      return 0;
    case 'lt': // Set level title
      if(argc<2 || !sqlite3_column_text(args,1)) break;
      free(level_title);
      level_title=strdup(sqlite3_column_text(args,1));
      if(!level_title) fatal("Allocation failed\n");
      return 0;
    case 'lv': // Set level version

      level_version=number;
      level_changed=0;
      return 0;
    case 'mR': // Select MRU relative
      number+=curmru;
      // fall through
    case 'mr': // Select MRU absolute
      if(number>=0 && number<MRUCOUNT) curmru=number;
      return 0;
    case 're': // Resize and clear
      if(argc<3) return 0;
      editrect.x0=editrect.y0=editrect.x1=editrect.y1=0;
      x=sqlite3_column_int(args,1);
      y=sqlite3_column_int(args,2);
      if(x<1 || y<1 || x>64 || y>64) return 0;
      level_changed=1;

      annihilate();
      pfwidth=x;
      pfheight=y;
      return 0;
    default:
      return prev;
  }
}

void run_editor(void) {
  SDL_Event ev;
  int i;
  curmru=0;
  set_caption();

  load_level(level_id);
  redraw_editor();
  while(SDL_WaitEvent(&ev)) {
    switch(ev.type) {
      case SDL_VIDEOEXPOSE:
        redraw_editor();
        break;







>

















>
















>














>







1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
      if(argc>1 && sqlite3_column_type(args,1)!=SQLITE_NULL) mru_edit_obj(sqlite3_column_int64(args,1));
      return 0;
    case 'ex': // Export level
      if(argc<2) return prev;
      export_level(sqlite3_column_text(args,1));
      return prev;
    case 'go': // Select level
      discard_solution();
      load_level(number);
      return 1;
    case 'im': // Import level
      if(argc<2) return prev;
      import_level(sqlite3_column_text(args,1));
      return 0;
    case 'lc': // Set level code
      level_code=number;
      level_changed=1;
      return 0;
    case 'lt': // Set level title
      if(argc<2 || !sqlite3_column_text(args,1)) break;
      free(level_title);
      level_title=strdup(sqlite3_column_text(args,1));
      if(!level_title) fatal("Allocation failed\n");
      return 0;
    case 'lv': // Set level version
      discard_solution();
      level_version=number;
      level_changed=0;
      return 0;
    case 'mR': // Select MRU relative
      number+=curmru;
      // fall through
    case 'mr': // Select MRU absolute
      if(number>=0 && number<MRUCOUNT) curmru=number;
      return 0;
    case 're': // Resize and clear
      if(argc<3) return 0;
      editrect.x0=editrect.y0=editrect.x1=editrect.y1=0;
      x=sqlite3_column_int(args,1);
      y=sqlite3_column_int(args,2);
      if(x<1 || y<1 || x>64 || y>64) return 0;
      level_changed=1;
      discard_solution();
      annihilate();
      pfwidth=x;
      pfheight=y;
      return 0;
    default:
      return prev;
  }
}

void run_editor(void) {
  SDL_Event ev;
  int i;
  curmru=0;
  set_caption();
  discard_solution();
  load_level(level_id);
  redraw_editor();
  while(SDL_WaitEvent(&ev)) {
    switch(ev.type) {
      case SDL_VIDEOEXPOSE:
        redraw_editor();
        break;

Modified export.doc from [9602e574ee] to [78c7466e91].

54
55
56
57
58
59
60









61
62
63
64
65
66
67
  imported, it will accept objects listed in any order, but when being
  exported from Free Hero Mesh, they are ordered as described above.

%string
  A level string. The level strings are numbered in order, starting from
  string 0. The format is like that of the title.











=== Misc values ===

A number is written in decimal, and must be 0 to 65535.

A class name has $ at first.








>
>
>
>
>
>
>
>
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  imported, it will accept objects listed in any order, but when being
  exported from Free Hero Mesh, they are ordered as described above.

%string
  A level string. The level strings are numbered in order, starting from
  string 0. The format is like that of the title.

'key number
  Specify the key name (same as the "input constants" described in
  class.doc), and optionally the repeat count. This enters it into the
  solution; if at least one command like this is present then it will
  overwrite the existing solution when this level is saved. (Note: This
  does not automatically verify the solution; it only records it.)
  (Currently, this line is implemented only for import, and not for
  export. Later an option might be added to do this on export, too.)


=== Misc values ===

A number is written in decimal, and must be 0 to 65535.

A class name has $ at first.

Modified picture.c from [fcd841c5a5] to [ba5f92d7a9].

310
311
312
313
314
315
316
317
318





319
320
321
322
323
324
325
    }
  }
  return 0;
}

int screen_message(const char*txt) {
  int n=0;
  SDL_Rect r={0,0,screen->w,16};
  SDL_Event ev;





  SDL_FillRect(screen,&r,0xF4);
  r.y=16;
  r.h=1;
  SDL_FillRect(screen,&r,0xF8);
  SDL_LockSurface(screen);
  draw_text(0,0,txt,0xF4,0xFE);
  draw_text(0,8,"<Push ENTER to continue>",0xF4,0xF7);







|

>
>
>
>
>







310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
    }
  }
  return 0;
}

int screen_message(const char*txt) {
  int n=0;
  SDL_Rect r={0,0,0,16};
  SDL_Event ev;
  if(!screen) {
    fprintf(stderr," * %s\n",txt);
    return 0;
  }
  r.w=screen->w;
  SDL_FillRect(screen,&r,0xF4);
  r.y=16;
  r.h=1;
  SDL_FillRect(screen,&r,0xF8);
  SDL_LockSurface(screen);
  draw_text(0,0,txt,0xF4,0xFE);
  draw_text(0,8,"<Push ENTER to continue>",0xF4,0xF7);