Free Hero Mesh

Check-in [c4208ea179]
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:Start to implement the LEVELS table.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c4208ea17976e568bdd6c9eb1cc036f9f786ca68
User & Date: user on 2021-12-14 06:54:52
Other Links: manifest | tags
Context
2021-12-15
07:12
Auto-update the LEVELS table when it is appropriate to do so. check-in: 656b937843 user: user tags: trunk
2021-12-14
06:54
Start to implement the LEVELS table. check-in: c4208ea179 user: user tags: trunk
2021-12-10
00:00
Implement the (Control) block to define a global control object. check-in: 665dcd24be user: user tags: trunk
Changes

Modified TODO from [8cd629c2b8] to [6e5666c132].

12
13
14
15
16
17
18
19

20
21



22
23
24
25
26
27
28
12
13
14
15
16
17
18

19
20

21
22
23
24
25
26
27
28
29
30







-
+

-
+
+
+







  * "Goto message" instruction (?)
  * Returning a class from COLLIDE/COLLIDEBY to transform
  * Coordinate input (may be suitable for some kind of games)
  * Possibility to define auto-generation levels mode
* Editor
  * Mouse dragging
  * Level index editor
* Table of contents for levels
* Table of contents for levels (partially implemented)
  * Can define your own columns
  * User can write SQL queries on them
  * Update SOLVED (and SOLVABLE) column during playing
  * Cache invalidation in editor
  * Can be divisions by sections
* Deal better with allowing to skip past corrupted levels
* Picture editor/loading
  * Allowing more altimages
  * Batch insert multiple dependent image lumps
* Puzzle set catalog format (using with internet; a separate program)
* Inventory/replay hybrid view
* Bookmarks

Modified default.heromeshrc from [8008201b1c] to [cf39f14a84].

132
133
134
135
136
137
138


139
140
141
142
143
144
145
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







+
+







?.gameKey.alt.G: ^g
?.gameKey.alt.P: ^p
?.gameKey.alt.X: ^x
?.gameKey.alt.leftbracket: select 'rs',-5;
?.gameKey.alt.rightbracket: select 'rs',+5;
?.gameKey.delete: ^-
?.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;

! 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 function.c from [6d93dbc609] to [77ed877244].

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

13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30












+









+







#if 0
gcc ${CFLAGS:--s -O2} -c -fplan9-extensions function.c `sdl-config --cflags`
exit
#endif

#include "SDL.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "smallxrm.h"
#include "heromesh.h"
#include "cursorshapes.h"

typedef struct {
  struct sqlite3_vtab_cursor;
  sqlite3_int64 rowid;
  char unique,eof;
  Uint16 arg[4];
} Cursor;

static void*bizarro_vtab;
static char*levels_schema;

