Map editor

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-02-18 17:17:33 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-02-18 17:17:33 +0100
Commit 148bd1115e328defead3205878039e8cc598712e (patch)
-rw-r--r-- Makefile 12
-rw-r--r-- game.c 387
-rw-r--r-- mapeditor.html 722
-rw-r--r-- maps/map1.h 348
-rw-r--r-- maps/map1.txt 40
5 files changed, 1460 insertions, 49 deletions
diff --git a/Makefile b/Makefile
...
13
PROMPT_TXT := $(wildcard prompts/*.txt)
13
PROMPT_TXT := $(wildcard prompts/*.txt)
14
PROMPT_HEADERS := $(PROMPT_TXT:.txt=.h)
14
PROMPT_HEADERS := $(PROMPT_TXT:.txt=.h)
15
  
15
  
  
16
MAP_TXT := $(wildcard maps/*.txt)
  
17
MAP_HEADERS := $(MAP_TXT:.txt=.h)
  
18
  
16
help: .help
19
help: .help
17
  
20
  
18
build/llama.cpp: .assure # Build llama.cpp libraries
21
build/llama.cpp: .assure # Build llama.cpp libraries
...
27
build/npc: build/prompts npc.c vectordb.c models.h # Build npc binary for testing
30
build/npc: build/prompts npc.c vectordb.c models.h # Build npc binary for testing
28
	$(CC) $(CFLAGS) npc.c vectordb.c -o npc $(LDFLAGS)
31
	$(CC) $(CFLAGS) npc.c vectordb.c -o npc $(LDFLAGS)
29
  
32
  
30
build/game: build/prompts game.c vectordb.c models.h # Build npc binary for testing
33
build/game: build/prompts build/maps game.c vectordb.c models.h # Build npc binary for testing
31
	$(CC) $(CFLAGS) game.c vectordb.c -o game $(LDFLAGS)
34
	$(CC) $(CFLAGS) game.c vectordb.c -o game $(LDFLAGS)
32
  
35
  
33
build/prompts: $(PROMPT_HEADERS) # Generate C style header
36
build/prompts: $(PROMPT_HEADERS) # Generate prompts in C style header
  
37
  
  
38
build/maps: $(MAP_HEADERS) # Generate maps in  C style header
34
  
39
  
35
run/fetch-models: .assure # Fetch GGUF models
40
run/fetch-models: .assure # Fetch GGUF models
36
	-mkdir -p models
41
	-mkdir -p models
...
47
  
52
  
48
prompts/%.h: prompts/%.txt .assure
53
prompts/%.h: prompts/%.txt .assure
49
	xxd -i $< > $@
54
	xxd -i $< > $@
  
55
  
  
56
maps/%.h: maps/%.txt .assure
  
57
	xxd -i $< > $@
diff --git a/game.c b/game.c
...
3
#define TB_IMPL
3
#define TB_IMPL
4
#include "termbox2.h"
4
#include "termbox2.h"
5
  
5
  
  
6
#define NONSTD_IMPLEMENTATION
  
7
#include "nonstd.h"
  
8
  
  
9
#include "maps/map1.h"
  
10
  
6
#define MIN_W 40
11
#define MIN_W 40
7
#define MIN_H 12
12
#define MIN_H 12
8
#define SIDEBAR_W 40
13
#define SIDEBAR_W 40
...
12
#define CP_TR 0x2510
17
#define CP_TR 0x2510
13
#define CP_BL 0x2514
18
#define CP_BL 0x2514
14
#define CP_BR 0x2518
19
#define CP_BR 0x2518
  
20
#define MAP_FLOOR_CH '.'
  
21
#define MAP_BORDER_MIN 0x2500
  
22
#define MAP_BORDER_MAX 0x257f
  
23
#define MAP_FLOOR_FG 234
  
24
#define COLOR_WHITE_256 0x0f
  
25
#define COLOR_RED_256 161
  
26
#define COLOR_GREEN_256 0x2e
  
27
#define COLOR_BORDER_256 101
  
28
#define COLOR_CYAN_256 0x33
  
29
#define COLOR_ORANGE_256 0xd0
  
30
#define COLOR_BLUE_256 0x1b
  
31
  
  
32
typedef struct {
  
33
	char key;
  
34
	const char *name;
  
35
} InventoryItem;
  
36
  
  
37
typedef struct {
  
38
	array(InventoryItem) items;
  
39
} Inventory;
  
40
  
  
41
typedef struct {
  
42
	int x;
  
43
	int y;
  
44
	int hp;
  
45
	int hp_max;
  
46
	int ac;
  
47
	int str;
  
48
	int gold;
  
49
	Inventory inventory;
  
50
} Player;
  
51
  
  
52
typedef struct {
  
53
	const unsigned char *data;
  
54
	int len;
  
55
	int width;
  
56
	int height;
  
57
	u32 *cells;
  
58
} Map;
  
59
  
  
60
static int clamp(int value, int min, int max);
15
  
61
  
16
static void draw_border(int x, int y, int w, int h, uintattr_t fg) {
62
static void draw_border(int x, int y, int w, int h, uintattr_t fg) {
17
	int ix;
63
	int ix;
...
52
	*msg2_y = h - 1;
98
	*msg2_y = h - 1;
53
}
99
}
54
  
100
  
55
static void draw_map(int map_x, int map_y, int map_w, int map_h, int px,
101
static void map_init(Map *map, const unsigned char *data, int len) {
56
	int py) {
102
	array(int) line_lengths;
57
	if (px >= 0 && py >= 0) {
103
	int width = 0;
58
		tb_set_cell(map_x + px, map_y + py, '@', TB_WHITE | TB_BOLD, TB_DEFAULT);
104
	int height = 0;
  
105
	int line_len = 0;
  
106
	int i = 0;
  
107
  
  
108
	map->data = data;
  
109
	map->len = len;
  
110
	map->cells = NULL;
  
111
	array_init(line_lengths);
  
112
	while (i < len) {
  
113
		uint32_t ch = 0;
  
114
		int consumed = tb_utf8_char_to_unicode(&ch, (const char *)&data[i]);
  
115
		if (consumed <= 0) {
  
116
			i++;
  
117
			continue;
  
118
		}
  
119
		i += consumed;
  
120
		if (ch == '\n') {
  
121
			array_push(line_lengths, line_len);
  
122
			if (line_len > width) {
  
123
				width = line_len;
  
124
			}
  
125
			height++;
  
126
			line_len = 0;
  
127
		} else {
  
128
			line_len++;
  
129
		}
  
130
	}
  
131
	if (line_len > 0 || (len > 0 && data[len - 1] != '\n')) {
  
132
		array_push(line_lengths, line_len);
  
133
		if (line_len > width) {
  
134
			width = line_len;
  
135
		}
  
136
		height++;
  
137
	}
  
138
  
  
139
	map->width = width;
  
140
	map->height = height;
  
141
	if (width > 0 && height > 0) {
  
142
		map->cells = ALLOC(u32, (usize)width * (usize)height);
  
143
		for (int y = 0; y < height; y++) {
  
144
			for (int x = 0; x < width; x++) {
  
145
				map->cells[(y * width) + x] = ' ';
  
146
			}
  
147
		}
  
148
	}
  
149
  
  
150
	i = 0;
  
151
	int x = 0;
  
152
	int y = 0;
  
153
	while (i < len && y < height) {
  
154
		uint32_t ch = 0;
  
155
		int consumed = tb_utf8_char_to_unicode(&ch, (const char *)&data[i]);
  
156
		if (consumed <= 0) {
  
157
			i++;
  
158
			continue;
  
159
		}
  
160
		i += consumed;
  
161
		if (ch == '\n') {
  
162
			y++;
  
163
			x = 0;
  
164
			continue;
  
165
		}
  
166
		if (map->cells && x < width) {
  
167
			map->cells[(y * width) + x] = ch;
  
168
		}
  
169
		x++;
  
170
	}
  
171
	array_free(line_lengths);
  
172
}
  
173
  
  
174
static u32 map_get(const Map *map, int x, int y) {
  
175
	if (!map->cells || x < 0 || y < 0 || x >= map->width || y >= map->height) {
  
176
		return ' ';
  
177
	}
  
178
	return map->cells[(y * map->width) + x];
  
179
}
  
180
  
  
181
static void map_set(Map *map, int x, int y, u32 ch) {
  
182
	if (!map->cells || x < 0 || y < 0 || x >= map->width || y >= map->height) {
  
183
		return;
  
184
	}
  
185
	map->cells[(y * map->width) + x] = ch;
  
186
}
  
187
  
  
188
static int map_is_walkable(const Map *map, int x, int y) {
  
189
	u32 ch = map_get(map, x, y);
  
190
	return ch == MAP_FLOOR_CH || ch == '$';
  
191
}
  
192
  
  
193
static void map_free(Map *map) {
  
194
	FREE(map->cells);
  
195
}
  
196
  
  
197
static void update_camera(const Map *map, int view_w, int view_h,
  
198
	const Player *player, int *cam_x, int *cam_y) {
  
199
	int max_cam_x;
  
200
	int max_cam_y;
  
201
	int margin_x;
  
202
	int margin_y;
  
203
	int next_x = *cam_x;
  
204
	int next_y = *cam_y;
  
205
  
  
206
	if (view_w <= 0 || view_h <= 0 || map->width <= 0 || map->height <= 0) {
  
207
		*cam_x = 0;
  
208
		*cam_y = 0;
  
209
		return;
  
210
	}
  
211
  
  
212
	margin_x = view_w > 8 ? 3 : view_w / 3;
  
213
	margin_y = view_h > 8 ? 3 : view_h / 3;
  
214
	max_cam_x = map->width - view_w;
  
215
	max_cam_y = map->height - view_h;
  
216
	if (max_cam_x < 0) {
  
217
		max_cam_x = 0;
  
218
	}
  
219
	if (max_cam_y < 0) {
  
220
		max_cam_y = 0;
  
221
	}
  
222
  
  
223
	if (player->x < next_x + margin_x) {
  
224
		next_x = player->x - margin_x;
  
225
	} else if (player->x > next_x + view_w - 1 - margin_x) {
  
226
		next_x = player->x - (view_w - 1 - margin_x);
  
227
	}
  
228
	if (player->y < next_y + margin_y) {
  
229
		next_y = player->y - margin_y;
  
230
	} else if (player->y > next_y + view_h - 1 - margin_y) {
  
231
		next_y = player->y - (view_h - 1 - margin_y);
  
232
	}
  
233
  
  
234
	*cam_x = clamp(next_x, 0, max_cam_x);
  
235
	*cam_y = clamp(next_y, 0, max_cam_y);
  
236
}
  
237
  
  
238
static void draw_map(const Map *map, int map_x, int map_y, int view_w,
  
239
	int view_h, const Player *player, int cam_x, int cam_y) {
  
240
	int ix;
  
241
	int iy;
  
242
  
  
243
	for (iy = 0; iy < view_h; iy++) {
  
244
		for (ix = 0; ix < view_w; ix++) {
  
245
			int mx = cam_x + ix;
  
246
			int my = cam_y + iy;
  
247
			u32 ch = map_get(map, mx, my);
  
248
			uintattr_t fg = COLOR_WHITE_256;
  
249
			if (ch == MAP_FLOOR_CH) {
  
250
				fg = MAP_FLOOR_FG;
  
251
			} else if (ch == '~') {
  
252
				fg = COLOR_BLUE_256;
  
253
			} else if (ch == '$') {
  
254
				fg = COLOR_ORANGE_256;
  
255
			} else if (ch == 'B' || ch == 'S' || ch == 'G') {
  
256
				fg = COLOR_RED_256;
  
257
			} else if (ch == 'N') {
  
258
				fg = COLOR_CYAN_256;
  
259
			} else if (ch >= MAP_BORDER_MIN && ch <= MAP_BORDER_MAX) {
  
260
				fg = COLOR_BORDER_256;
  
261
			}
  
262
			tb_set_cell(map_x + ix, map_y + iy, ch, fg, TB_DEFAULT);
  
263
		}
  
264
	}
  
265
  
  
266
	if (player->x >= cam_x && player->x < cam_x + view_w && player->y >= cam_y
  
267
		&& player->y < cam_y + view_h) {
  
268
		int sx = map_x + (player->x - cam_x);
  
269
		int sy = map_y + (player->y - cam_y);
  
270
		tb_set_cell(sx, sy, '@', COLOR_GREEN_256 | TB_BOLD, TB_DEFAULT);
59
	}
271
	}
60
}
272
}
61
  
273
  
...
78
	}
290
	}
79
  
291
  
80
	filled = (inner_w * value) / max;
292
	filled = (inner_w * value) / max;
81
	tb_set_cell(x, y, '[', TB_WHITE, TB_DEFAULT);
293
	tb_set_cell(x, y, '[', COLOR_WHITE_256, TB_DEFAULT);
82
	for (ix = 0; ix < inner_w; ix++) {
294
	for (ix = 0; ix < inner_w; ix++) {
83
		uintattr_t fg = ix < filled ? TB_GREEN : TB_WHITE;
295
	uintattr_t fg = ix < filled ? COLOR_GREEN_256 : COLOR_WHITE_256;
84
		uint32_t ch = ix < filled ? '=' : ' ';
296
		uint32_t ch = ix < filled ? '=' : ' ';
85
		tb_set_cell(x + 1 + ix, y, ch, fg, TB_DEFAULT);
297
		tb_set_cell(x + 1 + ix, y, ch, fg, TB_DEFAULT);
86
	}
298
	}
87
	tb_set_cell(x + w - 1, y, ']', TB_WHITE, TB_DEFAULT);
299
	tb_set_cell(x + w - 1, y, ']', COLOR_WHITE_256, TB_DEFAULT);
  
300
}
  
301
  
  
302
static void inventory_init(Inventory *inv) {
  
303
	array_init(inv->items);
  
304
}
  
305
  
  
306
static void inventory_add(Inventory *inv, char key, const char *name) {
  
307
	InventoryItem item = {.key = key, .name = name};
  
308
	array_push(inv->items, item);
  
309
}
  
310
  
  
311
static void inventory_free(Inventory *inv) {
  
312
	array_free(inv->items);
  
313
}
  
314
  
  
315
static void player_init(Player *player) {
  
316
	player->x = 6;
  
317
	player->y = 4;
  
318
	player->hp = 12;
  
319
	player->hp_max = 12;
  
320
	player->ac = 7;
  
321
	player->str = 16;
  
322
	player->gold = 42;
  
323
	inventory_init(&player->inventory);
  
324
	inventory_add(&player->inventory, 'a', "dagger");
  
325
	inventory_add(&player->inventory, 'b', "ration");
  
326
	inventory_add(&player->inventory, 'c', "potion");
  
327
	inventory_add(&player->inventory, 'd', "scroll");
88
}
328
}
89
  
329
  
90
static void draw_stats(int x, int y) {
330
static void player_free(Player *player) {
91
	tb_print(x, y, TB_WHITE | TB_BOLD, TB_DEFAULT, "Stats");
331
	inventory_free(&player->inventory);
92
	tb_print(x, y + 2, TB_WHITE, TB_DEFAULT, "HP 12/12");
  
93
	draw_progress_bar(x, y + 3, 18, 12, 12);
  
94
	tb_print(x, y + 4, TB_WHITE, TB_DEFAULT, "AC: 7");
  
95
	tb_print(x, y + 5, TB_WHITE, TB_DEFAULT, "Str: 16");
  
96
	tb_print(x, y + 6, TB_WHITE, TB_DEFAULT, "Gold: 42");
  
97
}
332
}
98
  
333
  
99
static void draw_inventory(int x, int y) {
334
static void draw_stats(int x, int y, const Player *player) {
100
	tb_print(x, y, TB_WHITE | TB_BOLD, TB_DEFAULT, "Inventory");
335
	tb_print(x, y, COLOR_WHITE_256 | TB_BOLD, TB_DEFAULT, "Stats");
101
	tb_print(x, y + 2, TB_WHITE, TB_DEFAULT, "a) dagger");
336
	tb_printf(x, y + 2, COLOR_WHITE_256, TB_DEFAULT, "HP %d/%d", player->hp, player->hp_max);
102
	tb_print(x, y + 3, TB_WHITE, TB_DEFAULT, "b) ration");
337
	draw_progress_bar(x, y + 3, 18, player->hp, player->hp_max);
103
	tb_print(x, y + 4, TB_WHITE, TB_DEFAULT, "c) potion");
338
	tb_printf(x, y + 4, COLOR_WHITE_256, TB_DEFAULT, "AC: %d", player->ac);
104
	tb_print(x, y + 5, TB_WHITE, TB_DEFAULT, "d) scroll");
339
	tb_printf(x, y + 5, COLOR_WHITE_256, TB_DEFAULT, "Str: %d", player->str);
  
340
	tb_printf(x, y + 6, COLOR_WHITE_256, TB_DEFAULT, "Gold: %d", player->gold);
  
341
}
  
342
  
  
343
static void draw_inventory(int x, int y, const Inventory *inv) {
  
344
	InventoryItem item;
  
345
	usize idx = 0;
  
346
  
  
347
	tb_print(x, y, COLOR_WHITE_256 | TB_BOLD, TB_DEFAULT, "Inventory");
  
348
	array_foreach(inv->items, item) {
  
349
		tb_printf(x, y + 2 + (int)idx, COLOR_WHITE_256, TB_DEFAULT, "%c) %s", item.key, item.name);
  
350
		idx++;
  
351
	}
105
}
352
}
106
  
353
  
107
static const char *status_msg = "";
354
static const char *status_msg = "";
...
110
	status_msg = message ? message : "";
357
	status_msg = message ? message : "";
111
}
358
}
112
  
359
  
113
static void render(int px, int py, int *out_map_w, int *out_map_h) {
360
static void render(const Map *map, const Player *player, int *cam_x,
  
361
	int *cam_y, int *out_view_w, int *out_view_h) {
114
	int w;
362
	int w;
115
	int h;
363
	int h;
116
	int map_x;
364
	int map_x;
...
131
	int inv_h;
379
	int inv_h;
132
	int msg1_y;
380
	int msg1_y;
133
	int msg2_y;
381
	int msg2_y;
  
382
	int view_w;
  
383
	int view_h;
  
384
	int draw_w;
  
385
	int draw_h;
  
386
	int pad_x;
  
387
	int pad_y;
134
  
388
  
135
	w = tb_width();
389
	w = tb_width();
136
	h = tb_height();
390
	h = tb_height();
...
138
  
392
  
139
	tb_clear();
393
	tb_clear();
140
	if (w < MIN_W || h < MIN_H || map_w < 8 || map_h < 3) {
394
	if (w < MIN_W || h < MIN_H || map_w < 8 || map_h < 3) {
141
		tb_print(1, 1, TB_RED | TB_BOLD, TB_DEFAULT, "Window too small. Resize to at least 40x12.");
395
		tb_print(1, 1, COLOR_RED_256 | TB_BOLD, TB_DEFAULT, "Window too small. Resize to at least 40x12.");
142
		tb_present();
396
		tb_present();
143
		*out_map_w = map_w;
397
		*out_view_w = map_w;
144
		*out_map_h = map_h;
398
		*out_view_h = map_h;
145
		return;
399
		return;
146
	}
400
	}
147
  
401
  
148
	draw_border(map_x, map_y, map_w, map_h, TB_WHITE);
402
	view_w = map_w - 2;
149
	draw_map(map_x + 1, map_y + 1, map_w - 2, map_h - 2, px, py);
403
	view_h = map_h - 2;
  
404
	draw_w = view_w;
  
405
	draw_h = view_h;
  
406
	if (map->width < draw_w) {
  
407
		draw_w = map->width;
  
408
	}
  
409
	if (map->height < draw_h) {
  
410
		draw_h = map->height;
  
411
	}
  
412
	pad_x = view_w > draw_w ? (view_w - draw_w) / 2 : 0;
  
413
	pad_y = view_h > draw_h ? (view_h - draw_h) / 2 : 0;
  
414
  
  
415
	draw_border(map_x, map_y, map_w, map_h, COLOR_WHITE_256);
  
416
	update_camera(map, view_w, view_h, player, cam_x, cam_y);
  
417
	draw_map(map, map_x + 1 + pad_x, map_y + 1 + pad_y, draw_w, draw_h, player, *cam_x, *cam_y);
150
  
418
  
151
	stats_x = side_x;
419
	stats_x = side_x;
152
	stats_y = side_y;
420
	stats_y = side_y;
...
157
	inv_w = side_w;
425
	inv_w = side_w;
158
	inv_h = side_h - stats_h;
426
	inv_h = side_h - stats_h;
159
	if (stats_w >= 12 && stats_h >= 9) {
427
	if (stats_w >= 12 && stats_h >= 9) {
160
		draw_border(stats_x, stats_y, stats_w, stats_h, TB_WHITE);
428
		draw_border(stats_x, stats_y, stats_w, stats_h, COLOR_WHITE_256);
161
		draw_stats(stats_x + 2, stats_y + 1);
429
		draw_stats(stats_x + 2, stats_y + 1, player);
162
	}
430
	}
163
	if (inv_w >= 12 && inv_h >= 7) {
431
	if (inv_w >= 12 && inv_h >= 7) {
164
		draw_border(inv_x, inv_y, inv_w, inv_h, TB_WHITE);
432
		draw_border(inv_x, inv_y, inv_w, inv_h, COLOR_WHITE_256);
165
		draw_inventory(inv_x + 2, inv_y + 1);
433
		draw_inventory(inv_x + 2, inv_y + 1, &player->inventory);
166
	}
434
	}
167
  
435
  
168
	tb_print(2, msg1_y, TB_GREEN, TB_DEFAULT, status_msg);
436
	tb_print(2, msg1_y, COLOR_GREEN_256, TB_DEFAULT, status_msg);
169
	tb_print(2, msg2_y, TB_WHITE, TB_DEFAULT, "Move: arrows  Quit: q/ESC");
437
	tb_print(2, msg2_y, COLOR_WHITE_256, TB_DEFAULT, "Move: arrows  Quit: q/ESC");
170
  
438
  
171
	tb_present();
439
	tb_present();
172
	*out_map_w = map_w - 2;
440
	*out_view_w = view_w;
173
	*out_map_h = map_h - 2;
441
	*out_view_h = view_h;
174
}
442
}
175
  
443
  
176
static int clamp(int value, int min, int max) {
444
static int clamp(int value, int min, int max) {
...
184
}
452
}
185
  
453
  
186
int main(void) {
454
int main(void) {
187
	int px = 6;
455
	Player player = {0};
188
	int py = 4;
456
	Map map = {0};
189
	int running = 1;
457
	int running = 1;
190
	int map_w = 0;
458
	int view_w = 0;
191
	int map_h = 0;
459
	int view_h = 0;
  
460
	int cam_x = 0;
  
461
	int cam_y = 0;
  
462
  
  
463
	player_init(&player);
  
464
	map_init(&map, maps_map1_txt, (int)maps_map1_txt_len);
192
  
465
  
193
	if (tb_init() != TB_OK) {
466
	if (tb_init() != TB_OK) {
194
		fprintf(stderr, "Failed to init termbox.\n");
467
		fprintf(stderr, "Failed to init termbox.\n");
...
196
	}
469
	}
197
  
470
  
198
	tb_set_input_mode(TB_INPUT_ESC);
471
	tb_set_input_mode(TB_INPUT_ESC);
  
472
	tb_set_output_mode(TB_OUTPUT_256);
199
	update_status("You feel like you have a lot of potential.");
473
	update_status("You feel like you have a lot of potential.");
200
	while (running) {
474
	while (running) {
201
		struct tb_event ev;
475
		struct tb_event ev;
202
  
476
  
203
		render(px, py, &map_w, &map_h);
477
		render(&map, &player, &cam_x, &cam_y, &view_w, &view_h);
204
		tb_poll_event(&ev);
478
		tb_poll_event(&ev);
205
		if (ev.type == TB_EVENT_KEY) {
479
		if (ev.type == TB_EVENT_KEY) {
206
			if (ev.key == TB_KEY_ESC || ev.ch == 'q') {
480
			if (ev.key == TB_KEY_ESC || ev.ch == 'q') {
207
				running = 0;
481
				running = 0;
208
			} else if (ev.key == TB_KEY_ARROW_UP) {
482
			} else if (ev.key == TB_KEY_ARROW_UP) {
209
				py -= 1;
483
				int next_y = player.y - 1;
  
484
				if (map_is_walkable(&map, player.x, next_y)) {
  
485
					player.y = next_y;
  
486
				}
210
			} else if (ev.key == TB_KEY_ARROW_DOWN) {
487
			} else if (ev.key == TB_KEY_ARROW_DOWN) {
211
				py += 1;
488
				int next_y = player.y + 1;
  
489
				if (map_is_walkable(&map, player.x, next_y)) {
  
490
					player.y = next_y;
  
491
				}
212
			} else if (ev.key == TB_KEY_ARROW_LEFT) {
492
			} else if (ev.key == TB_KEY_ARROW_LEFT) {
213
				px -= 1;
493
				int next_x = player.x - 1;
  
494
				if (map_is_walkable(&map, next_x, player.y)) {
  
495
					player.x = next_x;
  
496
				}
214
			} else if (ev.key == TB_KEY_ARROW_RIGHT) {
497
			} else if (ev.key == TB_KEY_ARROW_RIGHT) {
215
				px += 1;
498
				int next_x = player.x + 1;
  
499
				if (map_is_walkable(&map, next_x, player.y)) {
  
500
					player.x = next_x;
  
501
				}
216
			}
502
			}
217
			px = clamp(px, 0, map_w > 1 ? map_w - 1 : 0);
503
			if (map_get(&map, player.x, player.y) == '$') {
218
			py = clamp(py, 0, map_h > 1 ? map_h - 1 : 0);
504
				player.gold += 10;
  
505
				map_set(&map, player.x, player.y, MAP_FLOOR_CH);
  
506
				update_status("You pick up 10 gold.");
  
507
			}
  
508
			player.x = clamp(player.x, 0, map.width > 1 ? map.width - 1 : 0);
  
509
			player.y = clamp(player.y, 0, map.height > 1 ? map.height - 1 : 0);
219
		} else if (ev.type == TB_EVENT_RESIZE) {
510
		} else if (ev.type == TB_EVENT_RESIZE) {
220
			px = clamp(px, 0, map_w > 1 ? map_w - 1 : 0);
511
			player.x = clamp(player.x, 0, map.width > 1 ? map.width - 1 : 0);
221
			py = clamp(py, 0, map_h > 1 ? map_h - 1 : 0);
512
			player.y = clamp(player.y, 0, map.height > 1 ? map.height - 1 : 0);
222
		}
513
		}
223
	}
514
	}
224
  
515
  
  
516
	player_free(&player);
  
517
	map_free(&map);
225
	tb_shutdown();
518
	tb_shutdown();
226
	return 0;
519
	return 0;
227
}
520
}
diff --git a/mapeditor.html b/mapeditor.html
  
1
<!doctype html>
  
2
<html lang="en">
  
3
  <head>
  
4
    <meta charset="utf-8" />
  
5
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  
6
    <title>ASCII Map Editor</title>
  
7
    <style>
  
8
      :root {
  
9
        color-scheme: light;
  
10
        --gridline: rgba(0, 0, 0, 0.1);
  
11
        --mono: "SFMono-Regular", Menlo, Consolas, monospace;
  
12
      }
  
13
  
  
14
      * {
  
15
        box-sizing: border-box;
  
16
      }
  
17
  
  
18
      body {
  
19
        margin: 0;
  
20
        font-family: system-ui, sans-serif;
  
21
        background: #f5f5f5;
  
22
        color: #222;
  
23
        height: 100vh;
  
24
        overflow: hidden;
  
25
        display: flex;
  
26
        flex-direction: column;
  
27
      }
  
28
  
  
29
      header {
  
30
        padding: 16px 20px 8px;
  
31
      }
  
32
  
  
33
      header h1 {
  
34
        margin: 0 0 6px;
  
35
        font-size: 1.2rem;
  
36
      }
  
37
  
  
38
      header p {
  
39
        margin: 0;
  
40
        color: #555;
  
41
      }
  
42
  
  
43
      .layout {
  
44
        display: grid;
  
45
        grid-template-columns: 280px 1fr;
  
46
        gap: 16px;
  
47
        padding: 0 20px 20px;
  
48
        flex: 1;
  
49
        min-height: 0;
  
50
      }
  
51
  
  
52
      .panel,
  
53
      .grid-wrap {
  
54
        background: #fff;
  
55
        border: 1px solid #ddd;
  
56
        border-radius: 6px;
  
57
        padding: 12px;
  
58
        overflow: auto;
  
59
        min-height: 0;
  
60
      }
  
61
  
  
62
      .panel h2 {
  
63
        font-size: 0.9rem;
  
64
        margin: 0 0 10px;
  
65
      }
  
66
  
  
67
      .controls {
  
68
        display: grid;
  
69
        gap: 12px;
  
70
      }
  
71
  
  
72
      .control-group {
  
73
        display: grid;
  
74
        gap: 8px;
  
75
        padding-bottom: 8px;
  
76
        border-bottom: 1px solid #eee;
  
77
      }
  
78
  
  
79
      .control-group:last-child {
  
80
        border-bottom: 0;
  
81
        padding-bottom: 0;
  
82
      }
  
83
  
  
84
      .row {
  
85
        display: flex;
  
86
        align-items: center;
  
87
        justify-content: space-between;
  
88
        gap: 8px;
  
89
      }
  
90
  
  
91
      .palette {
  
92
        display: grid;
  
93
        grid-template-columns: repeat(4, 1fr);
  
94
        gap: 6px;
  
95
      }
  
96
  
  
97
      .tile-btn {
  
98
        border: 1px solid #ccc;
  
99
        border-radius: 4px;
  
100
        padding: 6px 0;
  
101
        background: #f7f7f7;
  
102
        font-family: var(--mono);
  
103
        cursor: pointer;
  
104
      }
  
105
  
  
106
      .tile-btn.active {
  
107
        outline: 2px solid #2b7fff;
  
108
        border-color: #2b7fff;
  
109
      }
  
110
  
  
111
      .button {
  
112
        background: #2b7fff;
  
113
        color: #fff;
  
114
        border: none;
  
115
        border-radius: 4px;
  
116
        padding: 6px 10px;
  
117
        cursor: pointer;
  
118
      }
  
119
  
  
120
      .button.secondary {
  
121
        background: #fff;
  
122
        color: #222;
  
123
        border: 1px solid #ccc;
  
124
      }
  
125
  
  
126
      input[type="range"] {
  
127
        width: 100%;
  
128
      }
  
129
  
  
130
      .stat {
  
131
        font-family: var(--mono);
  
132
        color: #2b7fff;
  
133
        font-size: 0.85rem;
  
134
      }
  
135
  
  
136
      #gridCanvas {
  
137
        display: block;
  
138
        background: #fff;
  
139
        border: 1px solid #ddd;
  
140
        image-rendering: pixelated;
  
141
        cursor: crosshair;
  
142
      }
  
143
  
  
144
      .footer-note {
  
145
        color: #666;
  
146
        font-size: 0.85rem;
  
147
      }
  
148
  
  
149
      @media (max-width: 980px) {
  
150
        .layout {
  
151
          grid-template-columns: 1fr;
  
152
        }
  
153
      }
  
154
    </style>
  
155
  </head>
  
156
  <body>
  
157
    <header>
  
158
      <h1>ASCII Map Editor</h1>
  
159
      <p>Paint tiles directly onto the grid. Export keeps the same text layout as your map files.</p>
  
160
    </header>
  
161
    <div class="layout">
  
162
      <aside class="panel">
  
163
        <h2>Tools</h2>
  
164
        <div class="controls">
  
165
          <div class="control-group">
  
166
            <div class="row">
  
167
              <span>Tile</span>
  
168
              <span class="stat" id="activeTile">#</span>
  
169
            </div>
  
170
            <div class="palette" id="palette"></div>
  
171
          </div>
  
172
          <div class="control-group">
  
173
            <div class="row">
  
174
              <span>Brush</span>
  
175
              <span class="stat" id="brushLabel">1</span>
  
176
            </div>
  
177
            <input type="range" id="brushSize" min="1" max="5" value="1" />
  
178
          </div>
  
179
          <div class="control-group">
  
180
            <div class="row">
  
181
        <span>Grid</span>
  
182
              <span class="stat" id="gridSize">0 x 0</span>
  
183
            </div>
  
184
            <div class="row">
  
185
              <button class="button secondary" id="toggleGrid">Toggle Gridlines</button>
  
186
              <button class="button secondary" id="clearMap">Clear</button>
  
187
            </div>
  
188
            <div class="row">
  
189
              <button class="button secondary" id="addBorder">Add Border</button>
  
190
              <button class="button secondary" id="addDoubleBorder">Add Double Border</button>
  
191
            </div>
  
192
            <div class="row">
  
193
              <label>
  
194
                W
  
195
                <input type="number" id="gridWidth" min="1" value="118" style="width: 64px" />
  
196
              </label>
  
197
              <label>
  
198
                H
  
199
                <input type="number" id="gridHeight" min="1" value="59" style="width: 64px" />
  
200
              </label>
  
201
              <button class="button secondary" id="resizeGrid">Resize</button>
  
202
            </div>
  
203
          </div>
  
204
          <div class="control-group">
  
205
            <div class="row">
  
206
              <button class="button" id="saveMap">Download .txt</button>
  
207
              <button class="button secondary" id="copyMap">Copy</button>
  
208
            </div>
  
209
            <div class="row">
  
210
              <input type="file" id="fileInput" accept=".txt" />
  
211
            </div>
  
212
            <div class="footer-note" id="status">Loaded maps/map1.txt</div>
  
213
          </div>
  
214
          <div class="control-group">
  
215
            <div class="footer-note">
  
216
              Paint: click or drag. Hold Shift for straight lines. Shortcut: 1-4 to pick tile.
  
217
            </div>
  
218
          </div>
  
219
        </div>
  
220
      </aside>
  
221
      <main class="grid-wrap" id="gridWrap">
  
222
        <canvas id="gridCanvas" aria-label="Map grid" role="img"></canvas>
  
223
      </main>
  
224
    </div>
  
225
  
  
226
    <script>
  
227
      const storageKey = "mapeditor-state";
  
228
      const defaultCols = 118;
  
229
      const defaultRows = 59;
  
230
      const tileOrder = [
  
231
        "┌",
  
232
        "┐",
  
233
        "└",
  
234
        "┘",
  
235
        "─",
  
236
        "│",
  
237
        "╔",
  
238
        "╗",
  
239
        "╚",
  
240
        "╝",
  
241
        "═",
  
242
        "║",
  
243
        "#",
  
244
        ".",
  
245
        "~",
  
246
        " ",
  
247
        "N",
  
248
        "B",
  
249
        "S",
  
250
        "G",
  
251
        "$",
  
252
      ];
  
253
      const tileLabels = {
  
254
        "#": "Wall",
  
255
        ".": "Floor",
  
256
        "~": "Water",
  
257
        "N": "Cyan",
  
258
        "B": "Red",
  
259
        "S": "Red",
  
260
        "G": "Red",
  
261
        "$": "Gold",
  
262
        " ": "Void",
  
263
        "┌": "Border corner",
  
264
        "┐": "Border corner",
  
265
        "└": "Border corner",
  
266
        "┘": "Border corner",
  
267
        "─": "Border",
  
268
        "│": "Border",
  
269
        "╔": "Double border corner",
  
270
        "╗": "Double border corner",
  
271
        "╚": "Double border corner",
  
272
        "╝": "Double border corner",
  
273
        "═": "Double border",
  
274
        "║": "Double border",
  
275
      };
  
276
  
  
277
      const canvas = document.getElementById("gridCanvas");
  
278
      const ctx = canvas.getContext("2d");
  
279
      const gridWrap = document.getElementById("gridWrap");
  
280
      const paletteEl = document.getElementById("palette");
  
281
      const activeTileEl = document.getElementById("activeTile");
  
282
      const brushSizeEl = document.getElementById("brushSize");
  
283
      const brushLabelEl = document.getElementById("brushLabel");
  
284
      const gridSizeEl = document.getElementById("gridSize");
  
285
      const toggleGridBtn = document.getElementById("toggleGrid");
  
286
      const clearBtn = document.getElementById("clearMap");
  
287
      const addBorderBtn = document.getElementById("addBorder");
  
288
      const addDoubleBorderBtn = document.getElementById("addDoubleBorder");
  
289
      const gridWidthEl = document.getElementById("gridWidth");
  
290
      const gridHeightEl = document.getElementById("gridHeight");
  
291
      const resizeGridBtn = document.getElementById("resizeGrid");
  
292
      const saveBtn = document.getElementById("saveMap");
  
293
      const copyBtn = document.getElementById("copyMap");
  
294
      const fileInput = document.getElementById("fileInput");
  
295
      const statusEl = document.getElementById("status");
  
296
  
  
297
      let mapData = [];
  
298
      let activeTile = "#";
  
299
      let isPainting = false;
  
300
      let lastCell = null;
  
301
      let showGridLines = true;
  
302
  
  
303
      const baseFontSize = 14;
  
304
      const minZoom = 0.5;
  
305
      const maxZoom = 4;
  
306
      let zoom = 1;
  
307
      let cellMetrics = { width: baseFontSize, height: baseFontSize, fontSize: baseFontSize };
  
308
      let devicePixelRatioValue = window.devicePixelRatio || 1;
  
309
      let autoSaveId = null;
  
310
      const tileStyle = {
  
311
        wall: { fill: "#8a6b3e", text: "#251a0b" },
  
312
        floor: { fill: "#f7f7f7", text: "#b9b9b9" },
  
313
        water: { fill: "#4a8fa3", text: "#e9f7f9" },
  
314
        cyan: { fill: "#27c8d1", text: "#07353a" },
  
315
        red: { fill: "#d84545", text: "#3d0a0a" },
  
316
        gold: { fill: "#d7a627", text: "#3a2605" },
  
317
        void: { fill: "#222", text: "#777" },
  
318
      };
  
319
  
  
320
      const tileClass = (tile) => {
  
321
        if ("#┌┐└┘─│╔╗╚╝═║".includes(tile)) return "wall";
  
322
        if (tile === ".") return "floor";
  
323
        if (tile === "~") return "water";
  
324
        if (tile === "N") return "cyan";
  
325
        if ("BSG".includes(tile)) return "red";
  
326
        if (tile === "$") return "gold";
  
327
        return "void";
  
328
      };
  
329
  
  
330
      const setStatus = (text) => {
  
331
        statusEl.textContent = text;
  
332
      };
  
333
  
  
334
      const normalizeMap = (lines) => {
  
335
        const cleanLines = lines.filter((line) => line.length > 0);
  
336
        const width = Math.max(...cleanLines.map((line) => line.length));
  
337
        return cleanLines.map((line) => line.padEnd(width, " ").split(""));
  
338
      };
  
339
  
  
340
      const mapToText = () => mapData.map((row) => row.join("")).join("\n");
  
341
  
  
342
      const buildCanvas = () => {
  
343
        if (!mapData.length) return;
  
344
        const rows = mapData.length;
  
345
        const cols = mapData[0].length;
  
346
        resizeCanvas(cols, rows);
  
347
        gridSizeEl.textContent = `${cols} x ${rows}`;
  
348
        gridWidthEl.value = cols;
  
349
        gridHeightEl.value = rows;
  
350
        renderAll();
  
351
      };
  
352
  
  
353
      const resizeCanvas = (cols, rows) => {
  
354
        const fontSize = Math.max(8, Math.round(baseFontSize * zoom));
  
355
        devicePixelRatioValue = window.devicePixelRatio || 1;
  
356
        ctx.setTransform(devicePixelRatioValue, 0, 0, devicePixelRatioValue, 0, 0);
  
357
        ctx.font = `${fontSize}px ${getComputedStyle(document.documentElement).getPropertyValue("--mono")}`;
  
358
        const metrics = ctx.measureText("M");
  
359
        const glyphWidth = Math.max(1, Math.ceil(metrics.width));
  
360
        const glyphHeight = Math.max(
  
361
          1,
  
362
          Math.ceil((metrics.actualBoundingBoxAscent || fontSize) + (metrics.actualBoundingBoxDescent || 0))
  
363
        );
  
364
        cellMetrics = { width: glyphWidth, height: glyphHeight, fontSize };
  
365
        canvas.style.width = `${cols * cellMetrics.width}px`;
  
366
        canvas.style.height = `${rows * cellMetrics.height}px`;
  
367
        canvas.width = Math.round(cols * cellMetrics.width * devicePixelRatioValue);
  
368
        canvas.height = Math.round(rows * cellMetrics.height * devicePixelRatioValue);
  
369
        ctx.setTransform(devicePixelRatioValue, 0, 0, devicePixelRatioValue, 0, 0);
  
370
        ctx.font = `${fontSize}px ${getComputedStyle(document.documentElement).getPropertyValue("--mono")}`;
  
371
        ctx.textAlign = "left";
  
372
        ctx.textBaseline = "top";
  
373
      };
  
374
  
  
375
      const drawCell = (x, y, tile) => {
  
376
        const type = tileClass(tile);
  
377
        const style = tileStyle[type];
  
378
        const px = x * cellMetrics.width;
  
379
        const py = y * cellMetrics.height;
  
380
        ctx.fillStyle = style.fill;
  
381
        ctx.fillRect(px, py, cellMetrics.width, cellMetrics.height);
  
382
        if (showGridLines) {
  
383
          ctx.strokeStyle = getComputedStyle(document.documentElement)
  
384
            .getPropertyValue("--gridline")
  
385
            .trim();
  
386
          ctx.strokeRect(
  
387
            px + 0.5,
  
388
            py + 0.5,
  
389
            Math.max(1, cellMetrics.width - 1),
  
390
            Math.max(1, cellMetrics.height - 1)
  
391
          );
  
392
        }
  
393
        if (tile !== " ") {
  
394
          ctx.fillStyle = style.text;
  
395
          ctx.fillText(tile, px, py);
  
396
        }
  
397
      };
  
398
  
  
399
      const renderAll = () => {
  
400
        const rows = mapData.length;
  
401
        const cols = mapData[0].length;
  
402
        for (let y = 0; y < rows; y += 1) {
  
403
          for (let x = 0; x < cols; x += 1) {
  
404
            drawCell(x, y, mapData[y][x]);
  
405
          }
  
406
        }
  
407
      };
  
408
  
  
409
      const updateCell = (x, y, tile) => {
  
410
        if (!mapData[y] || mapData[y][x] === undefined) return;
  
411
        mapData[y][x] = tile;
  
412
        drawCell(x, y, tile);
  
413
      };
  
414
  
  
415
      const paintAt = (x, y) => {
  
416
        const size = Number(brushSizeEl.value);
  
417
        const half = Math.floor(size / 2);
  
418
        for (let dy = -half; dy <= half; dy += 1) {
  
419
          for (let dx = -half; dx <= half; dx += 1) {
  
420
            updateCell(x + dx, y + dy, activeTile);
  
421
          }
  
422
        }
  
423
      };
  
424
  
  
425
      const drawLine = (from, to) => {
  
426
        const dx = Math.abs(to.x - from.x);
  
427
        const dy = Math.abs(to.y - from.y);
  
428
        const sx = from.x < to.x ? 1 : -1;
  
429
        const sy = from.y < to.y ? 1 : -1;
  
430
        let err = dx - dy;
  
431
        let x = from.x;
  
432
        let y = from.y;
  
433
        while (true) {
  
434
          paintAt(x, y);
  
435
          if (x === to.x && y === to.y) break;
  
436
          const e2 = err * 2;
  
437
          if (e2 > -dy) {
  
438
            err -= dy;
  
439
            x += sx;
  
440
          }
  
441
          if (e2 < dx) {
  
442
            err += dx;
  
443
            y += sy;
  
444
          }
  
445
        }
  
446
      };
  
447
  
  
448
      const setActiveTile = (tile) => {
  
449
        activeTile = tile;
  
450
        activeTileEl.textContent = tile === " " ? "(space)" : tile;
  
451
        paletteEl.querySelectorAll(".tile-btn").forEach((btn) => {
  
452
          btn.classList.toggle("active", btn.dataset.tile === tile);
  
453
        });
  
454
      };
  
455
  
  
456
      const setupPalette = () => {
  
457
        paletteEl.innerHTML = "";
  
458
        tileOrder.forEach((tile) => {
  
459
          const btn = document.createElement("button");
  
460
          btn.type = "button";
  
461
          btn.className = "tile-btn";
  
462
          btn.dataset.tile = tile;
  
463
          btn.textContent = tile === " " ? "space" : tile;
  
464
          btn.title = tileLabels[tile] || tile;
  
465
          btn.addEventListener("click", () => setActiveTile(tile));
  
466
          paletteEl.appendChild(btn);
  
467
        });
  
468
        setActiveTile(activeTile);
  
469
      };
  
470
  
  
471
      const loadFromText = (text, sourceLabel) => {
  
472
        const lines = text.replace(/\r/g, "").split("\n");
  
473
        mapData = normalizeMap(lines);
  
474
        buildCanvas();
  
475
        setStatus(`Loaded ${sourceLabel}`);
  
476
      };
  
477
  
  
478
      const loadDefaultMap = () => {
  
479
        const row = ".".repeat(defaultCols);
  
480
        const empty = Array.from({ length: defaultRows }, () => row).join("\n");
  
481
        loadFromText(empty, "empty map");
  
482
      };
  
483
  
  
484
      const saveState = () => {
  
485
        if (!mapData.length) return;
  
486
        const state = {
  
487
          map: mapToText(),
  
488
          cols: mapData[0].length,
  
489
          rows: mapData.length,
  
490
          zoom,
  
491
          brush: Number(brushSizeEl.value),
  
492
          tile: activeTile,
  
493
          grid: showGridLines,
  
494
        };
  
495
        try {
  
496
          localStorage.setItem(storageKey, JSON.stringify(state));
  
497
          setStatus("Autosaved");
  
498
        } catch (error) {
  
499
          setStatus("Autosave failed");
  
500
        }
  
501
      };
  
502
  
  
503
      const loadState = () => {
  
504
        try {
  
505
          const raw = localStorage.getItem(storageKey);
  
506
          if (!raw) return false;
  
507
          const state = JSON.parse(raw);
  
508
          if (!state?.map) return false;
  
509
          loadFromText(state.map, "local autosave");
  
510
          if (state.zoom) applyZoom(state.zoom);
  
511
          if (state.brush) {
  
512
            brushSizeEl.value = state.brush;
  
513
            brushLabelEl.textContent = state.brush;
  
514
          }
  
515
          if (state.tile) setActiveTile(state.tile);
  
516
          if (typeof state.grid === "boolean") showGridLines = state.grid;
  
517
          renderAll();
  
518
          return true;
  
519
        } catch (error) {
  
520
          return false;
  
521
        }
  
522
      };
  
523
  
  
524
      brushSizeEl.addEventListener("input", () => {
  
525
        brushLabelEl.textContent = brushSizeEl.value;
  
526
      });
  
527
  
  
528
      toggleGridBtn.addEventListener("click", () => {
  
529
        showGridLines = !showGridLines;
  
530
        renderAll();
  
531
      });
  
532
  
  
533
      clearBtn.addEventListener("click", () => {
  
534
        mapData = mapData.map((row) => row.map(() => "."));
  
535
        buildCanvas();
  
536
        setStatus("Cleared to floor tiles");
  
537
      });
  
538
  
  
539
      resizeGridBtn.addEventListener("click", () => {
  
540
        const newCols = Math.max(1, Number(gridWidthEl.value) || 1);
  
541
        const newRows = Math.max(1, Number(gridHeightEl.value) || 1);
  
542
        const oldRows = mapData.length;
  
543
        const oldCols = mapData[0]?.length || 0;
  
544
        const resized = [];
  
545
        for (let y = 0; y < newRows; y += 1) {
  
546
          const row = [];
  
547
          for (let x = 0; x < newCols; x += 1) {
  
548
            const tile = mapData[y]?.[x] ?? ".";
  
549
            row.push(tile);
  
550
          }
  
551
          resized.push(row);
  
552
        }
  
553
        mapData = resized;
  
554
        buildCanvas();
  
555
        setStatus(`Resized from ${oldCols}x${oldRows} to ${newCols}x${newRows}`);
  
556
      });
  
557
  
  
558
      addBorderBtn.addEventListener("click", () => {
  
559
        if (!mapData.length) return;
  
560
        const rows = mapData.length;
  
561
        const cols = mapData[0].length;
  
562
        if (cols < 2 || rows < 2) return;
  
563
        updateCell(0, 0, "┌");
  
564
        updateCell(cols - 1, 0, "┐");
  
565
        updateCell(0, rows - 1, "└");
  
566
        updateCell(cols - 1, rows - 1, "┘");
  
567
        for (let x = 1; x < cols - 1; x += 1) {
  
568
          updateCell(x, 0, "─");
  
569
          updateCell(x, rows - 1, "─");
  
570
        }
  
571
        for (let y = 1; y < rows - 1; y += 1) {
  
572
          updateCell(0, y, "│");
  
573
          updateCell(cols - 1, y, "│");
  
574
        }
  
575
        setStatus("Added box border tiles");
  
576
      });
  
577
  
  
578
      addDoubleBorderBtn.addEventListener("click", () => {
  
579
        if (!mapData.length) return;
  
580
        const rows = mapData.length;
  
581
        const cols = mapData[0].length;
  
582
        if (cols < 2 || rows < 2) return;
  
583
        updateCell(0, 0, "╔");
  
584
        updateCell(cols - 1, 0, "╗");
  
585
        updateCell(0, rows - 1, "╚");
  
586
        updateCell(cols - 1, rows - 1, "╝");
  
587
        for (let x = 1; x < cols - 1; x += 1) {
  
588
          updateCell(x, 0, "═");
  
589
          updateCell(x, rows - 1, "═");
  
590
        }
  
591
        for (let y = 1; y < rows - 1; y += 1) {
  
592
          updateCell(0, y, "║");
  
593
          updateCell(cols - 1, y, "║");
  
594
        }
  
595
        setStatus("Added double border tiles");
  
596
      });
  
597
  
  
598
      saveBtn.addEventListener("click", () => {
  
599
        const blob = new Blob([mapToText()], { type: "text/plain" });
  
600
        const url = URL.createObjectURL(blob);
  
601
        const link = document.createElement("a");
  
602
        link.href = url;
  
603
        link.download = "map.txt";
  
604
        document.body.appendChild(link);
  
605
        link.click();
  
606
        link.remove();
  
607
        URL.revokeObjectURL(url);
  
608
        setStatus("Downloaded map.txt");
  
609
      });
  
610
  
  
611
      copyBtn.addEventListener("click", async () => {
  
612
        try {
  
613
          await navigator.clipboard.writeText(mapToText());
  
614
          setStatus("Copied map to clipboard");
  
615
        } catch (error) {
  
616
          setStatus("Clipboard unavailable");
  
617
        }
  
618
      });
  
619
  
  
620
      fileInput.addEventListener("change", async (event) => {
  
621
        const file = event.target.files[0];
  
622
        if (!file) return;
  
623
        const text = await file.text();
  
624
        loadFromText(text, file.name);
  
625
      });
  
626
  
  
627
      canvas.addEventListener("contextmenu", (event) => {
  
628
        event.preventDefault();
  
629
      });
  
630
  
  
631
      const getCellFromEvent = (event) => {
  
632
        const rect = canvas.getBoundingClientRect();
  
633
        const x = Math.floor((event.clientX - rect.left) / cellMetrics.width);
  
634
        const y = Math.floor((event.clientY - rect.top) / cellMetrics.height);
  
635
        if (x < 0 || y < 0) return null;
  
636
        if (!mapData.length) return null;
  
637
        if (y >= mapData.length || x >= mapData[0].length) return null;
  
638
        return { x, y };
  
639
      };
  
640
  
  
641
      const applyZoom = (newZoom, anchor) => {
  
642
        const rows = mapData.length;
  
643
        const cols = mapData[0]?.length || 0;
  
644
        if (!rows || !cols) return;
  
645
        const clamped = Math.min(maxZoom, Math.max(minZoom, newZoom));
  
646
        if (clamped === zoom) return;
  
647
        const sizeBeforeX = cellMetrics.width;
  
648
        const sizeBeforeY = cellMetrics.height;
  
649
        const offsetX = anchor?.x ?? gridWrap.clientWidth / 2;
  
650
        const offsetY = anchor?.y ?? gridWrap.clientHeight / 2;
  
651
        const worldX = (gridWrap.scrollLeft + offsetX) / sizeBeforeX;
  
652
        const worldY = (gridWrap.scrollTop + offsetY) / sizeBeforeY;
  
653
        zoom = clamped;
  
654
        resizeCanvas(cols, rows);
  
655
        renderAll();
  
656
        gridWrap.scrollLeft = worldX * cellMetrics.width - offsetX;
  
657
        gridWrap.scrollTop = worldY * cellMetrics.height - offsetY;
  
658
      };
  
659
  
  
660
      const handleCanvasEvent = (event, overrideTile = null) => {
  
661
        const cell = getCellFromEvent(event);
  
662
        if (!cell) return;
  
663
        const previousTile = activeTile;
  
664
        if (overrideTile !== null) activeTile = overrideTile;
  
665
        if (event.shiftKey && lastCell) {
  
666
          drawLine(lastCell, cell);
  
667
        } else {
  
668
          paintAt(cell.x, cell.y);
  
669
        }
  
670
        if (overrideTile !== null) activeTile = previousTile;
  
671
        lastCell = cell;
  
672
      };
  
673
  
  
674
      canvas.addEventListener("mousedown", (event) => {
  
675
        if (event.button === 2) {
  
676
          isPainting = true;
  
677
          handleCanvasEvent(event, ".");
  
678
          return;
  
679
        }
  
680
        isPainting = true;
  
681
        handleCanvasEvent(event);
  
682
      });
  
683
  
  
684
      canvas.addEventListener("mousemove", (event) => {
  
685
        if (!isPainting) return;
  
686
        if (event.buttons === 2) {
  
687
          handleCanvasEvent(event, ".");
  
688
          return;
  
689
        }
  
690
        handleCanvasEvent(event);
  
691
      });
  
692
  
  
693
      canvas.addEventListener("wheel", (event) => {
  
694
        event.preventDefault();
  
695
        const delta = event.deltaY > 0 ? -0.1 : 0.1;
  
696
        const rect = canvas.getBoundingClientRect();
  
697
        const anchor = {
  
698
          x: event.clientX - rect.left,
  
699
          y: event.clientY - rect.top,
  
700
        };
  
701
        applyZoom(zoom + delta, anchor);
  
702
      });
  
703
  
  
704
      document.addEventListener("mouseup", () => {
  
705
        isPainting = false;
  
706
        lastCell = null;
  
707
      });
  
708
  
  
709
      document.addEventListener("keydown", (event) => {
  
710
        if (event.target.matches("input, textarea")) return;
  
711
        if (event.key >= "1" && event.key <= "4") {
  
712
          const tile = tileOrder[Number(event.key) - 1];
  
713
          setActiveTile(tile);
  
714
        }
  
715
      });
  
716
  
  
717
      setupPalette();
  
718
      if (!loadState()) loadDefaultMap();
  
719
      autoSaveId = setInterval(saveState, 5000);
  
720
    </script>
  
721
  </body>
  
722
</html>
diff --git a/maps/map1.h b/maps/map1.h
  
1
unsigned char maps_map1_txt[] = {
  
2
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
3
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
4
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
5
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
6
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
7
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
8
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
9
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
10
  0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
11
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x7e,
  
12
  0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
13
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
14
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
15
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
16
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
17
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
18
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e,
  
19
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
20
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x7e, 0x7e, 0x7e, 0x7e, 0x2e, 0x2e, 0x2e,
  
21
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
22
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
23
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
24
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
25
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
26
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
27
  0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
28
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
29
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
30
  0x2e, 0x2e, 0x53, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x7e,
  
31
  0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x2e, 0x2e, 0x2e, 0x2e,
  
32
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
33
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
34
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
35
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e,
  
36
  0x2e, 0x2e, 0x2e, 0x2e, 0x47, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
37
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
38
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
39
  0x2e, 0x2e, 0x2e, 0x2e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e,
  
40
  0x7e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
41
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
42
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
43
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
44
  0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
45
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
46
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
47
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x7e, 0x7e, 0x7e,
  
48
  0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
49
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
50
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
51
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
52
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
53
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
54
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
55
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
56
  0x2e, 0x2e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x2e, 0x2e, 0x2e, 0x2e,
  
57
  0x2e, 0x2e, 0x2e, 0x47, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
58
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
59
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
60
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e,
  
61
  0x2e, 0x2e, 0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0x2e, 0x2e,
  
62
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
63
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
64
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
65
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
66
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
67
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
68
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
69
  0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
70
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
71
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
72
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
73
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
74
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
75
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
76
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
77
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e,
  
78
  0x2e, 0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0xe2, 0x95, 0x94, 0xe2, 0x95, 0x90,
  
79
  0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90,
  
80
  0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90,
  
81
  0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90,
  
82
  0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x97, 0x2e, 0x2e, 0x2e,
  
83
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
84
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
85
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
86
  0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
87
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
88
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a,
  
89
  0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
  
90
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
91
  0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
92
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
93
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
94
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
95
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
96
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
97
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e,
  
98
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e,
  
99
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
100
  0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
101
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
102
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
103
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
104
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
105
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
106
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
107
  0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x4e, 0x2e, 0x2e,
  
108
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
  
109
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
110
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
111
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
112
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
113
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
114
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
115
  0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
116
  0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
117
  0x2e, 0x2e, 0x4e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x24, 0x2e,
  
118
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x24,
  
119
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
120
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
121
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
122
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
123
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a,
  
124
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
  
125
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
126
  0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
127
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
128
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
129
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
130
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
131
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
132
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e,
  
133
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e,
  
134
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
135
  0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
136
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
137
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x47, 0x2e,
  
138
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
139
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
140
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
141
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
142
  0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
143
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
  
144
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
145
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
146
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
147
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
148
  0x2e, 0x2e, 0x2e, 0x47, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
149
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
150
  0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
151
  0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
152
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e,
  
153
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
154
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
155
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
156
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
157
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
158
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a,
  
159
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91,
  
160
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
161
  0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x91, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
162
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
163
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
164
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
165
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
166
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
167
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e,
  
168
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x9a, 0xe2, 0x95, 0x90,
  
169
  0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90,
  
170
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2,
  
171
  0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x90, 0xe2, 0x95, 0x9d, 0x2e,
  
172
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
173
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
174
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
175
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
176
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
177
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
178
  0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
179
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
180
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
181
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
182
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
183
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
184
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
185
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
186
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
187
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
188
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
189
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
190
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
191
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
192
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
193
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
194
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a,
  
195
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
196
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
197
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
198
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
199
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
200
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
201
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
202
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
203
  0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
204
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
205
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
206
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
207
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
208
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
209
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
210
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
211
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e,
  
212
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
213
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
214
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
215
  0x2e, 0x2e, 0x24, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
216
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
217
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
218
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
219
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
220
  0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
221
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
222
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
223
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
224
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
225
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
226
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
227
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
228
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e,
  
229
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
230
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
231
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
232
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
233
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x24, 0x2e,
  
234
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
235
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
236
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
237
  0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
238
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
239
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
240
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
241
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
242
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
243
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
244
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
245
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
246
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x42, 0x2e, 0x2e, 0x2e, 0x2e,
  
247
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
248
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
249
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
250
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
251
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
252
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
253
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e,
  
254
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
255
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
256
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
257
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
258
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
259
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
260
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
261
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
262
  0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
263
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
264
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
265
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
266
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
267
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
268
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
269
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
270
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e,
  
271
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
272
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
273
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
274
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
275
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
276
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
277
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
278
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
279
  0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
280
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
281
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
282
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
283
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
284
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
285
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
286
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
287
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
288
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
289
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
290
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
291
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
292
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
293
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x47, 0x2e, 0x2e,
  
294
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
295
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a,
  
296
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
297
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
298
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
299
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
300
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
301
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
302
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
303
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
304
  0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
305
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
306
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
307
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
308
  0x2e, 0x47, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
309
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
310
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
311
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
312
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e,
  
313
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
314
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
315
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
316
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
317
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
318
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
319
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
320
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
321
  0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
322
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
323
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
324
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
325
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
326
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
327
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
328
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
329
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x0a, 0x2e, 0x2e, 0x2e, 0x2e,
  
330
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
331
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
332
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
333
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
334
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
335
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
336
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
337
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
338
  0x0a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
339
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
340
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
341
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
342
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
343
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
344
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
345
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e,
  
346
  0x2e, 0x2e, 0x2e, 0x2e, 0x2e
  
347
};
  
348
unsigned int maps_map1_txt_len = 4133;
diff --git a/maps/map1.txt b/maps/map1.txt
  
1
....................................................................................................
  
2
..................~~~~~~............................................................................
  
3
...................~~~~.............................................................................
  
4
...................................S........~~~~~~~~~...............................................
  
5
........G...................................~~~~~~~~~...............................................
  
6
............................................~~~~~~~~................................................
  
7
............................................~~~~~~.......G..........................................
  
8
.....$..$...........................................................................................
  
9
....................................................................................................
  
10
......$..╔═══════════════╗.........................................$................................
  
11
..$......║...............║..........................................................................
  
12
.........║...............║..........................................................................
  
13
.........║...N...........║..........................................................................
  
14
.........║...........N...║.$............$...........................................................
  
15
.........║...............║..........................................................................
  
16
.........║...............║...............................G..........................................
  
17
.........║...............║...................................................G......................
  
18
.........║...............║..........................................................................
  
19
.........║...............║..........................................................................
  
20
.........╚═════.....═════╝..........................................................................
  
21
....................................................................................................
  
22
....................................................................................................
  
23
....................................................................................................
  
24
....................................................................................................
  
25
........................................$...........................................................
  
26
....................................................................................................
  
27
..............................................................$.....................................
  
28
....................................................................................................
  
29
.............B......................................................................................
  
30
....................................................................................................
  
31
....................................................................................................
  
32
....................................................................................................
  
33
....................................................................................................
  
34
..........................................................................G.........................
  
35
....................................................................................................
  
36
............................................G.......................................................
  
37
....................................................................................................
  
38
....................................................................................................
  
39
....................................................................................................
  
40
....................................................................................................