static void find_first_usable_image(const Class*cl,sqlite3_context*cxt) {
  int i;
  if(cl->cflags&CF_GROUP) return;
  if(!cl->images) return;
  for(i=0;i<cl->nimages;i++) {
    if(cl->images[i]&0x8000) {
1176
1177
1178
1179
1180
1181
1182


















































































1183
1184
1185
1186
1187
1188
1189
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








Module(vt_playfield,
  .xBestIndex=vt1_playfield_index,
  .xColumn=vt1_playfield_column,
  .xFilter=vt1_playfield_filter,
  .xNext=vt1_playfield_next,
);

static int vt1_levels_connect(sqlite3*db,void*aux,int argc,const char*const*argv,sqlite3_vtab**vt,char**err) {
  //TODO: Add columns specific to a puzzle set.
  levels_schema=sqlite3_mprintf("CREATE TEMPORARY TABLE `LEVELS`"
   "(`ID` INTEGER PRIMARY KEY, `ORD` INT, `CODE` INT, `WIDTH` INT, `HEIGHT` INT, `TITLE` BLOB, `SOLVED` INT, `SOLVABLE` INT);");
  if(!levels_schema) fatal("Allocation failed\n");
  sqlite3_declare_vtab(db,levels_schema);
  *vt=sqlite3_malloc(sizeof(sqlite3_vtab));
  return *vt?SQLITE_OK:SQLITE_NOMEM;
}

static int vt1_levels_open(sqlite3_vtab*vt,sqlite3_vtab_cursor**cur) {
  //TODO: Add columns specific to a puzzle set.
  sqlite3_stmt*st1;
  sqlite3_stmt*st2;
  const unsigned char*d;
  unsigned char*p;
  int i,j;
  long n;
  int txn=sqlite3_get_autocommit(userdb)?sqlite3_exec(userdb,"BEGIN;",0,0,0):1;
  if(!levels_schema) return SQLITE_CORRUPT_VTAB;
  if(screen) set_cursor(XC_coffee_mug);
  fprintf(stderr,"Loading level index...\n");
  if(sqlite3_exec(userdb,levels_schema,0,0,0)) {
    err: fatal("SQL error while loading LEVELS table: %s\n",sqlite3_errmsg(userdb));
  }
  sqlite3_free(levels_schema);
  levels_schema=0;
  if(sqlite3_prepare_v2(userdb,"SELECT `LEVEL`, IFNULL(`DATA`,READ_LUMP_AT(`OFFSET`,?1)), `USERSTATE` FROM `USERCACHEDATA`"
   " WHERE `FILE` = LEVEL_CACHEID() AND `LEVEL` NOT NULL AND `LEVEL` >= 0 ORDER BY `LEVEL`;",-1,&st1,0))
   goto err;
  // ?1=ID, ?2=CODE, ?3=WIDTH, ?4=HEIGHT, ?5=TITLE, ?6=SOLVED, ?7=SOLVABLE
  if(sqlite3_prepare_v3(userdb,"INSERT INTO `LEVELS` VALUES(?1,NULL,?2,?3,?4,?5,?6,?7);",-1,SQLITE_PREPARE_NO_VTAB,&st2,0))
   goto err;
  sqlite3_bind_pointer(st1,1,levelfp,"http://zzo38computer.org/fossil/heromesh.ui#FILE_ptr",0);
  while((i=sqlite3_step(st1))==SQLITE_ROW) {
    sqlite3_reset(st2);
    sqlite3_bind_int(st2,1,sqlite3_column_int(st1,0));
    d=sqlite3_column_blob(st1,1); n=sqlite3_column_bytes(st1,1);
    if(n<7 || !d) continue;
    sqlite3_bind_int(st2,2,d[2]|(d[3]<<8));
    sqlite3_bind_int(st2,3,(d[4]&63)+1);
    sqlite3_bind_int(st2,4,(d[5]&63)+1);
    for(i=6;i<n && d[i];i++);
    j=d[0]|(d[1]<<8);
    sqlite3_bind_blob(st2,5,d+6,i-6,0);
    p=read_lump(FIL_SOLUTION,sqlite3_column_int(st1,0),&n);
    if(p) {
      sqlite3_bind_int(st2,7,(n>2 && d[0]==p[0] && d[1]==p[1]));
      free(p);
    } else {
      sqlite3_bind_int(st2,7,0);
    }
    d=sqlite3_column_blob(st1,2); n=sqlite3_column_bytes(st1,2);
    sqlite3_bind_int(st2,6,(d && n>5 && n-(d[n-2]<<8)-d[n-1]>3 && (d[n-4]<<8)+d[n-3]==j));
    while((i=sqlite3_step(st2))==SQLITE_ROW);
    if(i!=SQLITE_DONE) goto err;
    sqlite3_bind_null(st2,5);
  }
  if(i!=SQLITE_DONE) goto err;
  sqlite3_finalize(st1);
  sqlite3_finalize(st2);
  if(sqlite3_prepare_v3(userdb,"UPDATE `LEVELS` SET `ORD` = ?1 WHERE `ID` = ?2;",-1,SQLITE_PREPARE_NO_VTAB,&st2,0))
   goto err;
  for(j=0;j<level_nindex;j++) {
    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,
  .xConnect=vt1_levels_connect,
  .xOpen=vt1_levels_open,
);

void init_sql_functions(sqlite3_int64*ptr0,sqlite3_int64*ptr1) {
  sqlite3_create_function(userdb,"BASENAME",0,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_basename,0,0);
  sqlite3_create_function(userdb,"BCAT",-1,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_bcat,0,0);
  sqlite3_create_function(userdb,"BYTE",-1,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_byte,0,0);
  sqlite3_create_function(userdb,"CL",1,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_cl,0,0);
  sqlite3_create_function(userdb,"CLASS_DATA",2,SQLITE_UTF8|SQLITE_DETERMINISTIC,0,fn_class_data,0,0);
1228
1229
1230
1231
1232
1233
1234

1235
1312
1313
1314
1315
1316
1317
1318
1319
1320







+

  sqlite3_create_module(userdb,"MESSAGES",&vt_messages,"CREATE TABLE `MESSAGES`(`ID` INTEGER PRIMARY KEY, `NAME` TEXT, `TRACE` INT);");
  sqlite3_create_module(userdb,"OBJECTS",&vt_objects,"CREATE TABLE `OBJECTS`(`ID` INTEGER PRIMARY KEY, `CLASS` INT, `MISC1` INT, `MISC2` INT, `MISC3` INT,"
   "`IMAGE` INT, `DIR` INT, `X` INT, `Y` INT, `UP` INT, `DOWN` INT, `DENSITY` INT HIDDEN, `BIZARRO` INT HIDDEN);");
  sqlite3_create_module(userdb,"BIZARRO_OBJECTS",&vt_objects,"CREATE TABLE `OBJECTS`(`ID` INTEGER PRIMARY KEY, `CLASS` INT, `MISC1` INT, `MISC2` INT, `MISC3` INT,"
   "`IMAGE` INT, `DIR` INT, `X` INT, `Y` INT, `UP` INT, `DOWN` INT, `DENSITY` INT HIDDEN, `BIZARRO` INT HIDDEN);");
  sqlite3_create_module(userdb,"INVENTORY",&vt_inventory,"CREATE TABLE `INVENTORY`(`ID` INTEGER PRIMARY KEY, `CLASS` INT, `IMAGE` INT, `VALUE` INT);");
  sqlite3_create_module(userdb,"PLAYFIELD",&vt_playfield,"CREATE TABLE `PLAYFIELD`(`X` INT, `Y` INT, `OBJ` INT, `BIZARRO_OBJ` INT);");
  sqlite3_create_module(userdb,"LEVELS",&vt_levels,0);
}

Modified game.doc from [fc79222cb9] to [9d856ff616].

135
136
137
138
139
140
141


142
143
144
145
146
147
148
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150







+
+







  F9          Flash player position
  F10         SQL queries
  ESC         Restart level
  TAB         Toggle inventory/replay display
  KP ENTER    Restart level
  KP +        Next level
  KP -        Previous level
  ALT+KP +    Next unsolved level
  ALT+KP -    Previous unsolved level
  SHIFT+KP +  Last level
  SHIFT+KP -  First level
  ALT+G       Inspect globals
  ALT+P       Begin slow replay
  ALT+[       Increase slow replay speed
  ALT+]       Decrease slow replay speed
  INS         Toggle insertion mode

Modified heromesh.h from [390cfc3e4a] to [94459dbf11].

50
51
52
53
54
55
56


57
58
59
60
61
62
63
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65







+
+







extern char main_options[128];
extern Uint8 message_trace[0x4100/8];
extern Uint16 level_id,level_ord,level_version,level_code;
extern unsigned char*level_title;
extern Uint16*level_index;
extern int level_nindex;
extern char level_changed; // 1 if solution is potentially invalidated by edits
extern FILE*levelfp;
extern FILE*solutionfp;

#ifdef __GNUC__
extern char stack_protect_mode;
extern void*stack_protect_mark;
extern void*stack_protect_low;
extern void*stack_protect_high;
#define StackProtection() (stack_protect_mode && ( \

Modified main.c from [d4c1330f51] to [3d54a4ff62].

50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68


69
70
71
72
73
74
75







+
+










-
-







char main_options[128];
Uint8 message_trace[0x4100/8];
Uint16 level_id,level_ord,level_version,level_code;
unsigned char*level_title;
Uint16*level_index;
int level_nindex;
char level_changed;
FILE*levelfp;
FILE*solutionfp;

#ifdef __GNUC__
char stack_protect_mode=0;
void*stack_protect_mark;
void*stack_protect_low;
void*stack_protect_high;
#endif

static const char*globalclassname;
static SDL_Cursor*cursor[77];
static FILE*levelfp;
static FILE*solutionfp;
static FILE*compositefp;
static sqlite3_int64 leveluc,solutionuc;
static sqlite3_stmt*readusercachest;
static char*hpath;

typedef struct {
  FILE*fp;

Modified sql.doc from [4e9feff88b] to [556de4a794].

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
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
195
196
197
198
199
200
201
202







+
+
+
+
+














+
+
+
+
+
+
+
+
+
+
+







  Zero extends a 32-bit number to 64-bits. Same as NVALUE.


=== Tables ===

Asterisks denote virtual tables. (The only disk tables are USERCACHEDATA
and USERCACHEINDEX; all others are eiter temporary or virtual.)

CREATE TABLE "BIZARRO_OBJECTS"("ID" INTEGER PRIMARY KEY, "CLASS" INT,
"MISC1" INT, "MISC2" INT, "MISC3" INT, "IMAGE" INT, "DIR" INT, "X" INT,
"Y" INT, "UP" INT, "DOWN" INT, "DENSITY" INT HIDDEN); *
  See OBJECTS; this is for the bizarro world only.

CREATE TABLE "CLASSES"("ID" INTEGER PRIMARY KEY, "NAME" TEXT, "EDITORHELP"
TEXT, "HELP" TEXT, "INPUT" INT, "QUIZ" INT, "TRACEIN" INT, "TRACEOUT" INT,
"GROUP" TEXT, "PLAYER" INT); *
  A list of classes in the current puzzle set; mostly read-only. Only
  QUIZ, TRACEIN, and TRACEOUT are writable. If TRACEIN is true then it
  will trace messages received by this class (if tracing is enabled). If
  TRACEOUT is true then it will trace messages sent by this class (if
  tracing is enabled).

CREATE TABLE "INVENTORY"("ID" INTEGER PRIMARY KEY, "CLASS" INT, "IMAGE"
INT, "VALUE" INT); *
  This table contains the current inventory, and is read-only. It is not
  meaningful in the editor.

CREATE TEMPORARY TABLE "LEVELS"("ID" INTEGER PRIMARY KEY, "ORD" INT,
"CODE" INT, "WIDTH" INT, "HEIGHT" INT, "TITLE" BLOB, "SOLVED" INT,
"SOLVABLE" INT); *
  This table contains a list of the levels in the current puzzle set. The
  SOLVED column is 1 if the current user has solved the level or 0
  otherwise; the SOLVABLE column is 1 if a solution is stored for this
  level or 0 otherwise (it doesn't validate the solution; to do that, use
  the -a switch instead). Although you can write to this table after it
  has been read once (this table uses lazy loading), you should not do so,
  since the changes will not be saved.

CREATE TABLE "MESSAGES"("ID" INTEGER PRIMARY KEY, "NAME" TEXT, "TRACE"
INT); *
  The list of messages in the current puzzle set; mostly read-only. Only
  TRACE is writable; if true, this message will be traced.

CREATE TABLE "OBJECTS"("ID" INTEGER PRIMARY KEY, "CLASS" INT, "MISC1" INT,