🏠 🔑
Command - neverdark-diff
diff --git a/.config b/.config
deleted file mode 100644
index 3a275f0..0000000
--- a/.config
+++ /dev/null
@@ -1,2 +0,0 @@
-CONFIG_ROOT=y
-CONFIG_CHROOT=y
diff --git a/.gitignore b/.gitignore
index 49ec0e8..f3da86b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,3 @@
 /package-lock.json
 /pre.js
 /node_modules
-/build
-/js/vim.css
-/game/std.db
diff --git a/.vimrc.local b/.vimrc.local
index d5c18c2..2d541a5 100644
--- a/.vimrc.local
+++ b/.vimrc.local
@@ -1,63 +1,7 @@
-" FROM dotenv.vim
-
-let s:env_cache = {}
-let s:interpolation = '\\\=\${.\{-\}}\|\\\=\$\w\+'
-
-function! s:read_env(file, ...) abort
-  let file = fnamemodify(a:file, ':p')
-  let ftime = getftime(file)
-  if ftime < 0
-    return {}
-  endif
-  let [cachetime, lines] = get(s:env_cache, file, [-2, []])
-  if ftime != cachetime
-    let lines = []
-    for line in readfile(file)
-      let matches = matchlist(line, '\v\C^%(export\s+)=([[:alnum:]_.]+)%(\s*\=\s*|:\s{-})(''%(\\''|[^''])*''|"%(\\"|[^"])*"|[^#]+)=%( *#.*)?$')
-      if !empty(matches)
-        call add(lines, matches[1:2])
-      endif
-    endfor
-    let s:env_cache[file] = [ftime, lines]
-  endif
-  let env = a:0 ? a:1 : {}
-  for [key, value] in lines
-    if !has_key(env, key)
-      if value =~# '^\s*".*"\s*$'
-        let value = substitute(value, '\n', "\n", 'g')
-        let value = substitute(value, '\\\ze[^$]', '', 'g')
-      endif
-      let value = substitute(value, '^\s*\([''"]\)\=\(.\{-\}\)\1\s*$', '\2', '')
-      let value = substitute(value, s:interpolation, '\=s:lookup(submatch(0), env)', 'g')
-      let env[key] = value
-    endif
-  endfor
-  return env
-endfunction
-
-" FROM here on it is mine
-
-let s:config = s:read_env(".config")
-
-packadd qgdb
-
-let g:os = substitute(system('uname'), '\n', '', '')
-
 let s:muckb = 0
 let s:muckw = 0
 let s:muckO = 0
 
-let g:gdb_prog = 'gdb' " FIXME default linux?
-
-if s:config.CONFIG_ROOT == "y"
-	let g:gdb_sudo = 1
-	if os == "OpenBSD"
-		let g:sudo_prog = "doas"
-	endif
-else
-	let g:gdb_sudo = 0
-endif
-
 func s:Continue()
 	if s:muckb == 0 && s:muckO == 1
 		call s:Connect()
@@ -90,6 +34,8 @@ let g:Run = function("s:nothing")
 let g:GdbQuit = function("Quit")
 let g:GdbOut = function('s:GdbOut')
 
+packadd qgdb
+
 func MuckBuffer()
 	if s:muckw != 0
 		call win_gotoid(s:muckw)
@@ -107,18 +53,11 @@ endfunc
 func s:Connect()
 	let id = win_getid()
 	call MuckBuffer()
-	if g:os == "OpenBSD"
-		let muckt = term_start('openssl s_client -connect localhost:4201 -crlf', {
-					\ "term_kill": "int",
-					\ "curwin": 1,
-					\ })
-	else
-		let muckt = term_start('telnet localhost 4201', {
-					\ "term_kill": "int",
-					\ "curwin": 1,
-					\ })
-	endif
-	call term_sendkeys(muckt, "auth jqdyeivwu4q3d5fwxhgitusyq9ycpkeoanvqpo-frlc\<cr>")
+	let muckt = term_start('telnet localhost 4201', {
+				\ "term_kill": "int",
+				\ "curwin": 1,
+				\ })
+	call term_sendkeys(muckt, "auth One=qovmjbl\<cr>")
 	let s:muckw = win_getid()
 	let s:muckb = bufnr('%')
 	call win_gotoid(id)
@@ -136,12 +75,6 @@ func s:SigTerm()
 	endif
 endfunc
 
-func s:SigInt()
-	Stop
-	Gdb signal SIGINT
-	Continue
-endfunc
-
 func ConnectIgnore()
 	if s:muckb == 0 || !bufexists(s:muckb)
 		call s:Connect()
@@ -156,7 +89,6 @@ endfunc
 func Kill()
 	if s:SigTerm()
 		GdbQuit
-		Run
 	endif
 endfunc
 
@@ -169,43 +101,32 @@ func Restart()
 	Run
 endfunc
 
-func Reset()
-	!./reset.sh
-	Gdb run
-	Run
-endfunc
-
-func ConfigRun()
-	Gdb handle SIGPIPE noprint nostop
-	Gdb set confirm off
-	Run
-endfunc
+let g:os = substitute(system('uname'), '\n', '', '')
 
 if !has('macunix')
 	autocmd! QuickfixCmdPost make call Restart()
 
 	File ./src/fbmuck
-	" Args \-C ./game
+	Args \-C ./game
 	GdbStart
-	if g:gdb_sudo == 1
-		DebugFocus
-	else
-		call ConfigRun()
-	endif
+	Gdb handle SIGPIPE noprint nostop
+	Run
+endif
+
+if g:os != "OpenBSD"
+	set makeprg=bmake
 endif
 
-nmap 'd :StartRun<cr>
-nmap 'c :call ConnectIgnore()<cr>
-nmap 'b :Break<cr>
-nmap 'C :Clear<cr>
-nmap 'k :call Kill()<cr>
-nmap 'kk :call FullQuit()<cr>
-nmap 'q :call Quit()<cr>
-tno 'k <c-w>:call Kill()<cr>
-tno 'kk <c-w>:call FullQuit()<cr>
-tno 'q <c-w>:call Quit()<cr>
-tno 'r <c-w>:call ConfigRun()<cr>
-map 'reset :call Reset()<cr>
+nmap <leader>d :StartRun<cr>
+nmap <leader>c :call ConnectIgnore()<cr>
+nmap <leader>b :Break<cr>
+nmap <leader>C :Clear<cr>
+nmap <leader>k :call Kill()<cr>
+nmap <leader>kk :call FullQuit()<cr>
+nmap <leader>q :call Quit()<cr>
+tno <esc>k <c-w>:call Kill()<cr>
+tno <esc>kk <c-w>:call FullQuit()<cr>
+tno <esc>q <c-w>:call Quit()<cr>
 
 " jsc
 set efm +=%E%f:%l:\ ERROR\ \-\ %m,%-Z%p^
@@ -214,4 +135,7 @@ set efm +=%W%f:%l:\ WARNING\ \-\ %m,%-Z%p^
 set path=$PWD/src,$PWD/include
 set grepprg=ag\ --nogroup\ --nocolor
 nnoremap gr :grep <cword> src/*.c include/*.h<cr>
+" nnoremap gr :exe "grep /\\<" . expand("<cword>") . "\\>/gj src/*.c include/*.h"<cr>
+" call job_start('./src/ws-server')
+" term doas make web
 autocmd BufNewFile,BufRead *.hjs setf javascript.preproc
diff --git a/Makefile b/Makefile
index d6063c2..09e4b43 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,3 @@
-include .config
 #!/bin/make -f
 
 srcdir := ${PWD}
@@ -8,22 +7,24 @@ include scripts/Makefile.common
 
 GCC_JS := ${CC} -E -P -nostdinc -undef -x c
 
-js-src != find js -type f
-js-$(production) := ./build/main.js
-
-all: index.html main.js vim.css ${js-y}
+all: index.html main.js vim.css
 
+js-src != find js -type f
 art-y != find art -type f
 art-y += nd256.png
 
-./build/main.js: ${js-src}
-	npm run build
+pre.js: ${js-src}
+	${GCC_JS} -o $@ ./js/main.js
 
 ./node_modules/:
 	npm install
 
-index.html: pre-index.html
-	${scripts}/html_tool.sh pre-index.html > $@
+main.js: ./node_modules/ pre.js
+	./node_modules/.bin/babel pre.js > $@
+
+inline-js := main.js
+index.html: pre-index.html ${inline-js}
+	${scripts}/html_tool.sh pre-index.html ${inline-js} > $@
 
 vim.css: vss/
 
@@ -33,6 +34,9 @@ $(subdirs-cleaner):
 cleaner: ${subdirs-cleaner}
 	rm config.status config.cache config.log
 
+web: src
+	${srcdir}/src/ws-server
+
 game/data/: src/ vss/
 
 backup-date != date +%s
@@ -65,4 +69,4 @@ install: ${artdir} ${art-install}
 run: all
 	./src/fbmuck -C ./game
 
-.PHONY: cleaner ${subdirs-cleaner} backup run
+.PHONY: cleaner ${subdirs-cleaner} web backup run
diff --git a/art/muladhara.jpg b/art/muladhara.jpg
deleted file mode 100644
index 6340c4c..0000000
Binary files a/art/muladhara.jpg and /dev/null differ
diff --git a/art/muladhara_avatar.jpg b/art/muladhara_avatar.jpg
deleted file mode 100644
index 104f9c9..0000000
Binary files a/art/muladhara_avatar.jpg and /dev/null differ
diff --git a/game/minimal.db b/game/minimal.db
deleted file mode 100644
index de713a9..0000000
--- a/game/minimal.db
+++ /dev/null
@@ -1,49 +0,0 @@
-2
-#1
-One
-
-
-0 location
--1 contents
--1 next
-1600 value
-3 type
-0 flags
-0 home
-49 entity flags
-0 lvl
-0 cxp
-1 spend
-1 str
-1 con
-1 dex
-1 int
-1 wiz
-1 cha
-0 wtso
--1 sat
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-#0
-Room Zero
-
-
--1
-1
--1
-80
-0
-0
--1
-0
-0
-0
-255
-1
-***END OF DUMP***
diff --git a/game/std.db.ok b/game/std.db.ok
deleted file mode 100644
index de713a9..0000000
--- a/game/std.db.ok
+++ /dev/null
@@ -1,49 +0,0 @@
-2
-#1
-One
-
-
-0 location
--1 contents
--1 next
-1600 value
-3 type
-0 flags
-0 home
-49 entity flags
-0 lvl
-0 cxp
-1 spend
-1 str
-1 con
-1 dex
-1 int
-1 wiz
-1 cha
-0 wtso
--1 sat
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-0 spell
-#0
-Room Zero
-
-
--1
-1
--1
-80
-0
-0
--1
-0
-0
-0
-255
-1
-***END OF DUMP***
diff --git a/gpt.sh b/gpt.sh
deleted file mode 100755
index 78fbfe5..0000000
--- a/gpt.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/ksh
-
-curl https://api.openai.com/v1/completions \
-	-H "Content-Type: text/plain" \
-	-H "Authorization: Bearer $OPENAI_API_KEY" \
-	-d '{ "model": "text-davinci-002", "prompt": "Extensively describe one-eyed orc named Jesus who is currently sitting, is heavily wounded and is hungry.\n\n\n	Jesus is a one-eyed orc who is currently sitting down. He is heavily wounded and is very hungry. His one eye is swollen shut and he has multiple cuts and bruises all over his body. He is covered in dried blood and his clothes are tattered. He looks like he has been through a lot of tough battles. Despite all of his injuries, he still has a look of determination on his face. He is waiting for someone to come and help him.\n\n	I say: how can I help you, friend? I\'m not a healer, and I don\'t have any potions. Perhaps I can carry you to the nearest village.\n\n\n	He replies: I appreciate your offer, but I must stay here. I am waiting for someone.\n\n	I ask: who are you waiting for?\n\n	He answers: I am waiting for my son.", "temperature": 0.7, "max_tokens": 256, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 }'
diff --git a/include/config.h b/include/config.h
index 55b17ab..c3fe7c3 100644
--- a/include/config.h
+++ b/include/config.h
@@ -4,9 +4,7 @@
 #define STD_DB "std.db"
 #define GEO_DB "geo.db"
 
-#ifdef __OpenBSD__
 #define CONFIG_SECURE
-#endif
 #define TINYPORT 4201			/* Port that players connect to */
 
 #define HUMAN_BEING 1
diff --git a/include/entity.h b/include/entity.h
index a9f8100..6dff5a7 100644
--- a/include/entity.h
+++ b/include/entity.h
@@ -50,7 +50,6 @@ short kill_dmg(enum element dmg_type,
 
 void recycle(OBJ *player, OBJ *thing);
 void enter(OBJ *player, OBJ *loc, enum exit e);
-/* void enter(OBJ *player, OBJ *loc); */
 
 const char * unparse(OBJ *player, OBJ *loc);
 
@@ -74,6 +73,7 @@ void stats_init(ENT *enu, SENT *sk);
 void entity_update(OBJ *player);
 int entity_damage(OBJ *player, OBJ *target, short amt);
 int entity_boot(ENT *eplayer);
+void entity_gpt(OBJ *player, int echo_off, char *add_prompt);
 
 void entities_add(OBJ *where, enum biome, long long pdn);
 void enter_room(OBJ *player, enum exit e);
diff --git a/include/externs.h b/include/externs.h
index 4327bd1..5a3b9fd 100644
--- a/include/externs.h
+++ b/include/externs.h
@@ -76,4 +76,6 @@ extern void do_toad(command_t *);
 extern void do_boot(command_t *);
 extern void do_usage(command_t *);
 
+extern void do_gpt(command_t *);
+
 #endif /* _EXTERNS_H */
diff --git a/include/hashtable.h b/include/hashtable.h
index 0a847b8..4530999 100644
--- a/include/hashtable.h
+++ b/include/hashtable.h
@@ -1,7 +1,7 @@
 #ifndef HASHTABLE_H
 #define HASHTABLE_H
 
-#include "nddb.h"
+#include <db4/db.h>
 
 void hash_init(DB **db);
 void hash_put(DB *db, const char *key, void *data);
diff --git a/include/io.h b/include/io.h
index 02f228c..3824d45 100644
--- a/include/io.h
+++ b/include/io.h
@@ -5,12 +5,12 @@
 #include "object.h"
 #include "mcp.h"
 
-#define DESCR(fd) (&descr_map[fd])
-
 #ifdef CONFIG_SECURE
 #include <openssl/bio.h>
 #include <openssl/ssl.h>
 
+#define DESCR(fd) (&descr_map[fd])
+
 #define READ(cfd, to, len) \
 	SSL_read(descr_map[cfd].cSSL, to, len)
 #define WRITE(cfd, from, len) \
diff --git a/include/match.h b/include/match.h
index 8ce683d..eafb923 100644
--- a/include/match.h
+++ b/include/match.h
@@ -11,10 +11,57 @@
 OBJ *ematch_at(OBJ *player, OBJ *where, const char *name);
 OBJ *ematch_player(const char *name);
 OBJ *ematch_absolute(const char *name);
-OBJ *ematch_me(OBJ *player, const char *str);
-OBJ *ematch_here(OBJ *player, const char *str);
-OBJ *ematch_mine(OBJ *player, const char *str);
-OBJ *ematch_near(OBJ *player, const char *str);
-OBJ *ematch_all(OBJ *player, const char *name);
+
+inline OBJ *
+ematch_me(OBJ *player, const char *str)
+{
+	if (!strcmp(str, "me"))
+		return player;
+	else
+		return NULL;
+}
+
+inline OBJ *
+ematch_here(OBJ *player, const char *str)
+{
+	if (!strcmp(str, "here"))
+		return player->location;
+	else
+		return NULL;
+}
+
+inline OBJ *
+ematch_mine(OBJ *player, const char *str)
+{
+	return ematch_at(player, player, str);
+}
+
+inline OBJ *
+ematch_near(OBJ *player, const char *str)
+{
+	return ematch_at(player, player->location, str);
+}
+
+/* all ematch
+ * (not found by the linker if it is not static?)
+ */
+static inline OBJ *
+ematch_all(OBJ *player, const char *name)
+{
+	OBJ *res;
+
+	if (
+			(res = ematch_me(player, name))
+			|| (res = ematch_here(player, name))
+			|| (res = ematch_absolute(name))
+			|| (res = ematch_near(player, name))
+			|| (res = ematch_mine(player, name))
+			|| (res = ematch_player(name))
+	   )
+		return res;
+
+	else
+		return NULL;
+}
 
 #endif /* _MATCH_H */
diff --git a/include/nd/noise.h b/include/nd/noise.h
deleted file mode 100644
index ef3c1b0..0000000
--- a/include/nd/noise.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/* A noise algorithm that wraps well around data type limits.
- *
- * LICENCE: CC BY-NC-SA 4.0
- * For commercial use, contact me at q@qnixsoft.com
- *
- * DEFINITIONS:
- *
- * a point has DIM coordinates
- *
- * a matrix has 2^y (l) edge length.
- * It is an array of noise_t where we want to store the result.
- *
- * A noise feature quad has 2^x (d) edge length
- * and 2^DIM vertices with random values.
- *
- * Other values are the result of a "fade" between those.
- * And they are added to the resulting matrix (for multiple octaves);
- *
- * v is an array that stores a (noise feature) quad's vertices.
- * It has a peculiar order FIXME
- *
- * recursive implementations are here for reference
- * TODO further optimizations
- * TODO does this work in 3D+?
- * TODO improve documentation
- * TODO speed tests
- * TODO think before changing stuff
- * */
-
-#ifndef NOISE_H
-#define NOISE_H
-
-#include <stdint.h>
-#include <stddef.h>
-
-#define NOISE_MAX ((noise_t) -1)
-
-typedef uint32_t noise_t;
-typedef int32_t snoise_t;
-
-typedef struct { unsigned x, w; } octave_t;
-
-void
-noise_oct(noise_t *m, int16_t *s, size_t oct_n,
-	  octave_t *oct, unsigned seed, unsigned cy, uint8_t dim);
-
-#endif
-
-#ifdef NOISE_IMPLEMENTATION
-
-#include <string.h>
-#include "xxhash.h"
-#include "debug.h"
-
-#define HASH XXH32
-
-/* gets a mask for use with get_s */
-#define COORDMASK_LSHIFT(x) (uint16_t) ((((uint16_t) -1) << x))
-
-/* gets a (deterministic) random value for point p */
-static inline noise_t
-noise_r(int16_t *p, unsigned seed, unsigned w, uint8_t dim) {
-	register noise_t v = HASH(p, sizeof(int16_t) * dim, seed);
-		return ((long long unsigned) v) >> w;
-}
-
-/* generate values at quad vertices. */
-static inline void
-noise_get_v(noise_t *v, int16_t *s, uint16_t x, unsigned w, unsigned seed, uint8_t dim) {
-	const int16_t d = 1 << x;
-	int16_t va[dim * (1 << dim)];
-	int i, j;
-
-	for (i = 0; i < (1 << dim); i++) {
-		for (j = 0; j < dim; j++)
-			va[i * dim + j] = s[j] + ((i & (1 << (dim - j - 1))) ? d : 0);
-
-		v[i] = noise_r(va + dim * i, seed, w, dim);
-	}
-}
-
-static inline void
-calc_steps(snoise_t *st,
-	   noise_t *v,
-	   uint16_t z,
-	   uint16_t vl,
-	   unsigned y)
-{
-	int n;
-	for (n = 0; n < vl; n ++)
-		// FIXME FIND PARENS
-		st[n] = ((long) v[n + vl] - v[n]) >> z << y;
-}
-
-static inline void
-step(noise_t *v, snoise_t *st, uint16_t vl, uint16_t mul)
-{
-	int n;
-	for (n = 0; n < vl; n++)
-		v[n] = (long) v[n] + ((long) st[n] * mul);
-}
-
-/* Given a set of vertices,
- * fade between them and set target values acoordingly
- */
-static inline void
-noise_quad(noise_t *c, noise_t *vc, unsigned z, unsigned w, unsigned cy, uint8_t dim) {
-	uint16_t ndim = dim - 1;
-	uint16_t tvl = 1 << ndim; /* length of input values */
-	snoise_t st[(tvl << 1) - 1], *stc = st;
-	noise_t *ce_p[dim], *vt;
-	size_t cd = 1 << cy * ndim; /* (2^y)^ndim */
-	goto start;
-	do {
-		do {
-			/* PUSH */
-			cd >>= cy; ndim--; vc = vt; stc += tvl; tvl >>= 1;
-
-start:			ce_p[ndim] = c + (cd << z);
-			vt = vc + (tvl<<1);
-
-			calc_steps(stc, vc, z, tvl, 0);
-			memcpy(vt, vc, tvl * sizeof(noise_t));
-		} while (ndim);
-
-		for (; c < ce_p[0]; c += cd, vt[0] += *stc)
-		*c += vt[0];
-
-		do {
-			/* POP */
-			++ndim;
-			if (ndim >= dim)
-				return;
-			c -= cd << z; cd <<= cy; vt = vc;
-			tvl <<= 1; stc -= tvl; vc -= (tvl<<1);
-
-			step(vt, stc, tvl, 1);
-			c += cd;
-		} while (c >= ce_p[ndim]);
-	} while (1);
-}
-
-// FIXME there is a memory leak on the non recursive version on linux
-#ifndef __OpenBSD__
-static inline void
-_noise_mr(noise_t *c, noise_t *v, unsigned x, int16_t *qs, uint16_t ndim, unsigned w, unsigned seed, unsigned cy, uint8_t dim) {
-	int i = dim - 1 - ndim;
-	uint16_t ced = (1 << (cy * (ndim + 1))), cd;
-	noise_t *ce = c + ced;
-
-	ced >>= cy;
-	cd = ced << x;
-
-	for (; c < ce; qs[i] += (1<<x), c += cd)
-		if (ndim == 0) {
-			noise_get_v(v, qs, x, w, seed, dim);
-			noise_quad(c, v, x, w, cy, dim);
-		} else
-			_noise_mr(c, v, x, qs, ndim - 1, w, seed, cy, dim);
-
-	qs[i] -= 1 << cy; // reset
-}
-
-static inline void
-_noise_m(noise_t *c, noise_t *v, unsigned x, int16_t *qs, unsigned w, unsigned seed, unsigned cy, uint8_t dim)
-{
-	_noise_mr(c, v, x, qs, dim - 1, w, seed, cy, dim);
-}
-
-#else
-
-static inline void
-_noise_m(noise_t *c, noise_t *v, unsigned x, int16_t *qs, unsigned w, unsigned seed, unsigned cy, uint8_t dim) {
-	noise_t *ce_p[dim];
-	noise_t ced = 1<<(cy * dim);
-	int16_t *qsc = qs; /* quad coordinate */
-	uint16_t ndim = dim - 1;
-
-	goto start;
-
-	do {
-		do { /* PUSH */
-			qsc++; ndim--;
-start:			ce_p[ndim] = c + ced;
-			ced >>= cy;
-		} while (ndim);
-
-		do {
-			noise_get_v(v, qs, x, w, seed, dim);
-			noise_quad(c, v, x, w, cy, dim);
-			*qsc += 1 << x;
-			c += ced << x;
-		} while (c < ce_p[ndim]);
-
-		do { /* POP */
-			*qsc -= 1 << cy;
-			ced <<= cy;
-			c += (ced << x) - ced; /* reset and inc */
-			qsc--; ndim++;
-			*qsc += 1 << x;
-		} while (c >= ce_p[ndim]);
-	} while (ndim < dim);
-}
-
-#endif
-
-/* fixes v (vertex values)
- * when noise quad starts before matrix quad aka x > y aka d > l.
- * assumes ndim to be at least 1
- * vl = 1 << ndim
- */
-static inline void
-__fix_v(noise_t *v, snoise_t *st, int16_t *ms, int16_t *qs, unsigned x, uint16_t vl, unsigned cy) {
-	register noise_t *vn = v + vl;
-	register uint16_t dd = (*ms - *qs) >> cy;
-
-	// TODO make this more efficient
-	calc_steps(st, v, x, vl, cy);
-
-	if (dd) /* ms > qs */
-		step(v, st, vl, dd);
-
-	memcpy(vn, v, sizeof(noise_t) * vl); // COPY to opposite values
-	step(vn, st, vl, 1); // STEP by side length of M
-}
-
-// yes this does work fine a bit of a hack but ok i guess
-static inline void
-noise_fix_v(noise_t *v, int16_t *qs, int16_t *ms, unsigned x, unsigned cy, uint8_t dim) {
-	uint16_t vl = 1 << dim;
-	snoise_t st[vl - 1], *stc = st;
-	int ndim = dim - 1;
-	int first = 1;
-	vl >>= 1;
-
-	for (;;) {
-		__fix_v(v, stc, ms, qs, x, vl, cy);
-
-		if (ndim) {
-			ndim--; qs++; ms++; vl >>= 1; stc += vl; /* PUSH */
-		} else if (first) {
-			v += vl << 1;
-			first = 0;
-		} else break;
-	}
-}
-
-static inline void
-noise_get_s(int16_t *s, int16_t *p, uint16_t mask, uint8_t dim) {
-	int i;
-	for (i = 0; i < dim; i++)
-		s[i] = ((int16_t) p[i]) & mask;
-}
-
-/* Fills in matrix "mat" with deterministic noise.
- * "ms" is the matrix's min point in the world;
- * "x" 2^x = side length of noise quads;
- * "y" 2^y = side length of matrix "mat".
- * */
-static inline void
-noise_m(noise_t *mat, int16_t *ms, unsigned x,
-	unsigned w, unsigned seed, unsigned cy, uint8_t dim)
-{
-	noise_t v[(1 << (dim + 1)) - 1];
-
-	if (cy > x)
-		_noise_m(mat, v, x, ms, w, seed, cy, dim);
-	else {
-		int16_t qs[dim];
-
-		noise_get_s(qs, ms, COORDMASK_LSHIFT(x), dim);
-		noise_get_v(v, qs, x, w, seed, dim);
-		if (x > cy)
-			noise_fix_v(v, qs, ms, x, cy, dim);
-
-		noise_quad(mat, v, cy, w, cy, dim);
-	}
-}
-
-void
-noise_oct(noise_t *m, int16_t *s, size_t oct_n,
-	  octave_t *oct, unsigned seed, unsigned cy, uint8_t dim)
-{
-	unsigned cm = 1 << dim * cy;
-	octave_t *oe;
-	memset(m, 0, sizeof(noise_t) * cm);
-	for (oe = oct + oct_n; oct < oe; oct++)
-		noise_m(m, s, oct->x, oct->w, seed, cy, dim);
-}
-
-#endif
diff --git a/include/nd/sdb.h b/include/nd/sdb.h
deleted file mode 100644
index 91221be..0000000
--- a/include/nd/sdb.h
+++ /dev/null
@@ -1,544 +0,0 @@
-#ifndef SDB_H
-#define SDB_H
-
-// see http://www.vision-tools.com/h-tropf/multidimensionalrangequery.pdf
-// TODO support duplicates
-// TODO use cursors instead of a callback
-
-#include <db4/db.h>
-/* value returned when no position is found */
-#define SDB_INVALID 130056652770671ULL
-
-typedef void (*sdb_callback_t)(int16_t *pv, void *ptr);
-
-typedef struct {
-	DB *primary;
-	DB *point;
-	uint8_t dim, y;
-	size_t ekl; // extra key length
-	size_t vl; // value length
-	sdb_callback_t callback;
-	uint8_t max_dim;
-	DBTYPE type;
-} sdb_t;
-
-int sdb_init(sdb_t *, uint8_t dim, uint8_t y, size_t ekl, size_t len, DB_ENV *dbe, char *fname, DBTYPE type);
-void sdb_search(sdb_t *, int16_t *min, int16_t *max);
-int sdb_close(sdb_t *);
-void sdb_where(int16_t *p, sdb_t *, void *thing);
-int sdb_delete(sdb_t *, void *what);
-void *sdb_get(sdb_t *, int16_t *at);
-void sdb_put(sdb_t *, void *thing, int flags);
-
-#ifdef SDB_IMPLEMENTATION
-#include "debug.h"
-
-typedef uint64_t morton_t; // up to 4d morton
-typedef int64_t smorton_t;
-
-static const morton_t m1 = 0x111111111111ULL;
-static const morton_t m0 = 0x800000000000ULL;
-
-morton_t
-pos_morton(int16_t *p, uint8_t dim)
-{
-	morton_t res = 0;
-	int i, j;
-
-	for (i = 0; i < dim; i++)
-		for (j = 0; j < 16; j++) {
-			register uint16_t v = (smorton_t) p[i] + SHRT_MAX;
-			res |= ((morton_t) (v >> j) & 1) << (j * dim + i);
-		}
-
-	/* res <<= (4 - dim) * 16; */
-
-	return res;
-}
-
-/* leave me alone */
-void
-morton_pos(int16_t *p, morton_t code, uint8_t dim)
-{
-	uint16_t up[dim];
-	int i, j;
-
-	for (i = 0; i < dim; i++) {
-		up[i] = 0;
-		for (j = 0; j < 16; j++) {
-			int16_t v = (code >> (j * dim + i)) & 1;
-			/* int16_t v = (code >> (j * dim + i + (4 - dim) * 16)) & 1; */
-			up[i] |= v << j;
-		}
-	}
-
-	for (i = 0; i < dim; i++) {
-		p[i] = (smorton_t) up[i] - SHRT_MAX;
-	}
-}
-
-static inline void
-point_debug(int16_t *p, char *label, int8_t dim)
-{
-	warn("point %s", label);
-	for (int i = 0; i < dim; i++)
-		warn(" %d", p[i]);
-	warn("\n");
-}
-
-static inline void
-morton_debug(morton_t x, uint8_t dim)
-{
-	int16_t p[dim];
-	morton_pos(p, x, dim);
-	warn("%llx ", x);
-	point_debug(p, "morton_debug", dim);
-}
-
-static inline int
-morton_cmp(morton_t a, morton_t b, uint8_t dim, uint8_t cdim) {
-	int	shift_start = 64 - (4 - dim) * 16 - (dim - cdim),
-		shift;
-
-	for (shift = shift_start; shift >= 0; shift -= dim) {
-		if ((b >> shift) & 1) {
-			if (!((a >> shift) & 1))
-				return 1;
-		}
-		else if ((a >> shift) & 1)
-			return -1;
-	}
-
-	return 0;
-}
-
-static inline morton_t
-sdb_qload_0(morton_t c, morton_t lm0, morton_t lm1) // LOAD(0111...
-{
-	return (c & (~ lm0)) | lm1;
-}
-
-static inline morton_t
-sdb_qload_1(morton_t c, morton_t lm0, morton_t lm1) // LOAD(1000...
-{
-	return (c | lm0) & (~ lm1);
-}
-
-static inline int
-sdb_inrange(morton_t dr, morton_t min, morton_t max, uint8_t dim)
-{
-	for (int i = 0; i < dim; i++)
-		if (morton_cmp(min, dr, dim, i) < 0
-		    || morton_cmp(dr, max, dim, i) < 0)
-		    return 0;
-
-	return 1;
-}
-
-static inline void
-sdb_compute_bmlm(morton_t *bm, morton_t *lm,
-	     morton_t dr, morton_t min, morton_t max)
-{
-	register morton_t lm0 = m0, lm1 = m1;
-
-	for (; lm0; lm0 >>= 1, lm1 >>= 1)
-	{
-		register morton_t a = dr & lm0,
-			 b = min & lm0,
-			 c = max & lm0;
-
-		if (b) { // ? 1 ?
-			if (!a && c) { // 0 1 1
-				*bm = min;
-				break;
-			}
-
-		} else if (a) // 1 0 ?
-			if (c) { // 1 0 1
-				*lm = sdb_qload_0(max, lm0, lm1);
-				min = sdb_qload_1(min, lm0, lm1);
-			} else { // 1 0 0
-				*lm = max;
-				break;
-			}
-
-		else if (c) { // 0 0 1
-			// BIGMIN
-			*bm = sdb_qload_1(min, lm0, lm1);
-			max = sdb_qload_0(max, lm0, lm1);
-		}
-	}
-}
-
-static inline int
-sdb_range_unsafe(sdb_t *sdb, morton_t min, morton_t max)
-{
-	morton_t lm = 0, code;
-	DBC *cur;
-	DBT key, pkey, data;
-	int ret = 0, res = 0;
-	int dbflags;
-
-	/* warn("sdb_range_unsafe min %llx max %llx\n", min, max); */
-	/* morton_debug(min, sdb->dim); */
-	/* morton_debug(max, sdb->dim); */
-	/* CBUG((smorton_t) min > (smorton_t) max); */
-	if (sdb->point->cursor(sdb->point, NULL, &cur, 0))
-		return -1;
-
-	memset(&data, 0, sizeof(DBT));
-	memset(&pkey, 0, sizeof(DBT));
-
-	dbflags = DB_SET_RANGE;
-	memset(&key, 0, sizeof(DBT));
-	key.data = &min;
-	key.size = sizeof(min);
-
-next:	ret = cur->c_pget(cur, &key, &pkey, &data, dbflags);
-	dbflags = DB_NEXT;
-
-	switch (ret) {
-	case 0: break;
-	case DB_NOTFOUND:
-		ret = 0;
-		goto out;
-	default:
-		BUG("%s", db_strerror(ret));
-		goto out;
-	}
-
-	code = *(morton_t*) key.data;
-
-	if ((smorton_t) code > (smorton_t) max)
-		goto out;
-
-	if (sdb_inrange(code, min, max, sdb->dim)) {
-		int16_t pv[sdb->dim];
-		morton_pos(pv, code, sdb->dim);
-		sdb->callback(pv, pkey.data);
-		res++;
-	} else
-		sdb_compute_bmlm(&min, &lm, code, min, max);
-
-	goto next;
-
-out:	if (cur->close(cur) || ret)
-		return -1;
-
-	return res;
-}
-
-static inline int16_t
-morton_get(morton_t code, uint8_t i, uint8_t dim)
-{
-	uint16_t u = 0;
-	int j;
-
-	for (j = 0; j < 16; j++) {
-		int16_t v = (code >> (j * dim + i)) & 1;
-		u |= v << j;
-	}
-
-	return (smorton_t) u - SHRT_MAX;
-}
-
-static inline morton_t
-morton_set(morton_t x, uint8_t i, int16_t value, uint8_t dim)
-{
-	morton_t res = x;
-	int j;
-
-	for (j = 0; j < 16; j++) {
-		register unsigned shift = j * dim + i;
-		register uint16_t v = (smorton_t) value + SHRT_MAX;
-		res &= ~(1 << shift);
-		res |= ((morton_t) (v >> j) & 1) << shift;
-	}
-
-	return res;
-}
-
-/* this version accounts for type limits,
- * and wraps around them in each direction, if necessary
- * TODO iterative form
- */
-
-static int
-sdb_range_safe(sdb_t *sdb, morton_t min, morton_t max, int dim)
-{
-	int i, ret, aux;
-	morton_t lmin = min,
-		 lmax = max;
-
-	/* warn("sdb_range_safe %d %llx %llx\n", dim, min, max); */
-	/* morton_debug(min, sdb->dim); */
-	/* morton_debug(max, sdb->dim); */
-
-	for (i = dim; i < sdb->dim; i++) {
-		if (morton_cmp(lmin, lmax, sdb->dim, i) >= 0)
-			continue;
-
-		int16_t lmaxi = morton_get(lmax, i, sdb->dim);
-
-		// lmin is correct
-		lmax = morton_set(lmax, i, SHRT_MAX, sdb->dim);
-		aux = sdb_range_safe(sdb, lmin, lmax, i + 1);
-
-		if (aux < 0)
-			return aux;
-
-		lmin = morton_set(lmin, i, SHRT_MIN, sdb->dim);
-		lmax = morton_set(lmax, i, lmaxi, sdb->dim);
-		ret = sdb_range_safe(sdb, lmin, lmax, i + 1);
-
-		if (ret < 0)
-			return ret;
-
-		return aux + ret;
-	}
-
-	return sdb_range_unsafe(sdb, lmin, lmax);
-}
-
-// TODO make this actually "safe" aka working across data type boundaries
-static int
-sdb_hrange_safe(sdb_t *sdb, int16_t *min, int16_t *max, uint8_t dim)
-{
-	int16_t lmin[sdb->dim], lmax[sdb->dim];
-	int16_t mini, maxi, j, step = 1 << sdb->y;
-	int ret = 0;
-
-	memcpy(lmin, min, sdb->dim * sizeof(int16_t));
-	memcpy(lmax, max, sdb->dim * sizeof(int16_t));
-
-	mini = (min[dim] >> sdb->y) << sdb->y;
-	maxi = (max[dim] >> sdb->y) << sdb->y;
-
-	/* warn("sdb_hrange_safe %hhu\n", dim); */
-	/* point_debug(min, "min", sdb->dim); */
-	/* point_debug(max, "max", sdb->dim); */
-
-	if (mini > maxi) {
-		if (dim == 0)
-			for (j = mini; j <= SHRT_MAX; j++) {
-				lmin[dim] = j;
-				void *ptr = sdb_get(sdb, lmin);
-				if (ptr)
-					ret++;
-				sdb->callback(lmin, ptr);
-			}
-		else {
-			lmax[dim] = SHRT_MAX;
-			for (j = mini; j <= SHRT_MAX; j += step) {
-				lmin[dim] = j;
-				ret += sdb_hrange_safe(sdb, lmin, lmax, dim - 1);
-			}
-
-			lmax[dim] = maxi;
-			for (j = SHRT_MIN; j <= maxi; j += step) {
-				lmin[dim] = j;
-				ret += sdb_hrange_safe(sdb, lmin, lmax, dim - 1);
-			}
-		}
-	} else {
-		if (dim == 0)
-			for (j = mini; j <= maxi; j += step) {
-				lmin[dim] = j;
-				void *ptr = sdb_get(sdb, lmin);
-				if (ptr)
-					ret++;
-				sdb->callback(lmin, ptr);
-			}
-		else {
-			lmax[dim] = maxi;
-			for (j = mini; j <= maxi; j += step) {
-				lmin[dim] = j;
-				ret += sdb_hrange_safe(sdb, lmin, lmax, dim - 1);
-			}
-		}
-	}
-
-	return ret;
-}
-
-void
-sdb_search(sdb_t *sdb, int16_t *min, int16_t *max)
-{
-	/* warn("sdb_search %llx %llx\n", morton_min, morton_max); */
-
-	if (sdb->type == DB_BTREE) {
-		morton_t morton_min = pos_morton(min, 4),
-			 morton_max = pos_morton(max, 4);
-
-		sdb_range_safe(sdb, morton_min, morton_max, 0);
-	} else {
-		sdb_hrange_safe(sdb, min, max, sdb->dim - 1);
-	}
-	/* warn("sdb_search result %d\n", ret); */
-
-}
-
-static int
-sdb_cmp(DB *sec, const DBT *a_r, const DBT *b_r)
-{
-	morton_t a = * (morton_t*) a_r->data,
-		 b = * (morton_t*) b_r->data;
-
-	return b > a ? -1 : (a > b ? 1 : 0);
-}
-
-static int
-sdb_mki_code(DB *sec, const DBT *key, const DBT *data, DBT *result)
-{
-	morton_t *code = (morton_t *) malloc(sizeof(morton_t));
-	*code = pos_morton(data->data, 4);
-	result->size = sizeof(morton_t);
-	result->data = code;
-	result->flags = DB_DBT_APPMALLOC;
-	return 0;
-}
-
-int
-sdb_init(sdb_t *sdb, uint8_t dim, uint8_t y, size_t ekl, size_t len, DB_ENV *dbe, char *fname, DBTYPE type) {
-	int ret = 0;
-
-	sdb->dim = dim;
-	sdb->ekl = ekl;
-	sdb->vl = len - ekl - sizeof(int16_t) * 4;
-	sdb->type = type;
-	sdb->y = y;
-	sdb->max_dim = 4; // change this if coords can be int8_t for example (a lot of other fixed needed for that to work)
-
-	if ((ret = db_create(&sdb->primary, dbe, 0))
-	    || (ret = sdb->primary->open(sdb->primary, NULL, fname, fname, DB_HASH, DB_CREATE, 0664)))
-		return ret;
-
-	if (type == DB_BTREE) {
-		if ((ret = db_create(&sdb->point, dbe, 0))
-		    || (ret = sdb->point->set_bt_compare(sdb->point, sdb_cmp))
-		    || (ret = sdb->point->open(sdb->point, NULL, fname, fname, DB_BTREE, DB_CREATE, 0664))
-		    || (ret = sdb->primary->associate(sdb->primary, NULL, sdb->point, sdb_mki_code, DB_CREATE)))
-
-			sdb->point->close(sdb->point, 0);
-		else
-			return 0;
-	} else {
-		CBUG(type != DB_HASH);
-
-		if ((ret = db_create(&sdb->point, dbe, 0))
-		    || (ret = sdb->point->open(sdb->point, NULL, fname, fname, DB_HASH, DB_CREATE, 0664))
-		    || (ret = sdb->primary->associate(sdb->primary, NULL, sdb->point, sdb_mki_code, DB_CREATE)))
-
-			sdb->point->close(sdb->point, 0);
-		else
-			return 0;
-	}
-
-	sdb->primary->close(sdb->primary, 0);
-	return ret;
-}
-
-int
-sdb_close(sdb_t *sdb)
-{
-	return sdb->point->close(sdb->point, 0)
-		|| sdb->primary->close(sdb->primary, 0);
-}
-
-void
-sdb_put(sdb_t *sdb, void *thing, int flags)
-{
-	DBT key;
-	DBT data;
-	int ret;
-
-	memset(&key, 0, sizeof(DBT));
-	memset(&data, 0, sizeof(DBT));
-
-	key.data = thing;
-	key.size = sizeof(int16_t) * 4 + sdb->ekl;
-	data.data = thing;
-	data.size = key.size + sdb->vl;
-
-	ret = sdb->primary->put(sdb->primary, NULL, &key, &data, flags);
-	CBUG(ret);
-}
-
-morton_t
-_sdb_where(sdb_t *sdb, void *thing)
-{
-	int bad;
-	DBT key;
-	DBT data;
-
-	memset(&key, 0, sizeof(DBT));
-	memset(&data, 0, sizeof(DBT));
-	key.data = thing;
-	key.size = sizeof(int16_t) * 4 + sdb->ekl;
-
-	if ((bad = sdb->primary->get(sdb->primary, NULL, &key, &data, 0))) {
-		static morton_t code = SDB_INVALID;
-		if (bad && bad != DB_NOTFOUND)
-			warn("sdb_where %p %s", thing, db_strerror(bad));
-		return code;
-	}
-
-	return * (morton_t *) data.data;
-}
-
-void
-sdb_where(int16_t *p, sdb_t *sdb, void *thing)
-{
-	morton_t x = _sdb_where(sdb, thing);
-	morton_pos(p, x, sdb->dim);
-}
-
-void *
-sdb_get(sdb_t *sdb, int16_t *p)
-{
-	morton_t at = pos_morton(p, 4);
-	DBC *cur;
-	DBT pkey;
-	DBT data;
-	DBT key;
-	int res;
-
-	if (sdb->point->cursor(sdb->point, NULL, &cur, 0))
-		return NULL;
-
-	memset(&pkey, 0, sizeof(DBT));
-	memset(&key, 0, sizeof(DBT));
-	memset(&data, 0, sizeof(DBT));
-
-	key.size = sizeof(at);
-	key.data = &at;
-
-	while (1) {
-		res = cur->c_pget(cur, &key, &pkey, &data, DB_SET);
-		switch (res) {
-		case 0:
-			cur->close(cur);
-			return data.data;
-		case DB_NOTFOUND:
-			cur->close(cur);
-			return NULL;
-		default:
-			BUG("%s", db_strerror(res));
-			return NULL;
-		}
-	}
-}
-
-int
-sdb_delete(sdb_t *sdb, void *what)
-{
-	DBT key;
-	memset(&key, 0, sizeof(key));
-	key.data = what;
-	key.size = sizeof(int16_t) * 4 + sdb->ekl;
-	return sdb->primary->del(sdb->primary, NULL, &key, 0);
-}
-
-#endif
-
-#endif
diff --git a/include/nddb.h b/include/nddb.h
deleted file mode 100644
index de42b1d..0000000
--- a/include/nddb.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef NDDB_H
-#define NDDB_H
-
-#ifdef __OpenBSD__
-#include <db4/db.h>
-#else
-#include <db.h>
-#endif
-
-#endif
diff --git a/include/noise.h b/include/noise.h
index e07cfa9..7bfc990 100644
--- a/include/noise.h
+++ b/include/noise.h
@@ -1,11 +1,15 @@
-#ifndef ND_NOISE_H
-#define ND_NOISE_H
+#ifndef NOISE_H
+#define NOISE_H
 
-#include "nd/noise.h"
 #include "biome.h"
 #include "plant.h"
 #include "hash.h"
 
+#define NOISE_MAX ((noise_t) -1)
+
+typedef uint32_t noise_t;
+typedef int32_t snoise_t;
+
 struct bio {
 	coord_t tmp;
 	ucoord_t rn;
diff --git a/include/object.h b/include/object.h
index b14d4f8..a2895ef 100644
--- a/include/object.h
+++ b/include/object.h
@@ -156,6 +156,7 @@ typedef struct entity {
 	unsigned lvl, spend, cxp;
 	unsigned attr[ATTR_MAX];
 	unsigned equipment[ES_MAX];
+	char *gpt;
 } ENT;
 
 enum equipment_flags {
diff --git a/include/skeleton.h b/include/skeleton.h
index 698adf4..b49e0ef 100644
--- a/include/skeleton.h
+++ b/include/skeleton.h
@@ -3,6 +3,9 @@
 
 #include "stdint.h"
 
+typedef int16_t coord_t;
+typedef uint16_t ucoord_t;
+
 enum element {
 	ELM_PHYSICAL,
 	ELM_FIRE,
@@ -28,8 +31,8 @@ typedef struct entity_skeleton {
 
 struct plant_skeleton {
 	char const *pre, small, big, *post;
-	int16_t tmp_min, tmp_max;
-	uint16_t rn_min, rn_max;
+	coord_t tmp_min, tmp_max;
+	ucoord_t rn_min, rn_max;
 	struct drop *drop[32];
 	unsigned y;
 };
diff --git a/include/spacetime.h b/include/spacetime.h
index 498ea43..5c41531 100644
--- a/include/spacetime.h
+++ b/include/spacetime.h
@@ -3,7 +3,6 @@
 
 #include <limits.h>
 #include <stdint.h>
-#include <time.h>
 #include "object.h"
 
 // adds 2^DAYTICK_Y to day tick until it reaches DAYSIZE
diff --git a/include/spell.h b/include/spell.h
index 478c938..5a24e7a 100644
--- a/include/spell.h
+++ b/include/spell.h
@@ -20,7 +20,6 @@ extern element_t element_map[];
 #define DEBUF_TYPE(sp) (sp->flags & DEBUF_TYPE_MASK)
 
 #define SPELL_SKELETON(idx) (&spell_skeleton_map[idx])
-#define SPELL_REF(sp) (sp - &spell_skeleton_map[0])
 #define SPELL_COST(dmg, y, no_bdmg) (no_bdmg ? 0 : dmg) + dmg / (1 << y)
 
 enum spell_type {
@@ -45,6 +44,5 @@ int debufs_process(OBJ *player);
 void debuf_notify(OBJ *player, struct debuf *d, short val);
 int spell_cast(OBJ *player, OBJ *target, unsigned slot);
 int spells_cast(OBJ *player, OBJ *target);
-void spells_birth(OBJ *object);
 
 #endif
diff --git a/include/utils.h b/include/utils.h
index 1f7331e..0c66767 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -1,12 +1,8 @@
 #ifndef UTILS_H
 #define UTILS_H
 
-struct popen2 {
-	int in, out, pid;
-};
-
 int ok_name(const char *name);
 int string_prefix(const char *string, const char *prefix);
-int popen2(struct popen2 *child, const char *cmdline);
+char *gpt(char *prompt);
 
 #endif
diff --git a/js/Unused.jsx b/js/Unused.jsx
deleted file mode 100644
index e058372..0000000
--- a/js/Unused.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React, { useRef, useEffect } from "react";
-import { hot } from "react-hot-loader/root";
-import "xterm/css/xterm.css";
-// import mcp from "mcp"
-import { Terminal } from "xterm";
-import { FitAddon } from "xterm-addon-fit";
-
-const term = new Terminal();
-const fitAddon = new FitAddon();
-
-export default hot(function App() {
-	const ref = useRef(null);
-
-	useEffect(() => {
-		term.loadAddon(fitAddon);
-		term.open(ref.current);
-		console.log(fitAddon.proposeDimensions());
-		fitAddon.fit();
-		term.write("\x1B[8;30;120t"
-			+ "Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ");
-	}, []);
-
-	return <div ref={ref}></div>;
-});
diff --git a/js/actions.jsx b/js/actions.jsx
deleted file mode 100644
index 3184320..0000000
--- a/js/actions.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-const ACTIONS = {
-  LOOK: 0,
-  KILL: 1,
-  SHOP: 2,
-  DRINK: 3,
-  OPEN: 4,
-  CHOP: 5,
-  FILL: 6,
-  GET: 7,
-  TALK: 8,
-  PUT: 9,
-  EQUIP: 10,
-  DROP: 11,
-  EAT: 12,
-  13: 13,
-  INVENTORY: 14,
-  K: 15,
-  J: 16,
-};
-
-export
-const ACTIONS_LABEL = {
-  [ACTIONS.LOOK]: "look",
-  [ACTIONS.KILL]: "kill",
-  [ACTIONS.SHOP]: "shop",
-  [ACTIONS.DRINK]: "drink",
-  [ACTIONS.OPEN]: "open",
-  [ACTIONS.CHOP]: "chop",
-  [ACTIONS.FILL]: "fill",
-  [ACTIONS.GET]: "get",
-  [ACTIONS.TALK]: "talk",
-  [ACTIONS.PUT]: "put",
-  [ACTIONS.EQUIP]: "equip",
-  [ACTIONS.DROP]: "drop",
-  [ACTIONS.EAT]: "eat",
-  [ACTIONS.RESERVERD]: "reserved",
-  [ACTIONS.INVENTORY]: "inventory",
-  [ACTIONS.K]: "K",
-  [ACTIONS.J]: "J",
-};
-
-export default ACTIONS
diff --git a/js/canvas.jsx b/js/canvas.js
similarity index 51%
rename from js/canvas.jsx
rename to js/canvas.js
index e976d9e..40a40f1 100644
--- a/js/canvas.jsx
+++ b/js/canvas.js
@@ -1,9 +1,36 @@
 let canvas = document.querySelector("canvas");
 let ctx = canvas.getContext('2d');
 let tilemap = document.createElement("img");
-tilemap.src = process.env.CONFIG_BASEDIR + "/art/tilemap.png";
+tilemap.src = "/neverdark/art/tilemap.png";
 tilemap.classList.add("dn");
 
+const ACT_LOOK = 0;
+const ACT_KILL = 1;
+const ACT_SHOP = 2;
+const ACT_DRINK = 3;
+const ACT_OPEN = 4;
+const ACT_CHOP = 5;
+const ACT_FILL = 6;
+const ACT_GET = 7;
+const ACT_TALK = 8;
+const ACT_PUT = 9;
+const ACT_EQUIP = 10;
+const ACT_DROP = 11;
+const ACT_EAT = 12;
+const ACT_13 = 13;
+const ACT_INVENTORY = 14;
+const ACT_K = 15;
+const ACT_J = 16;
+
+let actions_lbl = [
+	"look", "kill", "shop",
+	"drink", "open", "chop",
+	"fill", "get", "talk",
+        "put", "equip", "drop",
+        "eat", "reserved", "inventory",
+        "K", "J",
+];
+
 let actions_tiles = [];
 
 function tilemap_cache() {
diff --git a/js/index.jsx b/js/index.jsx
deleted file mode 100644
index 5115e13..0000000
--- a/js/index.jsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import setup from "@tty-pt/scripts";
-import App from "./App";
-
-setup(App, "./App", document.getElementById("main"), () => require("./App").default);
diff --git a/js/App.jsx b/js/main.js
similarity index 86%
rename from js/App.jsx
rename to js/main.js
index 5a92b8e..47134a2 100644
--- a/js/App.jsx
+++ b/js/main.js
@@ -1,15 +1,9 @@
-import React, {
-  useState, useEffect, useRef,
-  useCallback, useReducer, useContext,
-} from "react";
-import { hot } from "react-hot-loader/root";
-import ReactDOM from "react-dom";
-import ACTIONS, { ACTIONS_LABEL } from "actions";
-import mcp from "mcp";
-import tty_proc from "tty";
-import canvas from "canvas";
-import "vim.css";
-const baseDir = process.env.CONFIG_BASEDIR || "";
+#include "mcp.js"
+#include "tty.js"
+#include "canvas.js"
+#include "vendor.js"
+
+#define CONFIG_PROTO "wss"
 
 class Modal extends React.Component {
 	constructor(props) {
@@ -23,7 +17,7 @@ class Modal extends React.Component {
 			if (e.target == this.el)
 				this.props.setOpen(false);
 		};
-		this.el.className = 'modal abs sfv v0 c fcc oh f';
+		this.el.className = 'modal abs sfv vn c fcc oh f';
 		// this.el.classList.remove('dn');
 	}
 
@@ -71,7 +65,7 @@ const useModal = _useModal.bind(null, modal);
 
 // window.onorientationchange = scroll_reset;
 
-mcp.init();
+mcp_init();
 
 const atiles = [
         "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAQklEQVQ4jWNgGL7g4OFb/9ExWZpwYZyaiXEBhiH4NBNSA+fg8wpWW5H1ETIAl98xvEHIEJyacfmdEB8vICsdjFAAAGW58imbroFwAAAAAElFTkSuQmCC",
@@ -100,7 +94,7 @@ function useSession(onOpen, onMessage, onClose) {
         const [ session, setSession ] = useState(null);
 
         const connect = useCallback(() => {
-                const ws = new WebSocket(process.env.CONFIG_PROTO + "://" + window.location.hostname + ':4201', 'text');
+                const ws = new WebSocket(CONFIG_PROTO + "://" + window.location.hostname + ':4201', 'text');
                 ws.binaryType = 'arraybuffer';
                 setSession(ws);
         }, []);
@@ -121,20 +115,18 @@ function useSession(onOpen, onMessage, onClose) {
                 };
         };
 
-	const _onClose = useCallback(evt => onClose(evt, connect));
-
         const updateCloseHandler = () => {
                 if (!session) return;
-                session.addEventListener('close', _onClose);
+                session.addEventListener('close', onClose);
                 return () => {
-                        session.removeEventListener('close', _onClose);
+                        session.removeEventListener('close', onClose);
                 };
         };
 
         useEffect(connect, []);
         useEffect(updateOpenHandler, [session, onOpen]);
         useEffect(updateMessageHandler, [session, onMessage]);
-        useEffect(updateCloseHandler, [session, _onClose]);
+        useEffect(updateCloseHandler, [session, onClose]);
 
         function sendMessage(text) {
                 // console.log("sendMessage", text);
@@ -334,7 +326,7 @@ function GameContextProvider(props) {
         );
 
         function onMessage(ev) {
-                const mcp_arr = mcp.proc(ev.data);
+                const mcp_arr = mcp_proc(ev.data);
                 for (let i = 0; i < mcp_arr.length; i++) {
                         // console.log(mcp_arr[i]);
                         dispatch(mcp_arr[i]);
@@ -350,19 +342,11 @@ function GameContextProvider(props) {
 
         function onOpen() {
                 output("socket connection open");
-
-          if (process.env.development) {
-            sendMessage("auth test");
-          } else {
-            sendMessage("auth " + getCookie("QSESSION"));
-          }
-          
                 setOpen(true);
         }
 
-        function onClose(evt, connect) {
+        function onClose() {
                 output("socket connection closed");
-		connect();
         }
 
         const [ connect, sendMessage, session ] = useSession(onOpen, onMessage, onClose);
@@ -373,6 +357,7 @@ function GameContextProvider(props) {
                         session,
                         sendMessage,
                         dispatch,
+                        open,
                 }}
         >
                 { children }
@@ -401,9 +386,9 @@ function RoomTitleAndArt() {
         if (!obj)
                 return null;
 
-        const src = baseDir + "/art/" + (obj.art || "unknown.jpg");
+        const src = "/neverdark/art/" + (obj.art || "unknown.jpg");
 
-        return (<div className="v0 fg f fic">
+        return (<div className="vn fg f fic">
                 <div className="tm pxs tac">{ obj.name }</div>
                 <img className="sr2" src={src} />
         </div>);
@@ -413,13 +398,13 @@ function Tabs(props) {
         const { children } = props;
         const [ activeTab, setActiveTab ] = useState(0);
 
-        return (<div className="fg v0">
-                <div className="h0">
+        return (<div className="fg vn">
+                <div className="_n">
                         { children.map((child, idx) => {
                                 const { label } = child.props;
                                 return (<a key={idx}
                                         onClick={() => setActiveTab(idx)}
-                                        className={activeTab == idx ? 'p8 c0' : 'p8'}
+                                        className={activeTab == idx ? 'ps c0' : 'ps'}
                                 >
                                         {label}
                                 </a>);
@@ -438,8 +423,8 @@ function Tabs(props) {
 function Stat(props) {
         const { label, value } = props;
 
-        return (<div className="h8">
-                <div className="tbbold">{label}</div><div>{value}</div>
+        return (<div className="_s">
+                <div className="tb">{label}</div><div>{value}</div>
         </div>);
 }
 
@@ -471,7 +456,7 @@ function Equipment(props) {
                 return null;
 
         if (!equipment[eql])
-                return <div className="s32 c0"></div>;
+                return <div className="sl c0"></div>;
 
         return (<Avatar
                 item={equipment[eql]}
@@ -487,8 +472,8 @@ function PlayerTabs() {
                 return null;
 
         return (<Tabs>
-                <div label="stats" className="p8 h8 f">
-                        <div className="v8 fg">
+                <div label="stats" className="ps _s f">
+                        <div className="vs fg">
                                 <Stat label="str" value={stats.str} />
                                 <Stat label="con" value={stats.con} />
                                 <Stat label="dex" value={stats.dex} />
@@ -496,7 +481,7 @@ function PlayerTabs() {
                                 <Stat label="wiz" value={stats.wiz} />
                                 <Stat label="cha" value={stats.cha} />
                         </div>
-                        <div className="v8 fg">
+                        <div className="vs fg">
                                 <Stat label="dodge" value={stats.dodge} />
                                 <Stat label="dmg" value={stats.dmg} />
                                 <Stat label="mdmg" value={stats.mdmg} />
@@ -504,34 +489,34 @@ function PlayerTabs() {
                                 <Stat label="mdef" value={stats.mdef} />
                         </div>
                 </div>
-                <div label="equipment" className="p8 v8">
-                        <div className="h8 f">
-                                <div className="s32"></div>
-                                <div className="s32"></div>
+                <div label="equipment" className="ps vs">
+                        <div className="_s f">
+                                <div className="sl"></div>
+                                <div className="sl"></div>
                                 <Equipment eql={EQL_HEAD} />
-                                <div className="s32"></div>
-                                <div className="s32"></div>
+                                <div className="sl"></div>
+                                <div className="sl"></div>
                         </div>
-                        <div className="h8 f">
+                        <div className="_s f">
                                 <Equipment eql={EQL_RHAND} />
-                                <div className="s32"></div>
+                                <div className="sl"></div>
                                 <Equipment eql={EQL_NECK} />
                                 <Equipment eql={EQL_BACK} />
-                                <div className="s32"></div>
+                                <div className="sl"></div>
                         </div>
-                        <div className="h8 f">
+                        <div className="_s f">
                                 <Equipment eql={EQL_RFINGER} />
-                                <div className="s32"></div>
+                                <div className="sl"></div>
                                 <Equipment eql={EQL_CHEST} />
-                                <div className="s32"></div>
+                                <div className="sl"></div>
                                 <Equipment eql={EQL_LFINGER} />
                         </div>
-                        <div className="h8 f">
-                                <div className="s32"></div>
-                                <div className="s32"></div>
+                        <div className="_s f">
+                                <div className="sl"></div>
+                                <div className="sl"></div>
                                 <Equipment eql={EQL_PANTS} />
-                                <div className="s32"></div>
-                                <div className="s32"></div>
+                                <div className="sl"></div>
+                                <div className="sl"></div>
                         </div>
                 </div>
         </Tabs>);
@@ -540,9 +525,9 @@ function PlayerTabs() {
 function MiniMap(props) {
         const { view, target } = useContext(GameContext);
 
-        if (target || !view)
+        if (target)
                 return null;
-        
+
         return <div className="fac"><pre id="map" dangerouslySetInnerHTML={{ __html: view }}></pre></div>;
 }
 
@@ -554,7 +539,7 @@ function TargetTitleAndArt() {
 
         const obj = objects[target];
 
-  const src = baseDir + "/art/" + (obj.art || "unknown_small.jpg");
+        const src = "/neverdark/art/" + (obj.art || "unknown_small.jpg");
 
         return (<>
                 <div className="tm pxs tac">{obj.name}</div>
@@ -570,8 +555,8 @@ function Avatar(props) {
 
         if (item.avatar)
                 return <img
-                        className={"sh" + size + " sv" + size}
-                        src={baseDir + "/art/" + item.avatar}
+                        className={"s_" + size + " sv" + size}
+                        src={"/neverdark/art/" + item.avatar}
                         { ...rest }
                 />;
         else
@@ -584,7 +569,7 @@ function Avatar(props) {
 
 function ContentsItem(props) {
         const { item, onClick, activeItem, isShop } = props;
-        const className = "f fic pxs h8 " + (activeItem == item.dbref ? 'c0' : "");
+        const className = "f fic pxs _s " + (activeItem == item.dbref ? 'c0' : "");
 
         return (<a className={className} onClick={onClick}>
                 <Avatar item={item} />
@@ -612,45 +597,45 @@ function Contents(props) {
                         onClick={e => onItemClick(e, item)} />;
         });
 
-        return (<div className="v0 fg oa icec">
+        return (<div className="vn fg oa icec">
                 { contentsEl }
         </div>);
 }
 
 function RB(props) {
         const { children, onClick } = props;
-        return <a className="round ts26 p8 c0" onClick={onClick}>{ children }</a>;
+        return <a className="round tsxl ps c0" onClick={onClick}>{ children }</a>;
 }
 
 function RBT(props) {
-        return <a className="round ts26 p8"></a>;
+        return <a className="round tsxl ps"></a>;
 }
 
 function RBI(props) {
         const { onClick, src } = props;
-        return (<a className="round ts26 p8 c0" onClick={onClick}>
-                <img className="svl shl" src={atiles[src]} />
+        return (<a className="round ps c0" onClick={onClick}>
+                <img className="svl s_l" src={atiles[src]} />
         </a>);
 }
 
 function Directions() {
         const { sendMessage } = useContext(GameContext);
 
-        return (<div className="v0 tar tnow abs ar">
-                <div className="h0 f">
+        return (<div className="vn tar tnow abs al">
+                <div className="_n">
                         <RBT />
                         <RB onClick={() => sendMessage("k")}>↑</RB>
-                        <RBI onClick={() => sendMessage("K")} src={ACTIONS.K} />
+                        <RBI onClick={() => sendMessage("K")} src={ACT_K} />
                 </div>
-                <div className="h0 f">
+                <div className="_n">
                         <RB onClick={() => sendMessage("h")}>←</RB>
                         <RBT />
                         <RB onClick={() => sendMessage("l")}>→</RB>
                 </div>
-                <div className="h0 f">
+                <div className="_n">
                         <RBT />
                         <RB onClick={() => sendMessage("j")}>↓</RB>
-                        <RBI onClick={() => sendMessage("J")} src={ACTIONS.J} />
+                        <RBI onClick={() => sendMessage("J")} src={ACT_J} />
                 </div>
         </div>);
 }
@@ -668,38 +653,38 @@ function ContentsAndActions(props) {
                                 if (!(parseInt(item.actions) & (1 << p)))
                                         continue;
 
-                                let id = ACTIONS_LABEL[p];
+                                let id = actions_lbl[p];
                                 newActions.push([p, function () {
                                         sendMessage(id + " #" + item.dbref);
                                 }]);
                         }
                 } else if (item.loc == me) {
-                        // action_add(ACTIONS.PUT, function () {
+                        // action_add(ACT_PUT, function () {
                         //         output("\nPUT is not implemented yet!");
                         // });
 
-                        newActions.push([ACTIONS.EQUIP, function () {
+                        newActions.push([ACT_EQUIP, function () {
                                 sendMessage("equip #" + item.dbref);
                         }]);
 
-                        newActions.push([ACTIONS.DROP, function () {
+                        newActions.push([ACT_DROP, function () {
                                 sendMessage("drop #" + item.dbref);
                         }]);
 
-                        newActions.push([ACTIONS.EAT, function () {
+                        newActions.push([ACT_EAT, function () {
                                 sendMessage("eat #" + item.dbref);
                         }]);
 
-                        newActions.push([ACTIONS.SHOP, function () {
+                        newActions.push([ACT_SHOP, function () {
                                 sendMessage("sell #" + item.dbref);
                         }]);
 
                 } else if (objects[item.loc].shop) {
-                        newActions.push([ACTIONS.SHOP, function () {
+                        newActions.push([ACT_SHOP, function () {
                                 sendMessage("buy #" + item.dbref);
                         }]);
                 } else {
-                        newActions.push([ACTIONS.GET, function () {
+                        newActions.push([ACT_GET, function () {
                                 sendMessage("get #" + target + "=#" + item.dbref);
                         }]);
                 }
@@ -715,7 +700,7 @@ function ContentsAndActions(props) {
         return (<>
                 <Contents onItemClick={onItemClick} activeItem={activeItem} />
 
-                <div className="h0 icec">
+                <div className="_n icec">
                         { actionsEl }
                 </div>
         </>);
@@ -776,7 +761,7 @@ function PlayerBars() {
 
         const { hp, hpMax, mp, mpMax } = bars;
 
-        return (<div className="h8 f p8">
+        return (<div className="_s f ps">
                 <Bar value={hp} max={hpMax} color="1" />
                 <Bar value={mp} max={mpMax} color="12" />
         </div>);
@@ -865,15 +850,15 @@ function Game() {
         }
 
         return (<>
-                <span className="v0 f">
+                <span className="vn f">
                         {/* <RB onClick={disconnect}>X</RB> */}
                         <RB onClick={toggle_help}>?</RB>
-                        <RBI onClick={() => sendMessage('inventory')} src={ACTIONS.OPEN} />
-                        <RBI onClick={() => sendMessage('look')} src={ACTIONS.LOOK} />
+                        <RBI onClick={() => sendMessage('inventory')} src={ACT_OPEN} />
+                        <RBI onClick={() => sendMessage('look')} src={ACT_LOOK} />
                 </span>
 
-                <form className="v0 f fg shf oa" onSubmit={onSubmit}>
-                        <div className="h0 f">
+                <form className="vn f fg s_f oa" onSubmit={onSubmit}>
+                        <div className="_n f">
                                 <PlayerTabs />
                                 <RoomTitleAndArt />
                         </div>
@@ -886,7 +871,7 @@ function Game() {
                                 autoComplete="off" autoCapitalize="off" />
                 </form>
 
-                <div className="v0 f fg sh33">
+                <div className="vn f fg s_33">
                         <MiniMap />
                         <TargetTitleAndArt />
                         <Directions />
@@ -901,7 +886,7 @@ function getCookie(cname) {
 	let name = cname + "=";
 	let decodedCookie = decodeURIComponent(document.cookie);
 	let ca = decodedCookie.split(';');
-	for(let i = 0; i < ca.length; i++) {
+	for(let i = 0; i <ca.length; i++) {
 		let c = ca[i];
 		while (c.charAt(0) == ' ') {
 			c = c.substring(1);
@@ -914,15 +899,20 @@ function getCookie(cname) {
 }
 
 function InnerApp() {
-        // const { sendMessage } = useContext(GameContext);
+        const { open, sendMessage } = useContext(GameContext);
+
+	useEffect(() => {
+		if (open)
+			sendMessage("auth " + getCookie("QSESSION"));
+	}, [open]);
 
         return (<Game />);
 }
 
-
-export default
-hot(function App() {
+function App() {
         return (<GameContextProvider>
                 <InnerApp />
         </GameContextProvider>);
-});
+}
+
+ReactDOM.render(<App />, document.getElementById('main'));
diff --git a/js/mcp.js b/js/mcp.js
index 920e0b8..97a9f6a 100644
--- a/js/mcp.js
+++ b/js/mcp.js
@@ -1,6 +1,6 @@
-function GET_FLAG(x) { return mcp.flags & x; }
-function SET_FLAGS(x) { mcp.flags |= x; }
-function UNSET_FLAGS(x) { mcp.flags &= ~(x); }
+#define GET_FLAG(x)	( mcp.flags & x )
+#define SET_FLAGS(x)	{ mcp.flags |= x ; }
+#define UNSET_FLAGS(x)	( mcp.flags &= ~(x) )
 
 let out_buf = "";
 
@@ -36,6 +36,7 @@ function mcp_clear() {
         mcp.args = [];
         mcp.name = mcp.cache = "";
         mcp.args_l = 0;
+        mcp_cache_i = 0;
         mcp.flags = 0;
         mcp.state = 1;
 }
@@ -225,14 +226,14 @@ function mcp_proc(data) {
 	return mcp_arr;
 }
 
+function mcp_reset() {
+        last_i = out_i = 0;
+        // out_buf += "[";
+        out_i ++;
+}
+
 function mcp_init() {
         mcp.state = 1;
+        mcp_reset();
         mcp_clear();
 }
-
-const mcp_export = {
-  init: mcp_init,
-  proc: mcp_proc,
-};
-
-export default mcp_export;
diff --git a/js/tty.js b/js/tty.js
index 619bef4..7abb5ee 100644
--- a/js/tty.js
+++ b/js/tty.js
@@ -82,7 +82,7 @@ esc_state_0(tty, ch) {
                 return 2;
 	// case '\\':
 	// case '/':
-                // tty.output += "\\";
+                tty.output += "\\";
 	}
 
         tty.output += ch;
@@ -187,5 +187,3 @@ function tty_proc(input) {
 
         return tty.output;
 }
-
-export default tty_proc;
diff --git a/package.json b/package.json
index 59c6ff9..59cc50c 100644
--- a/package.json
+++ b/package.json
@@ -1,24 +1,20 @@
 {
-  "name": "nd",
-  "version": "0.0.1",
+  "name": "neverdark",
+  "version": "1.0.0",
   "description": "This is TinyMUCK 2.2fb6.00 (FuzzBall 6.00), a programmable, player expandable, multi-user adventure game.  Please see the CHANGES file for a list of changes from the previous version.",
   "main": "main.js",
   "directories": {
     "doc": "docs"
   },
   "scripts": {
-    "test": "scripts test",
-    "start": "scripts start",
-    "build": "scripts build",
-    "watch": "scripts watch",
-    "lint": "scripts lint"
+    "test": "echo \"Error: no test specified\" && exit 1"
   },
   "repository": {
     "type": "git",
     "url": "git+https://github.com/tty-pt/neverdark.git"
   },
-  "author": "quirinpa",
-  "license": "BSD-2-Clause",
+  "author": "",
+  "license": "ISC",
   "bugs": {
     "url": "https://github.com/tty-pt/neverdark/issues"
   },
@@ -26,21 +22,6 @@
   "devDependencies": {
     "@babel/cli": "^7.16.0",
     "@babel/core": "^7.16.0",
-    "@babel/preset-react": "^7.16.0",
-    "@hot-loader/react-dom": "^17.0.2",
-    "@tty-pt/scripts": "^0.0.1-23"
-  },
-  "dependencies": {
-    "process": "^0.11.10",
-    "react": "^17.0.2",
-    "react-dom": "^17.0.2",
-    "xterm": "^5.1.0",
-    "xterm-addon-fit": "^0.7.0"
-  },
-  "keywords": [
-    "neverdark"
-  ],
-  "serve": [
-    "art"
-  ]
+    "@babel/preset-react": "^7.16.0"
+  }
 }
diff --git a/pre-index.html b/pre-index.html
index 3c14d9f..d6feec0 100644
--- a/pre-index.html
+++ b/pre-index.html
@@ -1,4 +1,3 @@
-<!DOCTYPE html>
 <html lang="en">
 	<head>
 		<meta charset="utf8"/>
@@ -6,16 +5,22 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no viewport-fit=cover">
 		<meta name="mobile-web-app-capable" content="yes">
 		<meta name="apple-mobile-web-app-capable" content="yes">
-		<link rel="manifest" href="$CONFIG_BASEDIR/manifest.json">
-		<link rel="preload" href="$CONFIG_BASEDIR/art/tilemap.png" as="image" />
-		<link rel="shortcut icon" type="image/png" href="$CONFIG_BASEDIR/nd256.png"/>
-		<script defer src="$CONFIG_BASEDIR/build/main.js"></script>
+		<link rel="manifest" href="/neverdark/manifest.json">
+		<link rel="stylesheet"  href="/neverdark/vim.css" />
+		<link rel="stylesheet" href="/neverdark/styles.css" />
+		<link rel="preload" href="/neverdark/art/tilemap.png" as="image" />
+		<!-- <script src="./lib/text.min.js"></script> -->
+		<link rel="shortcut icon" type="image/png" href="/neverdark/nd256.png"/>
+                <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
+                <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
+                <script crossorigin src="https://unpkg.com/react-router-dom@5.1.2/umd/react-router-dom.js"></script>
 	</head>
-	<body class="cf cb svf v0 f">
-		<div id="main" class="svf h0 f fg oh">
+	<body class="cf cb sfv vn f">
+		<div id="main" class="_n f fg oh">
                         <canvas height=16 width=16></canvas>
 		</div>
-		<div class="modal abs svf v0 c f fcc oh dn">
+		<div class="modal abs sfv vn c f fcc oh dn">
 		</div>
+		<!-- DEPS -->
 	</body>
 </html>
diff --git a/reset.sh b/reset.sh
deleted file mode 100755
index 2d25a8c..0000000
--- a/reset.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-rm game/geo.db
-cp game/std.db.ok game/std.db
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 037d316..c3a3327 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -1,6 +1,5 @@
 srcdir	:= ../${srcdir}
 
-include ${srcdir}/.config
 -include Makefile
 
 uname != uname
@@ -11,13 +10,9 @@ ldflags-Darwin += -L/usr/local/opt/berkeley-db@4/lib
 cflags-Darwin += -I/usr/local/opt/openssl@3/include
 ldflags-Darwin += -L/usr/local/opt/openssl@3/lib
 
-ldlibs-Linux += -lbsd
-
-config	!= cat ${srcdir}/.config | sed -n '/^.*=y$$/p' | sed 's/^\(.*\)=y$$/-D\1/' | tr '\n' ' '
-cflags-y	+= ${config}
-cflags-y	+= -I/usr/local/include ${cflags-${uname}} -I${srcdir}/include -D_GNU_SOURCE -std=c99 -Wall -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-designator
+cflags-y	+= -I/usr/local/include ${cflags-${uname}} -I${srcdir}/include --pedantic -Wall -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-designator
 ldflags-y	+= -L/usr/local/lib ${ldflags-${uname}}
-ldlibs-y	+= ${ldlibs-${uname}}
+#ldlibs-y ommited
 
 LD := ${CC}
 CFLAGS		+= ${cflags-y}
@@ -32,21 +27,18 @@ all: ${exe}
 $(exe): ${real-obj-y} ${exe-obj-y}
 	${LINK.c} -o $@ ${real-obj-y} ${$@-obj-y} ${ldlibs-y}
 
-reldir-OpenBSD := ${PWD:${srcdir}/%=%}
-reldir-Darwin := ${reldir-OpenBSD}
-reldir-Linux := ${reldir-OpenBSD}/src
-RELDIR=${reldir-${uname}}
+RELDIR=${PWD:${srcdir}/%=%}
 
 clean:
 	@echo rm ${RELDIR}/{ ${exe} ${exe-obj-y} ${real-obj-y} ${deps}}
 	@rm ${exe} ${exe-obj-y} ${real-obj-y} ${deps} >/dev/null 2>&1 || true
 
 deps: # ${deps}
-	@test -z "${real-obj-y}" || \
+	@[[ -z "${real-obj-y}" ]] || \
 		mkdep ${CFLAGS} ${real-obj-y:%.o=%.c}
 
 .c.o:
 	@echo CC ${@:%=${RELDIR}/%}
-	cd ${srcdir} && ${COMPILE.c} -o ${@:%=${RELDIR}/%} ${<:%=${RELDIR}/%}
+	cd ${srcdir} && ${COMPILE.c} -o ${@:%=${RELDIR}/%} ${.IMPSRC:%=${RELDIR}/%}
 
 include Makefile.common
diff --git a/scripts/Makefile.common b/scripts/Makefile.common
index 1c4a2a8..90b2bbd 100644
--- a/scripts/Makefile.common
+++ b/scripts/Makefile.common
@@ -12,10 +12,10 @@ MFLAGS := srcdir=${srcdir} -I${srcdir}/scripts \
 	-f ${srcdir}/scripts/Makefile.build
 
 $(subdirs): FORCE
-	${MAKE} -C $@ ${MFLAGS} all
+	${MAKE} -C $@ ${MFLAGS}
 	cd ..
 
-subdirs-clean != test -z "${subdirs}" || echo ${subdirs:%=%-clean}
+subdirs-clean != [[ -z "${subdirs}" ]] || echo ${subdirs:%=%-clean}
 
 $(subdirs-clean): FORCE
 	${Q}${MAKE} -C ${@:%-clean=%} ${MFLAGS} clean
@@ -24,7 +24,7 @@ clean: ${subdirs-clean}
 
 .PHONY: clean
 
-subdirs-deps != test -z "${subdirs}" || echo ${subdirs:%=%-deps}
+subdirs-deps != [[ -z "${subdirs}" ]] || echo ${subdirs:%=%-deps}
 
 $(subdirs-deps): FORCE
 	${MAKE} -C ${@:-deps=} ${MFLAGS} deps
diff --git a/scripts/html_tool.sh b/scripts/html_tool.sh
index 61c6851..30f62e2 100755
--- a/scripts/html_tool.sh
+++ b/scripts/html_tool.sh
@@ -1,19 +1,10 @@
-#/bin/sh
-export $(xargs < .config)
-
 parse() {
         while read st cmd arg nd; do
-		if test "$st" = "<!--"; then
+		if [[ "$st" == "<!--" ]]; then
 			case $cmd in
 			CAT) cat $arg ;;
 			STYLE) echo "<style>" ; cat $arg ; echo "</style>" ;;
-			DEPS)
-				if test $# -ge 1; then
-					echo '<script>'
-					cat $@
-					echo '</script>'
-				fi
-				;;
+			DEPS) echo '<script>' ; cat $@ ; echo '</script>' ;;
 			esac
 		else
 			echo $st $cmd $arg $nd
@@ -24,4 +15,4 @@ parse() {
 file=$1
 shift
 
-cat $file | envsubst | parse $@
+cat $file | parse $@
diff --git a/src/==WARNING== b/src/==WARNING==
new file mode 100644
index 0000000..0a8c954
--- /dev/null
+++ b/src/==WARNING==
@@ -0,0 +1,19 @@
+Make sure that you do a backup of your database and MUF before upgrading
+to any new version of the server.  The server seems stable, when I
+release it, but that doesn't mean that it won't eat up all of your
+database, spit out garbage, and nuke everything.  It just means that I
+haven't SEEN it do that in my testing.  Occasionally I miss big errors,
+and your only protection, until I can get a fixed version out, is to have
+backups.  Make backups regularly, and keep one a few weeks old, in case
+an insidious error creeps in and corrupts your db over time.
+
+I can't say it enough... BACK UP YOUR DB.
+
+	- Foxen
+
+Additional note: Don't just back up your db file (the one that is usually
+passed as the parameter to -out to fbmuck) -- you must also back up the muf/
+directory, usually game/muf/*.  Otherwise, your database will be incomplete,
+because the actual MUF program text is stored outside of the normal database.
+
+	- Winged
diff --git a/src/Makefile b/src/Makefile
index bdbc16c..b95bed2 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,7 +11,7 @@ ldlibs-$(CONFIG_SSL) += -lssl -lcrypto
 cflags-y := -g -Wall -Wno-initializer-overrides
 
 exe := fbmuck prochelp
-src = ${real-obj-y:%.o=%.c}
+src = ChangeLog COPYING ${real-obj-y:%.o=%.c}
 
 prochelp-obj-y := prochelp.o
 fbmuck-obj-y := object.o entity.o spacetime.o mcp.o map.o \
@@ -32,13 +32,21 @@ fbmuck-obj-y += create.o \
 exe-obj-y := ${fbmuck-obj-y} ${prochelp-obj-y}
 
 HELPFILES := man.txt help.txt mpihelp.txt
-CTAGS ?= ctags
+
+version.o: version.h version.c
+
+version.h:
+	{ \
+		DATE=`date` ; git=${srcdir}/.git ; \
+		HASH=`git branch -v | head -n1 | awk '{ print $$3 }'` ; \
+		printf "#define HASH \"$$HASH\"\n#define DATE \"$$DATE\"\n" ; \
+	} > $@
 
 tags: ${src}
 	${CTAGS} --c-types=-m ${src} ../include/*.h
 
-# .depend: ${obj-y:%.o=%.c}
-# 	mkdep ${CFLAGS} -I../include ${obj-y:%.o=%.c}
+.depend: ${obj-y:%.o=%.c}
+	mkdep ${CFLAGS} -I../include ${obj-y:%.o=%.c}
 
 cleaner: clean
 	-rm config.status config.cache config.log ${INCLUDE}/autoconf.h ${exe} prochelp
diff --git a/src/TO.DO b/src/TO.DO
new file mode 100644
index 0000000..6e29b4f
--- /dev/null
+++ b/src/TO.DO
@@ -0,0 +1,245 @@
+To Do list
+----------
+
+Current Planned Tasks for fb6.00 (9/30/96):
+	[updated 06/12/2005 by Winged with STATUS info]
+
+    Add error trapping to MUF.  ie: ERRCATCH-ERRHANDLE-ERREND blocks.
+STATUS: Done TRY/CATCH/ENDCATCH blocks.
+    Save out changed objects to temp file on prop-loaded queue ejection.
+STATUS: ?What does this refer to?
+    Eliminate DBSAVE lockups by caching changes in memory, and saving in BG.
+STATUS: Worked around with the magic of fork()/vfork()
+    Add threadsafe DB_LOCKING
+STATUS: Not a chance in hell -- unless we use db4 or such.
+
+    Make free frames pool dynamic in size, based on use timestamp.
+STATUS: Unknown
+
+    Add MUF prim to return how a MUF program instace was instantiated.
+STATUS: We have the initial strings on the stack, but that's not
+		technically "safe".  Still needs work.
+    Work with Points and Fre'ta to improve inserver Vehicle handling.
+STATUS: What do they want to do with it?
+    Perhaps change MOVETO to do conlock checking?
+STATUS: Should be done.
+
+    Investigate inserver player command macros.
+STATUS: Not done.  Can be imitated with {force:me,"string\nstring\nstring"}.
+    Error msgs in MUF interpreter need cleanup.
+STATUS: Most seem to be better, but still need full audit.
+    Make Netmuck simpler to compile/install.  Especially for doing upgrades.
+STATUS: If we could figure out a consistent way to do so, it'd be
+	possible.  At least we have 'make install', which will install
+	the binaries and shared help files.
+    Investigate forbidding X flags on Wizbitted objects.
+STATUS: Obsoleted via BLESS.
+    Allow exiting from the MUF editor without saving.
+STATUS: Undone.
+    Allow some way to execute commands from inside the muf editor. ie: allow
+      poses, says, pages, etc from inside the editor.
+STATUS: MCP-GUI allows this, but the editor itself needs a lot of work.
+
+    Add DESCR* prims to replace/supplement CON* prims.
+STATUS: Done.
+    Add keyhole optimizer to MUF.
+STATUS: Done.
+    Add standardized ~sys/cmd/@teleport/osucc type mesgs for inserver cmds.
+STATUS: Not done.
+    Add lock checking for inserver commands.
+STATUS: Give me a list of inserver commands to add locks for, and I'll do it.
+
+    Add NEWPLAYER and TOADPLAYER primitives to MUF.
+STATUS: Done.  TOADPLAYER is a SCARY_MUF_PRIMITIVE.
+    Add {moveto} to MPI.
+STATUS: Not done.  Would this need blessed permissions?
+    Add {cutlist} primitive for MPI.  Returns all items BUT the given sublist.
+STATUS: Not done.  We need to integrate MUF and MPI array handling.
+
+    Document what command @ and stack are in 'man [dis]connect', etc.
+STATUS: Not done.
+    Document running order for @succ, etc & _arrive, _listen, _depart etc.
+STATUS: Not done.
+
+    Make MPI use allocated strings instead of BUFFER_LEN argument buffers.
+STATUS: Done.
+    Improve prop deletion schemes for protected props.
+STATUS: ?what does this refer to?
+    Add process listing/management primitives.
+STATUS: We have KILL, and WAITFOR_PID, but still not any good arrays.
+    Clean up the struct specific fields of objects.
+STATUS: Not done.  We also need to make sure that we're checking any
+		reference of those fields against NULL, to avoid crashes.
+
+    WAITPROP (d s -- s)  Waits for deletion or change of given property.
+STATUS: Not done, propose an event class of PROP.*
++   TRIGDESCR ( -- i) relates the descriptor that started this program.
+STATUS: Done. (DESCR prim)
+
+    Implement following MUF prims: Make @tunable format str for list formats.
+~	GETLISTRANGE (i i d s -- {s})
+~	SETLISTRANGE ({s} i d s -- )
+    CLEARLISTRANGE (i i d s -- )
+	MOVELISTRANGE (i i i d s -- )
+STATUS: ?what does this refer to?  Is it superseded by array primitives?
+
+    Add a way to let programs edit and compile programs.
+STATUS: Done.
+    Make Properties store privs and ownership.
+STATUS: Not done.  This would bloat the database, including essentially a
+		boolexp and dbref with every property.  As well, toading would
+		suddenly become a nightmare as all properties are loaded and
+		ownership checked.
+
++   Make MUF ARRAYs that are dynamically allocated.
+STATUS: Done, but not completely safely.
+
+
+
+
+{tell}s > prepending may be broken.  Look into it.  Tell Timur when fixed.
+STATUS: ?what does this refer to?
+
+Risseth (Yesterday at 8:45:43PM) -- I found something that I want fixed..  
+ Parseprop strips spaces from the beginning and end of a string AFTER parsing 
+ it..  It shouldn't, only before.
+STATUS: Not done (to my knowledge).
+
+Kimi okays. A way for _listen to know if an @message/@omessage triggered them,
+ first thing. This can be done in the command variable, she thinks. Also, a way
+ for _listen to know if it was another program's notify or notify_exclude that
+ triggered the _listen, hopefully being able to know WHICH program, as well, as
+ say/pose muf's would make the former useless.
+STATUS: Not done, but it should be possible.  It'll just take a bit of deep
+		hacking.
+
+Current:
+    Document the _sys/ properties somewhere.
+STATUS: Not done.
+-   Add .rsplit to $lib/strings
+STATUS: Unknown
+    Make Huh? sysparm message exec for MPI, with dbref 0 perms.
+STATUS: Not done.  I propose a change to this request: #0 perms, with a
+		secondary @tune to state whether to evaluate blessed or not.
+		Perhaps superseded, though, by HUH? M3 exit-in-environment
+		patch.
+
+    Change compiler to bytecode style.  Use fn ptrs.
+STATUS: Not Done.  Probably not needed.  Would like to change it
+		to be a lot more like MPI's layout.
+
++   Make server force ZOMBIES to look whenever it moves them.  All cases.
+STATUS: Unknown, but I think it's done.
++   Make dropto treat zombies like players for triggering.
+STATUS: Unknown -- I'm not sure what this refers to. (oh, wait... now I
+		know.  If an object is a THING, and there are no players, and
+		there's a dropto, all THINGs get swept to the dropto.  If the
+		THING is a Zombie, though, it should be treated like an interactive
+		player.)
+?   Make dropto not drop zombies.
+STATUS: As in, zombies are treated as players for purposes of determining
+		if they should be dropto'ed, and as long as they are there (and
+		set ZOMBIE) they prevent dropto from occurring?
+
+?   Document the CONTROLS and SYSPARM muf primitives.
+STATUS: Unknown.
++   Write mpi-intro docs. (mostly done)
+STATUS: 2/3rds Done.  We need advanced MPI docs.
+
+?   Make pose strip starting space for ',:;!?. or space.
+STATUS: Not sure, will check.  Not really needed, with MUF.
+
+    Make building quotas.
+STATUS: Not done.
+    Make _verb/ and _overb/ propqueues.
+STATUS: ?What does this refer to?  When a player spouts a verb that's
+		not recognized by the server, it runs those propqueues?
+    Implement control Zones.
+STATUS: Is this like Realms Control? -- or like the Group object type
+		that I proposed on fbmuck-devel?
+    Implement inserver 'look player=object' command, with apropriate lock.
+STATUS: Unknown.
+    Implement {case:} to act as case statement.
+STATUS: Not done.
+
+    Add PROPMATCH (dObj sPropdir sString -- sPropname) exitname style match
+STATUS: Not done.
+    Add NEXT_ENTRANCE (dLinkedto dLastobj -- dNextobj) to scan inlinks.
+STATUS: Backported from ProtoMUCK, I believe.
+    Add PCREATE (s s -- d)
+STATUS: Done.
+	Add TOADPLAYER (d d -- )
+STATUS: Done -- SCARY_MUF_PRIMITIVES
+	Add NEWPASSWD (d s s -- i) prims.
+STATUS: Unknown.
+    LOCKEDPROP?
+STATUS: Relies on bloated database, as referenced above.
+    PARSEMPISTR
+STATUS: Not done.  Not sure what the MPI perms should be -- trig's? prog's?
+		uid's?
+    Add SEED prim to set te random number seed.
+STATUS: Every frame has its own seed, so I think it's done.
+
+
+  
+KNOWN BUGS:
++   SETLINK should allow linking exits and rooms to #-3
+STATUS: I can see linking exits to #-3. Rooms linked to #-3 would just send
+		their dropped stuff to the object's home?
+?   Make recursion limit in boolexp parsing.  ie no (((...((((( to crash with.
+STATUS: Unknown
++   Make internal MUF addresses use dbref+index instead of memory pointer.
+STATUS: Undone.
+
+
+PROBLEMS:
+?   Need to make FORCE prim log forces to status file.
+STATUS: Done.
+    Make it impossible to trigger an _listen in a prog that was itself
+    triggered by an _listen propqueue.
+STATUS: Not done, but should be fairly easy.
+?   M1, M2, M3, &c should be be flag aliases for 1, 2, 3, &c.
+STATUS: Not done, but is just part of command-line parsing.
+    Have Timequeues store command @ variable value.
+STATUS: Unknown.
+    Need a CON_PRIMS_MLEVEL @tune that sets the MLevel CON* and ONLINE run at.
+STATUS: Not done.  Would also need to apply to DESCR*.
+    Finish the documentation, especially MUF.manual and MUF-tutorial.
+STATUS: Not done.
+
+
+PRIMITIVES:
+    MATCH_EXIT (s -- d)
+STATUS: Why MATCH_EXIT, when we could have MATCH_TYPE?
+    RMATCH_EXIT (d s -- d)
+STATUS: Why RMATCH_EXIT, when we could have RMATCH_TYPE?
+    OMATCH (d s -- d) match from remote object's POV.  Use player for me&here.
+STATUS: Wouldn't this be RMATCH, following the above naming scheme?
+
+
+ENHANCEMENTS:
+    Let smatch() return the unknown parameters.
+STATUS: Not done.  Would be similar to what \1, \2, etc is for regsub.
+-   Add in Anon funcs. : Fn "cat" "dog" "bat" 3 ': strcmp > ; .sort ;
+STATUS: Not done.
+-   Make ticked macros and prims compile to anony funcs.
+STATUS: Not done.
+
+
+COMMANDS:
+    @unlink <obj>[=<newdest>]  to unlink and relink in one.
+STATUS: Not done.  Need to re-examine, and especially look at metalinks.
+
+
+CODE CLEANUP:
+    Make system only props that are protected.
+STATUS: @sys/* exist, but can still be overwritten by wizards.
+	Move passwords into sysprops.
+STATUS: Not done.  (Pennies are in sysprops, though.)
+    Make properties use skiplists. (?)
+STATUS: Not done.  Not needed.
+    Make PUBLIC declarations use faster structure.
+STATUS: Not done.
+
+
+
diff --git a/src/TODO b/src/TODO
new file mode 100644
index 0000000..2e6feaa
--- /dev/null
+++ b/src/TODO
@@ -0,0 +1,55 @@
+These items are mandatory for FB6 release:
+  Update documentation.  (Especially the muf prim index.)
+
+
+These items are deferred:
+  Fix compression bugs, or implement new compression schema.
+  Add memory usage based limits on MUF progs.
+
+  Separate MPI perms away from Builder bit a la ProtoMUCK.
+  Add Per-User CPU usage throttles.
+
+  ISO-Latin high-bit character support.
+  SWITCH/CASE compiler support within MUF.
+  Secure communications layer.
+  Hashed passwords in database.  If this is implemented, there _must_ exist a
+    command line option to reset #1's password.
+  MUF ability to call in-server commands acting as player (ie: WHO, or @dig).
+  Integrate time() speedups.
+
+  ARRAY_FILTER_LOCK ([d] l -- [d']) As per ARRAY_FILTER_PROP, except only
+    those dbrefs that would pass the given lock are returned.
+ 
+  CORD_OPEN  (          intDescr strType -- strCordID)
+  CORD_SEND  (strCordID strName dictData -- )
+  CORD_CLOSE (                 strCordID -- )
+
+  Modify array code to allow floats and dbrefs as indexes.
+  WATCHPROP (d s -- ) Specifies that this process wants to recieve an event
+    when the given property on the given object is set, deleted, or changed.
+    The event received will have an ID of "PROPCHANGE.#dbref.propname" where
+    the dbref and the property name will be replaced by those of the property
+    you specified.
+
+
+
+These items may be considered to be a wishlist:
+  In-server gagging based on lock-style structures.  This would ensure that
+    any text that was generated by something or someone which 'passed' the
+    given lock string, would never be sent to the user.
+  Player Point-of-View forwarding.  This would allow a program or a player to
+    see _exactly_ what a given player is seeing, if the source player allows
+    it via the use of a lock and a _listen - styled proplist _and_ a matching
+    'receive' lock on the target (in the case of players/objects/rooms).
+  TCP/IP socket communications layer.
+  Matrix math.
+  3D manipulation primitives.
+
+SYSPARM_NAMES ( -- list:parmnames ) returns a simple list of sysparm names.
+SYSPARM_INFO ( str:name -- dict:info ) returns the infor (type, etc) for a
+  single named sysparm.
+SYSPARMS_ARRAY ( -- dict:parms ) to return a dictionary of just parmname-
+  parmval entries.  This would not return any sysparms that your program
+  hasn't the permissions for.
+SETSYSPARMS_ARRAY ( dict:parms -- ) sets all the sysparms in the given array.
+
diff --git a/src/TUNES.TXT b/src/TUNES.TXT
new file mode 100644
index 0000000..11ce6f4
--- /dev/null
+++ b/src/TUNES.TXT
@@ -0,0 +1,97 @@
+dumps (bool) dbdump_warning       = no
+dumps (bool) deltadump_warning    = no
+dumps (str)  dumpwarn_mesg        = ## Game will pause to save the database in a few minutes. ##
+dumps (str)  deltawarn_mesg       = ## Game will pause to save changed objects in a few minutes. ##
+dumps (str)  dumpdeltas_mesg      = ## Saving changed objects ##
+dumps (str)  dumping_mesg         = ## Pausing to save database. This may take a while. ##
+dumps (str)  dumpdone_mesg        = ## Save complete. ##
+dumps (time) dump_interval        =   0d  4:00:00
+dumps (time) dump_warntime        =   0d  0:02:00
+dumps (time) monolithic_interval  =   1d  0:00:00
+
+money (str)  penny                = penny
+money (str)  pennies              = pennies
+money (str)  cpenny               = Penny
+money (str)  cpennies             = Pennies
+money (int)  max_pennies          = 10000
+money (int)  start_pennies        = 50
+money (int)  penny_rate           = 8
+money (int)  max_object_endowment = 100
+
+rwho  (bool) support_rwho         = no
+rwho  (str)  rwho_passwd          = potrzebie
+rwho  (str)  rwho_server          = riemann.math.okstate.edu
+rwho  (time) rwho_interval        =   0d  0:04:00
+
+costs (int)  object_cost          = 10
+costs (int)  exit_cost            = 1
+costs (int)  link_cost            = 1
+costs (int)  room_cost            = 10
+costs (int)  lookup_cost          = 0
+costs (int)  kill_base_cost       = 100
+costs (int)  kill_min_cost        = 10
+costs (int)  kill_bonus           = 50
+
+cnctn (bool) idleboot             = yes
+cnctn (time) maxidle              =   0d  2:00:00
+cnctn (str)  idle_boot_mesg       = Auto-disconnecting for inactivity.
+cnctn (bool) playermax            = no
+cnctn (int)  playermax_limit      = 56
+cnctn (str)  playermax_warnmesg   = You likely won't be able to connect right now, since too many players are online.
+cnctn (str)  playermax_bootmesg   = Sorry, but there are too many players online.  Please try reconnecting in a few minutes.
+
+limit (int)  command_burst_size   = 500
+limit (int)  commands_per_time    = 20
+limit (int)  command_time_msec    = 1000
+limit (int)  max_delta_objs       = 20
+limit (int)  max_loaded_objs      = 5
+limit (int)  pause_min            = 100
+limit (int)  free_frames_pool     = 8
+limit (int)  mpi_max_commands     = 2048
+limit (time) clean_interval       =   0d  0:15:00
+
+muf   (int)  max_process_limit    = 400
+muf   (int)  max_plyr_processes   = 32
+muf   (int)  max_instr_count      = 20000
+muf   (int)  instr_slice          = 2000
+muf   (int)  listen_mlev          = 3
+muf   (bool) force_mlev1_name_notify = yes
+muf   (bool) allow_listeners      = yes
+muf   (bool) allow_listeners_obj  = yes
+muf   (bool) allow_listeners_env  = yes
+muf   (bool) look_propqueues      = no
+muf   (bool) periodic_program_purge = yes
+
+secur (bool) realms_control       = no
+secur (bool) use_hostnames        = yes
+secur (bool) log_commands         = yes
+secur (bool) log_reads            = no
+secur (bool) log_failed_commands  = no
+secur (bool) log_programs         = yes
+secur (bool) secure_who           = no
+
+muck  (str)  muckname             = TygryssMUCK2
+muck  (str)  huh_mesg             = Huh?  (Type "help" for help.)
+muck  (str)  leave_mesg           = See ya later, alligator!
+muck  (ref)  player_start         = Cave of Awakening(#64RD)
+muck  (bool) registration         = no
+muck  (str)  register_mesg        = Sorry, but you need to mail to foxen@netcom.com to get a character on this test muck.
+muck  (time) aging_time           =  90d  0:00:00
+muck  (str)  autolook_cmd         = look
+
+misc  (bool) who_doing            = yes
+misc  (bool) allow_zombies        = yes
+misc  (bool) wiz_vehicles         = no
+misc  (bool) restrict_kill        = yes
+misc  (bool) teleport_to_player   = yes
+misc  (bool) secure_teleport      = no
+misc  (bool) exit_darking         = yes
+misc  (bool) thing_darking        = yes
+misc  (bool) dark_sleepers        = no
+misc  (bool) who_hides_dark       = yes
+misc  (bool) enable_home          = yes
+misc  (bool) enable_prefix        = no
+misc  (bool) compatible_priorities = yes
+misc  (bool) do_mpi_parsing       = yes
+misc  (bool) lock_envcheck        = no
+
diff --git a/src/biome.c b/src/biome.c
index 4370372..4f32931 100644
--- a/src/biome.c
+++ b/src/biome.c
@@ -159,7 +159,7 @@ struct object_skeleton biomes[] = {
 	},
 	[BIOME_SAVANNAH] = {
 		.name = "savannah",
-                .description = "",
+                .description = "a place that is hot and dry",
                 .art = "savannah.jpeg",
                 .avatar = "",
                 .type = S_TYPE_BIOME,
diff --git a/src/create.c b/src/create.c
index 8e50813..9d131cf 100644
--- a/src/create.c
+++ b/src/create.c
@@ -1,10 +1,5 @@
 #include <string.h>
 #include <ctype.h>
-#include <stdlib.h>
-
-#ifndef __OpenBSD__
-#include <bsd/string.h>
-#endif
 
 #include "io.h"
 #include "entity.h"
diff --git a/src/entity.c b/src/entity.c
index 001ff7b..b5e139c 100644
--- a/src/entity.c
+++ b/src/entity.c
@@ -2,7 +2,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <stdlib.h>
 
 #include "io.h"
 #include "spacetime.h"
@@ -17,6 +16,7 @@
 #include "spell.h"
 #include "debug.h"
 #include "mob.h"
+#include "utils.h"
 
 #define HUNGER_Y	4
 #define THIRST_Y	2
@@ -54,6 +54,8 @@ birth(OBJ *player)
 	EFFECT(eplayer, DMG).value = DMG_BASE(eplayer);
 	EFFECT(eplayer, DODGE).value = DODGE_BASE(eplayer);
 
+	memset(eplayer->spells, 0, sizeof(eplayer->spells));
+
 	int i;
 
 	for (i = 0; i < ES_MAX; i++) {
@@ -79,7 +81,6 @@ recycle(OBJ *player, OBJ *thing)
 	OBJ *first, *rest;
 	int looplimit;
 
-	CBUG(!thing->owner);
 	OBJ *owner = thing->owner;
 	ENT *eowner = &owner->sp.entity;
 
@@ -221,21 +222,83 @@ entities_aggro(OBJ *player)
 	eplayer->klock += klock;
 }
 
+void
+enter_room(OBJ *player, enum exit e)
+{
+	ENT *eplayer = &player->sp.entity;
+	char contents[BIGBUFSIZ];
+	OBJ *oi;
+	int count = 0;
+
+	if (eplayer->gpt) {
+		free(eplayer->gpt);
+		eplayer->gpt = NULL;
+	}
+
+	sprintf(contents, "This is a fantasy story narrated by an artificial intelligence.\n");
+	sprintf(contents, "%sIt takes place in a '%s'",
+			contents, player->location->name);
+	if (player->location->description && *player->location->description)
+		sprintf(contents, "%s that is described as '%s' and ",
+			contents, player->location->description);
+	else
+		sprintf(contents, "%s that ", contents);
+	sprintf(contents, "%shas the following contents:\n", contents);
+
+	FOR_LIST(oi, player->location->contents) {
+		count++;
+		switch (oi->type) {
+		case TYPE_ENTITY:
+			{
+				ENT *ei = &oi->sp.entity;
+				if (ei->flags & EF_PLAYER) {
+					sprintf(contents, "%sA player named %s", contents, oi->name);
+					if (e != E_ALL) {
+						sprintf(contents, "%s, who just ", contents);
+						if (e == E_NULL)
+							sprintf(contents, "%steleported in", contents);
+						else
+							sprintf(contents, "%scame in from the %s", contents, e_name(e_simm(e)));
+					}
+				} else
+					sprintf(contents, "%sA %s", contents, oi->name);
+
+				if (player->description)
+					sprintf(contents, "%s, described as '%s'", contents, oi->name);
+				if (eplayer->flags & EF_SITTING)
+					sprintf(contents, "%s, who is sitting down", contents);
+
+				sprintf(contents, "%s.\n", contents);
+			}
+			break;
+		case TYPE_PLANT:
+			sprintf(contents, "%sA plant or tree named %s.\n", contents, oi->name);
+			break;
+		default:
+			sprintf(contents, "%sA %s.\n", contents, oi->name);
+		}
+	}
+
+	/* sprintf(contents, "%s\n" */
+	/* 		"Narrate this information as if in a fantasy novel.\n" */
+	/* 		, contents); */
+	sprintf(contents, "%s\n"
+			"Describe this.\n"
+			, contents);
+
+	entity_gpt(player, 1, contents);
+}
+
 void
 enter(OBJ *player, OBJ *loc, enum exit e)
 {
 	OBJ *old = player->location;
 
-	if (e == E_NULL)
-		onotifyf(player, "%s teleports out.", player->name);
-	else
-		onotifyf(player, "%s goes %s.", player->name, e_name(e));
+	onotifyf(player, "%s has left.", player->name);
 	object_move(player, loc);
+	enter_room(player, e);
 	room_clean(player, old);
-	if (e == E_NULL)
-		onotifyf(player, "%s teleports in.", player->name);
-	else
-		onotifyf(player, "%s comes in from the %s.", player->name, e_name(e_simm(e)));
+	onotifyf(player, "%s has arrived.", player->name);
 	entities_aggro(player);
 	look_around(player);
 }
@@ -462,7 +525,8 @@ look_room(OBJ *player, OBJ *loc)
 	if (mcp_look(player, loc)) {
 		notify(eplayer, unparse(player, loc));
 		notify(eplayer, description);
-		look_contents(player, loc, "You see:");
+		if (!eplayer->gpt)
+			look_contents(player, loc, "You see:");
 	}
 }
 
@@ -589,9 +653,6 @@ static inline unsigned
 entity_xp(ENT *eplayer, ENT *etarget)
 {
 	// alternatively (2000/x)*y/x
-	if (!eplayer->lvl)
-		return 0;
-
 	unsigned r = 254 * etarget->lvl / (eplayer->lvl * eplayer->lvl);
 	if (r < 0)
 		return 0;
@@ -632,9 +693,9 @@ entity_body(OBJ *player, OBJ *mob)
 	unsigned n = 0;
 
 	for (; (tmp = mob->contents); ) {
-		/* CBUG(tmp->type != TYPE_GARBAGE); */
-		if (tmp->type == TYPE_GARBAGE)
-			continue;
+		CBUG(tmp->type != TYPE_GARBAGE);
+		/* if (object_get(tmp)->type == TYPE_GARBAGE) */
+		/* 	continue; */
 		if (tmp->type == TYPE_EQUIPMENT) {
 			EQU *etmp = &tmp->sp.equipment;
 			unequip(mob, EQL(etmp->eqw));
@@ -1147,7 +1208,48 @@ do_reroll(command_t *cmd)
 		eplayer->attr[i] = d20();
 	}
 
-	EFFECT(eplayer, DMG).value = DMG_BASE(eplayer);
-	EFFECT(eplayer, DODGE).value = DODGE_BASE(eplayer);
 	mcp_stats(player);
 }
+
+void
+entity_gpt(OBJ *player, int echo_off, char *add_prompt)
+{
+	ENT *eplayer = &player->sp.entity;
+	int len = strlen(add_prompt);
+	char *has_previous = eplayer->gpt;
+	char *pgpt = has_previous ? eplayer->gpt : "";
+	int glen = strlen(pgpt);
+	/* char *prompt = malloc(glen + len + 3); */
+	char *prompt = malloc(glen + len + 1);
+	strcpy(prompt, pgpt);
+	strcat(prompt, add_prompt);
+	/* strcat(prompt, "\n\n"); */
+	warn("GPT prompt:\n%s\n", prompt);
+	char *result = gpt(prompt);
+	warn("GPT result:\n%s\n", result);
+	int rlen = strlen(result);
+	/* eplayer->gpt = realloc(has_previous, glen + (echo_off ? 0 : len) + rlen + 3); */
+	eplayer->gpt = realloc(has_previous, glen + len + rlen + 1);
+	if (!has_previous)
+		*eplayer->gpt = '\0';
+	/* strcpy(eplayer->gpt, pgpt); */
+	strcat(eplayer->gpt, add_prompt);
+	strcat(eplayer->gpt, result);
+	warn("Updated GPT:\n%s\n", eplayer->gpt);
+	/* if (has_previous && has_previous != eplayer->gpt) */
+	/* 	free((void *) has_previous); */
+	notifyf(eplayer, "%s", eplayer->gpt + glen + (echo_off ? len : 0));
+}
+
+void
+entity_gptcat(OBJ *player, char *str)
+{
+	ENT *eplayer = &player->sp.entity;
+	char *has_previous = eplayer->gpt;
+	char *pgpt = has_previous ? eplayer->gpt : "";
+	int glen = strlen(pgpt);
+	int len = strlen(str);
+	eplayer->gpt = realloc(has_previous, glen + len + 1);
+	strcpy(eplayer->gpt, pgpt);
+	strcat(eplayer->gpt, str);
+}
diff --git a/src/help.c b/src/help.c
index 979cefc..3678a87 100644
--- a/src/help.c
+++ b/src/help.c
@@ -1,13 +1,3 @@
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <string.h>
-#include <dirent.h>
-#include <stdlib.h>
-#ifndef __OpenBSD__
-#include <bsd/string.h>
-#endif
-
 #include "io.h"
 #include "entity.h"
 
@@ -16,6 +6,13 @@
 #include "params.h"
 #include "utils.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <dirent.h>
+
 #include "command.h"
 
 #define NLENGTH(dirent) (strlen((dirent)->d_name))
@@ -163,7 +160,7 @@ index_file(OBJ *player, const char *onwhat, const char *file)
 int
 show_subfile(OBJ *player, const char *dir, const char *topic, const char *seg, int partial)
 {
-	char buf[512];
+	char buf[256];
 	struct stat st;
 	DIR *df;
 	struct dirent *dp;
diff --git a/src/interface.c b/src/interface.c
index 05bd75b..0ff4461 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -4,12 +4,6 @@
 
 #include "io.h"
 
-#include <string.h>
-#ifndef __OpenBSD__
-#include <bsd/string.h>
-#endif
-
-
 #include "copyright.h"
 #include "object.h"
 #include "config.h"
@@ -42,9 +36,13 @@
 #include <sys/socket.h>
 #endif
 #include <pwd.h>
-#include <grp.h>
 
-#include "nddb.h"
+#ifdef __OpenBSD__
+#include <db4/db.h>
+#else
+#include <db.h>
+#endif
+
 #include "command.h"
 #include "params.h"
 #include "defaults.h"
@@ -86,36 +84,7 @@ SSL_CTX *sslctx;
 void do_bio(command_t *cmd);
 extern void do_auth(command_t *cmd);
 extern void do_httpget(command_t *cmd);
-
-char *
-command(char *prompt) {
-	static char buf[BUFFER_LEN];
-	struct popen2 child;
-	ssize_t len;
-	CBUG(popen2(&child, prompt));
-	close(child.in);
-	len = read(child.out, buf, sizeof(buf));
-	if (len >= 2) {
-		buf[len - 2] = '\0';
-	}
-	close(child.out);
-	kill(child.pid, 0);
-	return buf;
-}
-
-void
-do_diff(command_t *cmd) {
-	// TODO ssh in chroot?
-	/* descr_inband(cmd->fd, command("git fetch origin master; git diff origin/master")); */
-
-	// the fetch is not needed I think if it is
-	// not updated we can do it manually or display
-	// the status or the commit. It adds a lot of overhead to
-	// fetch on every diff. Perhaps later, when the repo is
-	// local, fetch will make no sense at all
-	descr_inband(cmd->fd, command("git diff origin/master"));
-}
-
+extern void do_gpt(command_t *cmd);
 
 core_command_t cmds[] = {
 	{
@@ -126,6 +95,9 @@ core_command_t cmds[] = {
 		.name = "GET",
 		.cb = &do_httpget,
 		.flags = CF_NOAUTH | CF_NOARGS,
+	}, {
+		.name = "gpt",
+		.cb = &do_gpt,
 	}, {
 		.name = "advitam",
 		.cb = &do_advitam,
@@ -147,9 +119,6 @@ core_command_t cmds[] = {
 	}, {
 		.name = "create",
 		.cb = &do_create,
-	}, {
-		.name = "diff",
-		.cb = &do_diff,
 	}, {
 		.name = "heal",
 		.cb = &do_heal,
@@ -380,7 +349,7 @@ close_sockets(const char *msg) {
 
 void sig_shutdown(int i)
 {
-	warn("SHUTDOWN: via SIGNAL\n");
+	warn("SHUTDOWN: via SIGNAL");
 	shutdown_flag = 1;
 }
 
@@ -390,7 +359,7 @@ init_game()
 	FILE *f = fopen(STD_DB, "rb");
 
 	if (f == NULL) {
-                warn("No such file\n");
+                warn("No such file");
                 return -1;
         }
 
@@ -432,6 +401,9 @@ main(int argc, char **argv)
 		}
 	}
 
+	if (geteuid())
+		errx(1, "need root privileges");
+
 #ifdef CONFIG_SECURE
 	SSL_load_error_strings();
 	SSL_library_init();
@@ -443,18 +415,12 @@ main(int argc, char **argv)
 	ERR_print_errors_fp(stderr);
 #endif
 
+	warn("INIT: TinyMUCK %s starting.", "version");
+	warn("%s PID is: %d", argv[0], getpid());
 
-#ifdef CONFIG_ROOT
-	if (geteuid())
-		errx(1, "need root privileges");
-#endif
-
-#ifdef CONFIG_CHROOT
-	char *user = "www";
-	struct passwd *pw = getpwnam(user);
-
+	struct passwd *pw = getpwnam("www");
 	if (pw == NULL)
-		errx(1, "unknown user %s", user);
+		errx(1, "unknown user %s", "www");
 
 	if (chroot("/var/www") != 0 || chdir("/htdocs/neverdark/game") != 0)
 		err(1, "%s", "/var/www");
@@ -462,13 +428,9 @@ main(int argc, char **argv)
 	if (setgroups(1, &pw->pw_gid) ||
 			setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
 			setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
-		fprintf(stderr, "%s: cannot drop privileges\n", __func__);
+		fprintf(stderr, "%s: cannot drop privileges", __func__);
 		exit(1);
 	}
-#else
-	if (chdir("./game") != 0)
-		err(1, "No dir ./game");
-#endif
 
 	players_init();
 	hash_init(&cmdsdb);
@@ -641,13 +603,6 @@ do_auth(command_t *cmd)
 	OBJ *player;
 	descr_t *d = &descr_map[fd];
 
-
-	if (!*qsession) {
-		descr_inband(fd, "Please log in.");
-		mcp_auth_fail(fd, 3);
-		return;
-	}
-
 	snprintf(buf, sizeof(buf), "/sessions/%s", qsession);
 	fp = fopen(buf, "r");
 
@@ -667,7 +622,6 @@ do_auth(command_t *cmd)
 	if (!player) {
 		player = player_create(buf);
 		birth(player);
-		spells_birth(player);
 	} else if (player->sp.entity.fd > 0) {
                 descr_inband(fd, "That player is already connected.\r\n");
                 mcp_auth_fail(fd, 2);
@@ -684,9 +638,19 @@ do_auth(command_t *cmd)
         mcp_auth_success(player);
         mcp_equipment(player);
 	look_around(player);
+	enter_room(player, E_ALL);
 	do_view(cmd);
 }
 
+void
+do_gpt(command_t *cmd)
+{
+	OBJ *player = cmd->player;
+	ENT *eplayer = &player->sp.entity;
+	char *reply = gpt(cmd->argv[1]);
+	notifyf(eplayer, "%s", reply);
+}
+
 static inline void
 do_v(OBJ *player, char const *cmdstr)
 {
@@ -722,7 +686,7 @@ command_process(command_t *cmd)
 	OBJ *player = cmd->player;
 	ENT *eplayer = &player->sp.entity;
 
-	/* command_debug(cmd, "command_process"); */
+	command_debug(cmd, "command_process");
 
 	// set current descriptor (needed for death)
 	eplayer->fd = descr;
@@ -860,9 +824,8 @@ descr_close(descr_t *d)
 		eplayer->fd = -1;
 		d->flags = 0;
 		d->player = NULL;
-	} else {
+	} else
 		warn("%d never connected\n", d->fd);
-	}
 
 	shutdown(d->fd, 2);
 	close(d->fd);
@@ -944,8 +907,6 @@ shovechars()
 	start = get_now();
 	/* And here, we do the actual player-interaction loop */
 
-	warn("Done.\n");
-
 	while (shutdown_flag == 0) {
 		/* process_commands(); */
 		now = get_now();
@@ -969,9 +930,6 @@ shovechars()
 				return 0;
 			case EINTR:
 				continue;
-			case EBADF:
-				descr_close(d);
-				continue;
 			}
 
 			perror("select");
diff --git a/src/item.c b/src/item.c
index 4d5077a..1447df3 100644
--- a/src/item.c
+++ b/src/item.c
@@ -1,4 +1,3 @@
-#include <stdlib.h>
 #include "io.h"
 
 #include "entity.h"
diff --git a/src/kill.c b/src/kill.c
index a6b5679..b3fb565 100644
--- a/src/kill.c
+++ b/src/kill.c
@@ -1,8 +1,7 @@
-#include <stdlib.h>
-#include <ctype.h>
 #include "io.h"
 #include "entity.h"
 #include "debug.h"
+#include <ctype.h>
 #include "params.h"
 #include "match.h"
 #include "command.h"
diff --git a/src/map.c b/src/map.c
index 66c823f..dd8485b 100644
--- a/src/map.c
+++ b/src/map.c
@@ -1,7 +1,7 @@
 #include "map.h"
 
 #include <string.h>
-#include "nddb.h"
+#include <db4/db.h>
 #include "debug.h"
 
 // #define PRECOVERY
diff --git a/src/match.c b/src/match.c
index e3b6dfd..f5a3ca6 100644
--- a/src/match.c
+++ b/src/match.c
@@ -13,59 +13,6 @@
 #include "entity.h"
 #include "player.h"
 
-OBJ *
-ematch_me(OBJ *player, const char *str)
-{
-	if (!strcmp(str, "me"))
-		return player;
-	else
-		return NULL;
-}
-
-OBJ *
-ematch_here(OBJ *player, const char *str)
-{
-	if (!strcmp(str, "here"))
-		return player->location;
-	else
-		return NULL;
-}
-
-OBJ *
-ematch_mine(OBJ *player, const char *str)
-{
-	return ematch_at(player, player, str);
-}
-
-OBJ *
-ematch_near(OBJ *player, const char *str)
-{
-	return ematch_at(player, player->location, str);
-}
-
-/* all ematch
- * (not found by the linker if it is not static?)
- */
-OBJ *
-ematch_all(OBJ *player, const char *name)
-{
-	OBJ *res;
-
-	if (
-			(res = ematch_me(player, name))
-			|| (res = ematch_here(player, name))
-			|| (res = ematch_absolute(name))
-			|| (res = ematch_near(player, name))
-			|| (res = ematch_mine(player, name))
-			|| (res = ematch_player(name))
-	   )
-		return res;
-
-	else
-		return NULL;
-}
-
-
 /* TODO move to match.h as static inline */
 OBJ *
 ematch_player(const char *name)
diff --git a/src/mcp.c b/src/mcp.c
index 7f1443c..c5e2057 100644
--- a/src/mcp.c
+++ b/src/mcp.c
@@ -11,10 +11,6 @@
 #include "io.h"
 #include "command.h"
 
-#ifndef __OpenBSD__
-#include <bsd/string.h>
-#endif
-
 #define MCP_WEB_PKG "web"
 #define MCP_MESG_PREFIX		"#$#"
 #define MCP_ARG_EMPTY		"\"\""
diff --git a/src/netmuck.pid b/src/netmuck.pid
new file mode 100644
index 0000000..0d2a5c7
--- /dev/null
+++ b/src/netmuck.pid
@@ -0,0 +1 @@
+68388
diff --git a/src/noise.c b/src/noise.c
index 53ffc04..5296941 100644
--- a/src/noise.c
+++ b/src/noise.c
@@ -28,8 +28,6 @@
  * */
 
 #include "noise.h"
-#define NOISE_IMPLEMENTATION
-#include "nd/noise.h"
 #include <string.h>
 #include "xxhash.h"
 #include "debug.h"
@@ -40,16 +38,22 @@
 #define CHUNK_SIZE (1 << CHUNK_Y)
 #define CHUNK_M (CHUNK_SIZE * CHUNK_SIZE)
 
+/* gets a mask for use with get_s */
+#define COORDMASK_LSHIFT(x) (ucoord_t) ((((ucoord_t) -1) << x))
+
 #define PLANTS_SEED 5
 
 #define NOISE_OCT(to, x, seed) \
-	noise_oct(to, s, sizeof(x) / sizeof(octave_t), x, seed, CHUNK_Y, 2)
+	noise_oct(to, s, sizeof(x) / sizeof(octave_t), x, seed)
+
+typedef struct { unsigned x, w; } octave_t;
 
 struct tilemap {
 	struct rect r;
 	struct bio *bio;
 };
 
+static size_t v_total = (1<<(DIM+1)) - 1;
 static struct bio chunks_bio_raw[CHUNK_M * 4],
 		  chunks_bio[CHUNK_M * 4],
 		  *bio;
@@ -111,6 +115,249 @@ temp(ucoord_t obits, noise_t he, noise_t tm, coord_t pos_y) // fahrenheit * 10
 /* }}} */
 /* }}} */
 
+/* gets a (deterministic) random value for point p */
+
+static inline noise_t
+r(point_t p, unsigned seed, unsigned w)
+{
+	register noise_t v = HASH(p, sizeof(point_t), seed);
+	return ((long long unsigned) v) >> w;
+}
+
+/* generate values at quad vertices. */
+
+static inline void
+get_v(noise_t v[1 << DIM], point_t s, ucoord_t x, unsigned w, unsigned seed)
+{
+	const coord_t d = 1 << x;
+	const point_t va[] = {
+		{ s[0], s[1] },
+		{ s[0], s[1] + d },
+		{ s[0] + d, s[1] },
+		{ s[0] + d, s[1] + d }
+	};
+	int i;
+
+	for (i = 0; i < (1 << DIM); i++)
+		v[i] = r((coord_t *) va[i], seed, w);
+}
+
+static inline void
+calc_steps(snoise_t *st,
+	   noise_t *v,
+	   ucoord_t z,
+	   ucoord_t vl,
+	   unsigned y)
+{
+	int n;
+	for (n = 0; n < vl; n ++)
+		// FIXME FIND PARENS
+		st[n] = ((long) v[n + vl] - v[n]) >> z << y;
+}
+
+static inline void
+step(noise_t *v, snoise_t *st, ucoord_t vl, ucoord_t mul)
+{
+	int n;
+	for (n = 0; n < vl; n++)
+		v[n] = (long) v[n] + ((long) st[n] * mul);
+}
+
+/* Given a set of vertices,
+ * fade between them and set target values acoordingly
+ */
+
+static inline void
+noise_quad(noise_t *c, noise_t *vc, unsigned z, unsigned w)
+{
+	ucoord_t ndim = DIM - 1;
+	ucoord_t tvl = 1<<ndim; // length of input values
+	snoise_t st[(tvl<<1) - 1], *stc = st;
+	noise_t *ce_p[DIM], *vt;
+	size_t cd = 1 << CHUNK_Y * ndim; /* (2^y)^ndim */
+
+	goto start;
+
+	do {
+		do {
+			// PUSH
+			cd >>= CHUNK_Y; ndim--; vc = vt; stc += tvl; tvl >>= 1;
+
+start:			ce_p[ndim] = c + (cd<<z);
+			vt = vc + (tvl<<1);
+
+			calc_steps(stc, vc, z, tvl, 0);
+			memcpy(vt, vc, tvl * sizeof(noise_t));
+		} while (ndim);
+
+		for (; c < ce_p[0]; c+=cd, vt[0]+=*stc)
+			*c += vt[0];
+
+		do {
+			// POP
+			++ndim;
+			if (ndim >= DIM)
+				return;
+			c -= cd << z; cd <<= CHUNK_Y; vt = vc;
+			tvl <<= 1; stc -= tvl; vc -= (tvl<<1);
+
+			step(vt, stc, tvl, 1);
+			c += cd;
+		} while (c >= ce_p[ndim]);
+	} while (1);
+}
+
+#if 0
+
+static inline void
+_noise_mr(noise_t *c, noise_t *v, unsigned x, point_t qs, ucoord_t ndim, unsigned w, unsigned seed) {
+	int i = DIM - 1 - ndim;
+	ucoord_t ced = (1<<(CHUNK_Y*(ndim+1))), cd;
+	noise_t *ce = c + ced;
+
+	ced >>= CHUNK_Y;
+	cd = ced << x;
+
+	for (; c < ce; qs[i] += (1<<x), c += cd)
+		if (ndim == 0) {
+			get_v(v, qs, x, w, seed);
+			noise_quad(c, v, x, w);
+		} else
+			_noise_mr(c, v, x, qs, ndim - 1, w, seed);
+
+	qs[i] -= 1 << CHUNK_Y; // reset
+}
+
+static inline void
+_noise_m(noise_t *c, noise_t *v, unsigned x, point_t qs, unsigned w, unsigned seed)
+{
+	_noise_mr(c, v, x, qs, DIM - 1, w, seed);
+}
+
+#else
+
+static inline void
+_noise_m(noise_t *c, noise_t *v, unsigned x, point_t qs, unsigned w, unsigned seed) {
+	noise_t *ce_p[DIM];
+	noise_t ced = 1<<(CHUNK_Y * DIM);
+	coord_t *qsc = qs; // quad coordinate
+	ucoord_t ndim = DIM - 1;
+
+	goto start;
+
+	do {
+		do { // PUSH
+			qsc++; ndim--;
+start:			ce_p[ndim] = c + ced;
+			ced >>= CHUNK_Y;
+		} while (ndim);
+
+		do {
+			get_v(v, qs, x, w, seed);
+			noise_quad(c, v, x, w);
+			*qsc += 1<<x;
+			c += ced<<x;
+		} while (c < ce_p[ndim]);
+
+		do { // POP
+			*qsc -= 1 << CHUNK_Y;
+			ced <<= CHUNK_Y;
+			c += (ced<<x) - ced; // reset and inc
+			qsc--; ndim++;
+			*qsc += 1<<x;
+		} while (c >= ce_p[ndim]);
+	} while (ndim < DIM);
+}
+
+#endif
+
+/* fixes v (vertex values)
+ * when noise quad starts before matrix quad aka x > y aka d > l.
+ * assumes ndim to be at least 1
+ * vl = 1 << ndim
+ */
+
+static inline void
+__fix_v(noise_t *v, snoise_t *st, coord_t *ms, coord_t *qs, unsigned x, ucoord_t vl) {
+	register noise_t *vn = v + vl;
+	register ucoord_t dd = (*ms - *qs) >> CHUNK_Y;
+
+	// TODO make this more efficient
+	calc_steps(st, v, x, vl, CHUNK_Y);
+
+	if (dd) /* ms > qs */
+		step(v, st, vl, dd);
+
+	memcpy(vn, v, sizeof(noise_t) * vl); // COPY to opposite values
+	step(vn, st, vl, 1); // STEP by side length of M
+}
+
+// yes this does work fine a bit of a hack but ok i guess
+static inline void
+fix_v(noise_t *v,
+       coord_t *qs,
+       coord_t *ms,
+       unsigned x)
+{
+	ucoord_t vl = 1 << DIM;
+	snoise_t st[vl - 1], *stc = st;
+	int ndim = DIM - 1;
+	int first = 1;
+	vl >>= 1;
+
+	for (;;) {
+		__fix_v(v, stc, ms, qs, x, vl);
+
+		if (ndim) {
+			ndim--; qs++; ms++; vl>>=1; stc+=vl; // PUSH
+		} else if (first) {
+			v += vl<<1;
+			first = 0;
+		} else break;
+	}
+}
+
+static inline void
+get_s(point_t s, point_t p, ucoord_t mask) {
+	POOP s[I] = ((coord_t) p[I]) & mask;
+}
+
+/* Fills in matrix "mat" with deterministic noise.
+ * "ms" is the matrix's min point in the world;
+ * "x" 2^x = side length of noise quads;
+ * "y" 2^y = side length of matrix "mat".
+ * */
+
+static inline void
+noise_m(noise_t *mat, point_t ms, unsigned x,
+	unsigned w, unsigned seed)
+{
+	noise_t v[v_total];
+
+	if (CHUNK_Y > x)
+		_noise_m(mat, v, x, ms, w, seed);
+	else {
+		point_t qs;
+
+		get_s(qs, ms, COORDMASK_LSHIFT(x));
+		get_v(v, qs, x, w, seed);
+		if (x > CHUNK_Y)
+			fix_v(v, qs, ms, x);
+
+		noise_quad(mat, v, CHUNK_Y, w); }
+}
+
+static inline void
+noise_oct(noise_t *m, point_t s, size_t oct_n, octave_t *oct, unsigned seed)
+{
+	octave_t *oe;
+
+	memset(m, 0, sizeof(noise_t) * CHUNK_M);
+
+	for (oe = oct + oct_n; oct < oe; oct++)
+		noise_m(m, s, oct->x, oct->w, seed);
+}
+
 static inline unsigned
 bio_idx(ucoord_t rn, coord_t tmp)
 {
@@ -224,7 +471,7 @@ noise_chunks(point_t pos)
 		pos[1] - VIEW_AROUND
 	};
 
-	noise_get_s(r.s, vprs, COORDMASK_LSHIFT(CHUNK_Y), 2);
+	get_s(r.s, vprs, COORDMASK_LSHIFT(CHUNK_Y));
 
 	POOP {
 		n[I] = ((VIEW_SIZE + vprs[I] - r.s[I]) >> CHUNK_Y) + 1;
diff --git a/src/object.c b/src/object.c
index 8f9d713..4e450cb 100644
--- a/src/object.c
+++ b/src/object.c
@@ -102,7 +102,6 @@ object_clear(OBJ *o)
 	o->name = 0;
 	o->first_observer = NULL;
 	o->contents = o->location = o->next = NULL;
-	o->owner = object_get(GOD);
 }
 
 OBJ *
@@ -186,7 +185,6 @@ object_add(SKEL *sk, OBJ *where)
 			enu->flags = sk->sp.entity.flags;
 			enu->wtso = sk->sp.entity.wt;
 			birth(nu);
-			spells_birth(nu);
 			object_drop(nu, sk->sp.entity.drop);
 			enu->home = where;
 		}
@@ -243,8 +241,6 @@ object_write(FILE * f, OBJ *obj)
 {
 	int j;
 	putstring(f, obj->name);
-	putstring(f, obj->art);
-	putstring(f, obj->avatar);
 	putref(f, object_ref(obj->location));
 	putref(f, object_ref(obj->contents));
 	putref(f, object_ref(obj->next));
@@ -310,7 +306,6 @@ object_write(FILE * f, OBJ *obj)
 			putref(f, object_ref(roo->dropto));
 			putref(f, roo->flags);
 			putref(f, roo->exits);
-			putref(f, roo->doors);
 			putref(f, roo->floor);
 			putref(f, object_ref(obj->owner));
 		}
@@ -417,9 +412,8 @@ objects_free(void)
 dbref
 ref_read(FILE * f)
 {
-	char buf[BUFSIZ];
-	CBUG(!fgets(buf, sizeof(buf), f));
-	warn("ref_read %s", buf);
+	static char buf[BUFSIZ];
+	fgets(buf, sizeof(buf), f);
 	return (atol(buf));
 }
 
@@ -463,8 +457,6 @@ object_read(FILE * f)
 	objects_grow(ref + 1);
 
 	o->name = STRING_READ(f);
-	o->art = STRING_READ(f);
-	o->avatar = STRING_READ(f);
 	o->location = object_get(ref_read(f));
 	o->contents = object_get(ref_read(f));
 	o->next = object_get(ref_read(f));
@@ -518,16 +510,15 @@ object_read(FILE * f)
 			ro->doors = ref_read(f);
 			ro->floor = ref_read(f);
 			o->owner = object_get(ref_read(f));
-			warn("ROOM\n");
 		}
 		return;
 	case TYPE_ENTITY:
 		{
 			ENT *eo = &o->sp.entity;
 			eo->home = object_get(ref_read(f));
-			/* warn("entity home\n"); */
 			eo->fd = -1;
 			eo->last_observed = NULL;
+			eo->gpt = NULL;
 			eo->flags = ref_read(f);
 			eo->lvl = ref_read(f);
 			eo->cxp = ref_read(f);
@@ -538,19 +529,16 @@ object_read(FILE * f)
 			eo->sat = object_get(ref_read(f));
 			for (j = 0; j < 8; j++) {
 				struct spell *sp = &eo->spells[j];
-				int ref = ref_read(f);
-				struct spell_skeleton *_sp = SPELL_SKELETON(ref);
-				/* warn("entity! flags %d ref %d sp %p\n", eo->flags, ref); */
+				struct spell_skeleton *_sp = SPELL_SKELETON(ref_read(f));
 				sp->_sp = _sp;
 				sp->val = SPELL_DMG(eo, _sp);
 				sp->cost = SPELL_COST(sp->val, _sp->y, _sp->flags & AF_BUF);
 			}
-			/* warn("entity! flags %d ref %d\n", eo->flags, ref_read(f)); */
 			o->owner = o;
 			if (eo->flags & EF_PLAYER)
 				player_put(o);
 			birth(o);
-			warn("ENTITY\n");
+			warn("entity! flags %d sat %d\n", eo->flags, object_ref(eo->sat));
 		}
 		break;
 	case TYPE_GARBAGE:
diff --git a/src/player.c b/src/player.c
index 471ae28..df265f1 100644
--- a/src/player.c
+++ b/src/player.c
@@ -1,5 +1,3 @@
-#include <string.h>
-
 #include "player.h"
 #include "io.h"
 #include "entity.h"
@@ -8,6 +6,24 @@
 
 DB *playerdb = NULL;
 
+OBJ *
+player_connect(const char *qsession)
+{
+	char buf[BUFSIZ];
+	FILE *fp;
+
+	snprintf(buf, sizeof(buf), "/sessions/%s", qsession);
+	fp = fopen(buf, "r");
+
+	if (!fp)
+		return NULL;
+
+	fscanf(fp, "%s", buf);
+	fclose(fp);
+
+	return player_get(buf);
+}
+
 OBJ *
 player_create(const char *name)
 {
diff --git a/src/prochelp.c b/src/prochelp.c
index 3d680b0..df57e01 100644
--- a/src/prochelp.c
+++ b/src/prochelp.c
@@ -1,8 +1,6 @@
 #include <stdio.h>
 #include <string.h>
-#ifndef __OpenBSD__
-#include <bsd/string.h>
-#endif
+/* #include <strings.h> */
 #include <ctype.h>
 #include <stdlib.h>
 
@@ -27,6 +25,7 @@
 #define HTML_IDXGROUP_ENTRY	"    <td nowrap>   <a href=\"#%s\">%s</a>   </td>\n"
 #define HTML_IDXGROUP_NEWROW	"  </tr>\n  <tr>\n"
 #define HTML_IDXGROUP_END		"  </tr>\n</table></blockquote>\n\n"
+#define HTML_INDEX_END		""
 
 #define HTML_TOPICHEAD		"<hr><h4><a name=\"%s\">"
 #define HTML_TOPICHEAD_BREAK	"<br>\n"
@@ -280,6 +279,7 @@ print_sections(FILE * f, FILE * hf, int cols)
 	char sectname[256];
 	char *osectptr;
 	char *sectptr;
+	char buf[256];
 	char buf3[256];
 	char buf4[256];
 	char *currsect;
@@ -296,6 +296,7 @@ print_sections(FILE * f, FILE * hf, int cols)
 	}
 	for (sptr = secthead; sptr; sptr = sptr->next) {
 		currsect = sptr->section;
+		buf[0] = '\0';
 		strlcpy(sectname, currsect, sizeof(sectname));
 		sectptr = index(sectname, '|');
 		if (sectptr) {
@@ -413,6 +414,7 @@ print_topics(FILE * f, FILE * hf)
 			fprintf(hf, HTML_IDXGROUP_END);
 		}
 	}
+	fprintf(hf, HTML_INDEX_END);
 	fprintf(f, " \nUse '%s <topicname>' to get more information on a topic.\n", doccmd);
 }
 
diff --git a/src/rob.c b/src/rob.c
index 32394d7..97ab1db 100644
--- a/src/rob.c
+++ b/src/rob.c
@@ -1,4 +1,3 @@
-#include <stdlib.h>
 #include "io.h"
 #include "entity.h"
 #include "params.h"
diff --git a/src/set.c b/src/set.c
index 238712d..2a7aedc 100644
--- a/src/set.c
+++ b/src/set.c
@@ -1,11 +1,15 @@
+/* $Header$ */
+#include "io.h"
+
+
+#include "copyright.h"
+#include "entity.h"
+
+/* commands which set parameters */
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
-#include <stdlib.h>
 
-#include "io.h"
-#include "copyright.h"
-#include "entity.h"
 #include "utils.h"
 #include "params.h"
 #include "defaults.h"
diff --git a/src/spacetime.c b/src/spacetime.c
index 898db5e..ce9b062 100644
--- a/src/spacetime.c
+++ b/src/spacetime.c
@@ -2,10 +2,8 @@
 
 #include <stdlib.h>
 #include <ctype.h>
-#include <string.h>
-#include <time.h>
+#include <db4/db.h>
 
-#include "nddb.h"
 #include "io.h"
 #include "debug.h"
 #include "noise.h"
@@ -498,7 +496,8 @@ e_move(OBJ *player, enum exit e) {
 	}
 
 	dest = st_there(loc, e);
-	notifyf(eplayer, "You go %s.", e_name(e));
+	if (!eplayer->gpt)
+		notifyf(eplayer, "You go %s.", e_name(e));
 
 	if (!dest)
 		dest = st_room(player, e);
@@ -837,7 +836,8 @@ st_teleport(OBJ *player, struct cmd_dir cd)
 	if (!there)
 		there = st_room_at(player, pos);
 	CBUG(!there);
-	notifyf(eplayer, "Teleporting to 0x%llx.", cd.rep);
+	if (!eplayer->gpt)
+		notifyf(eplayer, "Teleporting to 0x%llx.", cd.rep);
 	enter(player, there, E_NULL);
 	return ret;
 }
diff --git a/src/speech.c b/src/speech.c
index 42b7145..0285013 100644
--- a/src/speech.c
+++ b/src/speech.c
@@ -22,17 +22,29 @@ do_say(command_t *cmd)
 	ENT *eplayer = &player->sp.entity;
 	const char *message = cmd->argv[1];
 
-	notifyf(eplayer, "You say, \"%s\"", message);
-	onotifyf(player, "%s says, \"%s\"", player->name, message);
+	if (eplayer->gpt) {
+		char contents[BUFFER_LEN];
+		sprintf(contents, "%s says: %s\n\n", player->name, message);
+		entity_gpt(player, 0, contents);
+	} else {
+		notifyf(eplayer, "You say, \"%s\"", message);
+		onotifyf(player, "%s says, \"%s\"", player->name, message);
+	}
 }
 
 void
 do_pose(command_t *cmd)
 {
 	OBJ *player = cmd->player;
+	ENT *eplayer = &player->sp.entity;
 	const char *message = cmd->argv[1];
 
-	anotifyf(player->location, "%s %s", player->name, message);
+	if (eplayer->gpt) {
+		char contents[BUFFER_LEN];
+		sprintf(contents, "Describe %s %s.\n\n", player->name, message);
+		entity_gpt(player, 1, contents);
+	} else
+		anotifyf(player->location, "%s %s", player->name, message);
 }
 
 void
diff --git a/src/spell.c b/src/spell.c
index e3a9a07..1d02d03 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -1,12 +1,11 @@
-#include <string.h>
-#include <stdlib.h>
-
 #include "io.h"
+
 #include "spell.h"
 #include "entity.h"
+
 #include "mob.h"
+
 #include "params.h"
-#include "debug.h"
 /* #include "speech.h" */
 
 element_t element_map[] = {
@@ -292,8 +291,8 @@ spell_cast(OBJ *player, OBJ *target, unsigned slot)
 
 	unsigned mana = eplayer->mp;
 	char a[BUFSIZ]; // FIXME way too big?
-	char b[BUFSIZ + 32];
-	char c[BUFSIZ + 32];
+	char b[BUFSIZ];
+	char c[BUFSIZ];
 	struct wts wts = { a, b };
 
 	char const *color = sp_color(_sp);
@@ -351,15 +350,3 @@ spells_cast(OBJ *player, OBJ *target)
 	return 0;
 }
 
-void
-spells_birth(OBJ *object) {
-	ENT *entity = &object->sp.entity;
-	register int j;
-	for (j = 0; j < 8; j++) {
-		struct spell *sp = &entity->spells[j];
-		struct spell_skeleton *_sp = SPELL_SKELETON(0);
-		sp->_sp = _sp;
-		sp->val = SPELL_DMG(entity, _sp);
-		sp->cost = SPELL_COST(sp->val, _sp->y, _sp->flags & AF_BUF);
-	}
-}
diff --git a/src/utils.c b/src/utils.c
index 70ddb39..a90e5b2 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -3,10 +3,13 @@
 #include <ctype.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include "params.h"
-/* #include "debug.h" */
-/* #include "config.h" */
+#include "debug.h"
+#include "config.h"
+
+struct popen2 {
+	int in, out, pid;
+};
 
 int
 ok_name(const char *name)
@@ -30,19 +33,20 @@ string_prefix(register const char *string, register const char *prefix)
 	return *prefix == '\0';
 }
 
-int
+static int
 popen2(struct popen2 *child, const char *cmdline)
 {
 	pid_t p;
 	int pipe_stdin[2], pipe_stdout[2];
 
-	if (pipe(pipe_stdin) || pipe(pipe_stdout))
-		return -1;
+	if(pipe(pipe_stdin)) return -1;
+	if(pipe(pipe_stdout)) return -1;
 
-	p = fork();
-	if (p < 0)
-		return p;
+	printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]);
+	//printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]);
 
+	p = fork();
+	if(p < 0) return p; /* Fork failed */
 	if(p == 0) { /* child */
 		close(pipe_stdin[1]);
 		dup2(pipe_stdin[0], 0);
@@ -51,11 +55,26 @@ popen2(struct popen2 *child, const char *cmdline)
 		execl("/bin/sh", "sh", "-c", cmdline, NULL);
 		perror("execl"); exit(99);
 	}
-
 	child->pid = p;
 	child->in = pipe_stdin[1];
 	child->out = pipe_stdout[0];
 	close(pipe_stdin[0]);
 	close(pipe_stdout[1]);
-	return 0;
+	return 0; 
+}
+
+char *
+gpt(char *prompt)
+{
+	static char buf[BUFFER_LEN];
+	struct popen2 child;
+
+	CBUG(popen2(&child, "/gpt3.sh"));
+	write(child.in, prompt, strlen(prompt));
+	close(child.in);
+	memset(buf, 0, sizeof(buf));
+	read(child.out, buf, sizeof(buf));
+	close(child.out);
+	kill(child.pid, 0);
+	return buf;
 }
diff --git a/src/view.c b/src/view.c
index ae5f97e..f5eea77 100644
--- a/src/view.c
+++ b/src/view.c
@@ -10,10 +10,6 @@
 #include "debug.h"
 #include "command.h"
 
-#ifndef __OpenBSD__
-#include "bsd/string.h"
-#endif
-
 #define BIOME_BG(i) (NIGHT_IS \
 		? ANSI_RESET : biomes[i].sp.biome.bg)
 
diff --git a/src/ws.c b/src/ws.c
index 66eb487..1797ad0 100644
--- a/src/ws.c
+++ b/src/ws.c
@@ -5,7 +5,6 @@
 #include <unistd.h>
 #include <sys/select.h>
 #include <openssl/sha.h>
-#include <arpa/inet.h>
 
 #define OPCODE(head) ((unsigned char) (head[0] & 0x0f))
 #define PAYLOAD_LEN(head) ((unsigned char) (head[1] & 0x7f))
diff --git a/tmp/TermPlay.jsx b/tmp/TermPlay.jsx
deleted file mode 100644
index 5a92b8e..0000000
--- a/tmp/TermPlay.jsx
+++ /dev/null
@@ -1,928 +0,0 @@
-import React, {
-  useState, useEffect, useRef,
-  useCallback, useReducer, useContext,
-} from "react";
-import { hot } from "react-hot-loader/root";
-import ReactDOM from "react-dom";
-import ACTIONS, { ACTIONS_LABEL } from "actions";
-import mcp from "mcp";
-import tty_proc from "tty";
-import canvas from "canvas";
-import "vim.css";
-const baseDir = process.env.CONFIG_BASEDIR || "";
-
-class Modal extends React.Component {
-	constructor(props) {
-		super(props);
-		// this.ref = React.createRef();
-		this.el = props.el;
-	}
-
-	componentDidMount() {
-		this.el.onclick = e => {
-			if (e.target == this.el)
-				this.props.setOpen(false);
-		};
-		this.el.className = 'modal abs sfv v0 c fcc oh f';
-		// this.el.classList.remove('dn');
-	}
-
-	componentWillUnmount() {
-		this.el.classList.add('dn');
-	}
-
-	onKeyDown(e) {
-		if (e.keyCode == 27) // escape
-			this.props.setOpen(false);
-	}
-
-	render() {
-		return ReactDOM.createPortal(
-			(<span
-				onKeyDown={e => this.onKeyDown(e)}
-				onClick={e => e.stopPropagation()}
-                                className="r c0 p oa"
-			>
-				{ this.props.children }
-                        </span>),
-			this.el,
-		);
-	}
-}
-
-const modal = document.querySelector('.modal');
-
-function _useModal(el, Component, props = {}) {
-	const [ isOpen, setOpen ] = useState(false);
-
-	const modal = isOpen ? (
-		<Modal setOpen={setOpen} el={el}>
-			<Component
-				{ ...props }
-				close={() => setOpen(false)}
-			/>
-		</Modal>
-	) : null;
-
-	return [ modal, isOpen, setOpen ];
-}
-
-const useModal = _useModal.bind(null, modal);
-
-// window.onorientationchange = scroll_reset;
-
-mcp.init();
-
-const atiles = [
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAQklEQVQ4jWNgGL7g4OFb/9ExWZpwYZyaiXEBhiH4NBNSA+fg8wpWW5H1ETIAl98xvEHIEJyacfmdEB8vICsdjFAAAGW58imbroFwAAAAAElFTkSuQmCC",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAWElEQVQ4jWNgoAY4ePjW/4OHb/0nWw+MQ6whWNUTawhedYQMIcoSXIpI8ia6YlLDCMMQcmKJdGdTxQW4/IyNT7KziXYJMemAoBewOROfHG5BAi4lRT1OAAA/Xu7MVtQQRgAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOUlEQVQ4jWNgGJTg4OFb/3FhkjXjEiNaMzFyWBXB+Li8RHsDKPYCIZfQxwCSvYBLIcWJiSTNQw8AAO8uLsItXTaGAAAAAElFTkSuQmCC",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAR0lEQVQ4jWNgIBIcPHzrPzImWREyn2wDyHIBuhhJBhDUgM8QdDZJmim2nWwDcBlCkmZ0Q8iynSRN2AwYGP9T3QAYnyQDyAEAd7gQfVonw9EAAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAVElEQVQ4jaXRywkAMAgDUPffStzCZey1FE3UCt7Cw48IKTUPNQ+Wo8AauYEV8gIQycJVQ6ALwwlWq7DAGEBIegO07+iV3wBDWgBC2kCFjIAMGQOoDj9EP5M9YZdfAAAAAElFTkSuQmCC",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVQ4jWNgGLTg4OFb/5ExyRoGjwHIcmQZgi5HsiHY5EgyBJccQUOIsWBwGEKRAcMEAABAgauIKxMDjQAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAUElEQVQ4jWNgwAEOHr71H5cc0QBmCEWGUcUlZNuMjskyBJmmnwHYnEySNw4evvX/////TLhcRFAzJfLUMYAQxmsAIUMIaqaKAdgMIkkjKQAABfenzXQV7xsAAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAQUlEQVQ4jWNggIKDh2/9P3j41n8GcgBMM9mGoBswuAwh2UBChhBlENUMwGcwWQYQkiPJFQQNgClGp8lOraMANwAA27bDE5yTd30AAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAM0lEQVQ4jWNgGFTg4OFb/4nFOA0g1iK8EoRsHhgXkGQAxS6gyACKo5FYV1DkBeprHtoAAOijqCv2+qTkAAAAAElFTkSuQmCC",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAWElEQVQ4jc3QwQ0AMAQFUPtvJbawjF4qaUVQcair/18EwB4kFiQW6IyW24gF/kKewQwpQWNABJcBWxq7IgU03C5HUH2ZZJBYwrPOx1no6mWI3bv5yiVebgFV9xc3cxaEggAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAALElEQVQ4jWNgGF7g4OFb/5ExsXI4FeHDRLuCJM3EGEJWWIxEA3AZQrTmEQYAvLnktS1HPJUAAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAATElEQVQ4jWNggIKDh2/9P3j41n8GcgBMM9mGoBswuAwh2UBChhBlENUMwGcw0Qaga6KaKwgaAFNMtmZ8BtHPAOSAI9sgipI2ukuwAQABHugSAyRIzQAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAQUlEQVQ4jWNgoDY4ePjWf7LkYRLoNLoasg2AaR6iBiBrpq0BuBQOEQPQFeIzHKcBMMWEXEeyAfhcR7Iisg3AJQcA7LE1fLFqxRoAAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAR0lEQVQ4jWNgoCU4ePjWfximSDNZhlDFAGw02a4gWTNJtuOzBZ84hhwxziXKS9gUURyVJGlEt5EkQwgFKE6DSLGForRBdQAAjPakzh7dkTsAAAAASUVORK5CYII=",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVQ4jWNgGFTg4OFb/4nBBA0hRw7FBWTJE+U8fOqI0YxX7agBVDKAomhElqQoKdMdAADVVJq360zbaQAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASUlEQVQ4jWNgoBU4ePjWf2RMsSFUcwFRLkNXgG4ANjbRzsZrAC7N+LxDlM0EXUOKs3GKE+tsYgwmLEkzA0hNvlgNIKSJ9gaQigF3uf+dQNoz9QAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAATElEQVQ4jWNgoDU4ePjWf4o0wzDVNCOL4zQYnwJkMawGEDIdqwEwBqnORjGAWGdjFSfF2QQNwOYdUg0mLEkzA4hKJIQMIKSJ9gaQigFrg/+dXV9AmAAAAABJRU5ErkJggg==",
-        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAEklEQVQ4jWNgGAWjYBSMAggAAAQQAAF/TXiOAAAAAElFTkSuQmCC"
-];
-
-const GameContext = React.createContext({});
-
-function useSession(onOpen, onMessage, onClose) {
-        const [ session, setSession ] = useState(null);
-
-        const connect = useCallback(() => {
-                const ws = new WebSocket(process.env.CONFIG_PROTO + "://" + window.location.hostname + ':4201', 'text');
-                ws.binaryType = 'arraybuffer';
-                setSession(ws);
-        }, []);
-
-        const updateOpenHandler = () => {
-                if (!session) return;
-                session.addEventListener('open', onOpen);
-                return () => {
-                        session.removeEventListener('open', onOpen);
-                };
-        };
-
-        const updateMessageHandler = () => {
-                if (!session) return;
-                session.addEventListener('message', onMessage);
-                return () => {
-                        session.removeEventListener('message', onMessage);
-                };
-        };
-
-	const _onClose = useCallback(evt => onClose(evt, connect));
-
-        const updateCloseHandler = () => {
-                if (!session) return;
-                session.addEventListener('close', _onClose);
-                return () => {
-                        session.removeEventListener('close', _onClose);
-                };
-        };
-
-        useEffect(connect, []);
-        useEffect(updateOpenHandler, [session, onOpen]);
-        useEffect(updateMessageHandler, [session, onMessage]);
-        useEffect(updateCloseHandler, [session, _onClose]);
-
-        function sendMessage(text) {
-                // console.log("sendMessage", text);
-                session.send(text + "\n");
-        }
-
-        return [connect, sendMessage, session];
-}
-
-function webLookReducer(state, action) {
-        const dbref = parseInt(action.dbref);
-        const { room } = action;
-
-        const ret = {
-                ...state,
-                ...(room ? {
-                        here: dbref,
-                        target: null,
-                } : {
-                        target: dbref,
-                }),
-                objects: {
-                        ...state.objects,
-                        [dbref]: {
-                                ...action,
-                                contents: {},
-                        },
-                },
-                terminal: state.terminal + (action.description ? "\nYou see: " + action.description : ""),
-        };
-
-        // console.log("WEB-LOOK", state, action, ret);
-
-        return ret;
-}
-
-function webLookContentReducer(state, action) {
-        const dbref = parseInt(action.dbref);
-        const loc = parseInt(action.loc);
-        return {
-                ...state,
-                objects: {
-                        ...state.objects,
-                        [loc]: {
-                                ...state.objects[loc],
-                                contents: {
-                                        ...state.objects[loc].contents,
-                                        [dbref]: {
-                                                ...action,
-                                                icon: tty_proc(action.icon),
-                                                pname: tty_proc(action.pname),
-                                        },
-                                },
-                        },
-                }
-        };
-}
-
-function webInReducer(state, action) {
-        const dbref = parseInt(action.dbref);
-        const loc = parseInt(action.loc);
-
-        if (!state.objects[loc]) {
-                console.warn("web-in: actionable of loc", loc, "is not available");
-                return state;
-        }
-
-        return {
-                ...state,
-                objects: {
-                        ...state.objects,
-                        [loc]: {
-                                ...state.objects[loc],
-                                contents: {
-                                        ...state.objects[loc].contents,
-                                        [dbref]: {
-                                                ...action,
-                                                icon: tty_proc(action.icon),
-                                                pname: tty_proc(action.pname),
-                                        },
-                                }
-                        }
-                }
-        };
-}
-
-function gameReducer(state, action) {
-        // console.log(action, state);
-
-        switch (action.key) {
-                case 'inband':
-                        if (action.data != "\n\r")
-                                return {
-                                        ...state,
-                                        terminal: state.terminal + tty_proc(action.data),
-                                }
-                        break;
-
-                case 'web-view':
-                        return {
-                                ...state,
-                                view: tty_proc(action.data),
-                        }
-
-                case 'web-look':
-                        return webLookReducer(state, action);
-
-                case 'web-look-content':
-                        return webLookContentReducer(state, action);
-
-                case 'web-in':
-                        return webInReducer(state, action);
-
-                case 'web-out':
-                        const dbref = parseInt(action.dbref);
-                        const loc = parseInt(action.loc);
-
-                        if (!state.objects[loc])
-                                return state;
-
-                        let newContents = { ...state.objects[loc].contents };
-                        delete newContents[dbref];
-
-                        return {
-                                ...state,
-                                objects: {
-                                        ...state.objects,
-                                        [loc]: {
-                                                ...state.objects[loc],
-                                                contents: newContents
-                                        }
-                                },
-                        };
-
-                case 'web-auth-success':
-                        return {
-                                ...state,
-                                me: parseInt(action.player),
-                                authFail: null,
-                        };
-
-                case 'web-auth-fail':
-                        return {
-                                ...state,
-                                me: parseInt(action.player),
-                                authFail: true,
-                        };
-
-
-                case 'web-stats':
-                        return {
-                                ...state,
-                                stats: action,
-                        };
-
-                case 'web-bars':
-                        return {
-                                ...state,
-                                bars: action,
-                        };
-
-                case 'web-equipment':
-                        return {
-                                ...state,
-                                equipment: {},
-                        };
-
-                case 'web-equipment-item':
-                        return {
-                                ...state,
-                                equipment: {
-                                        ...(state.equipment || {}),
-                                        [parseInt(action.eql)]: {
-                                                ...action,
-                                                pname: tty_proc(action.pname),
-                                                icon: tty_proc(action.icon),
-                                        },
-                                },
-                        };
-        }
-
-        return state;
-}
-
-function GameContextProvider(props) {
-        const [ open, setOpen ] = useState(false);
-        const { children } = props;
-
-        const [state, dispatch] = useReducer(
-                gameReducer,
-                {
-                        terminal: "",
-                        objects: {},
-                        target: null,
-                        here: null,
-                }
-        );
-
-        function onMessage(ev) {
-                const mcp_arr = mcp.proc(ev.data);
-                for (let i = 0; i < mcp_arr.length; i++) {
-                        // console.log(mcp_arr[i]);
-                        dispatch(mcp_arr[i]);
-                }
-        }
-
-        function output(text) {
-                dispatch({
-                        key: "inband", 
-                        data: "\n" + text,
-                });
-        }
-
-        function onOpen() {
-                output("socket connection open");
-
-          if (process.env.development) {
-            sendMessage("auth test");
-          } else {
-            sendMessage("auth " + getCookie("QSESSION"));
-          }
-          
-                setOpen(true);
-        }
-
-        function onClose(evt, connect) {
-                output("socket connection closed");
-		connect();
-        }
-
-        const [ connect, sendMessage, session ] = useSession(onOpen, onMessage, onClose);
-
-        return <GameContext.Provider
-                value={{
-                        ...state, 
-                        session,
-                        sendMessage,
-                        dispatch,
-                }}
-        >
-                { children }
-        </GameContext.Provider>
-        
-}
-
-function Terminal() {
-        const { terminal } = useContext(GameContext);
-        const ref = useRef(null);
-
-        useEffect(() => {
-                ref.current.scrollTop = ref.current.scrollHeight;
-        }, [terminal]);
-
-        // console.log(context);
-        return (<pre id="term" ref={ref} className="fg oa"
-                dangerouslySetInnerHTML={{ __html: terminal }}>
-        </pre>)
-}
-
-function RoomTitleAndArt() {
-        const { here, objects } = useContext(GameContext);
-        const obj = objects[here];
-
-        if (!obj)
-                return null;
-
-        const src = baseDir + "/art/" + (obj.art || "unknown.jpg");
-
-        return (<div className="v0 fg f fic">
-                <div className="tm pxs tac">{ obj.name }</div>
-                <img className="sr2" src={src} />
-        </div>);
-}
-
-function Tabs(props) {
-        const { children } = props;
-        const [ activeTab, setActiveTab ] = useState(0);
-
-        return (<div className="fg v0">
-                <div className="h0">
-                        { children.map((child, idx) => {
-                                const { label } = child.props;
-                                return (<a key={idx}
-                                        onClick={() => setActiveTab(idx)}
-                                        className={activeTab == idx ? 'p8 c0' : 'p8'}
-                                >
-                                        {label}
-                                </a>);
-                        }) }
-                </div>
-                { children.map((child, idx) => {
-                        // const { label } = child.props;
-                        if (idx != activeTab)
-                                return null;
-
-                        return child;
-                }) }
-        </div>);
-}
-
-function Stat(props) {
-        const { label, value } = props;
-
-        return (<div className="h8">
-                <div className="tbbold">{label}</div><div>{value}</div>
-        </div>);
-}
-
-const EQL_HEAD = 1;
-const EQL_NECK = 2;
-const EQL_CHEST = 3;
-const EQL_BACK = 4;
-const EQL_RHAND = 5;
-const EQL_LFINGER = 6;
-const EQL_RFINGER = 7;
-const EQL_PANTS = 8;
-
-const eql_label = {
-        [EQL_HEAD]: "hand",
-        [EQL_NECK]: "neck",
-        [EQL_CHEST]: "chest",
-        [EQL_BACK]: "back",
-        [EQL_RHAND]: "weapon",
-        [EQL_LFINGER]: "lfinger",
-        [EQL_RFINGER]: "rfinger",
-        [EQL_PANTS]: "grieves",
-};
-
-function Equipment(props) {
-        const { eql } = props;
-        const { equipment, sendMessage } = useContext(GameContext);
-
-        if (!equipment)
-                return null;
-
-        if (!equipment[eql])
-                return <div className="s32 c0"></div>;
-
-        return (<Avatar
-                item={equipment[eql]}
-                size="l"
-                onClick={() => sendMessage("unequip " + eql_label[eql])}
-        />);
-}
-
-function PlayerTabs() {
-        const { me, stats } = useContext(GameContext);
-
-        if (!me)
-                return null;
-
-        return (<Tabs>
-                <div label="stats" className="p8 h8 f">
-                        <div className="v8 fg">
-                                <Stat label="str" value={stats.str} />
-                                <Stat label="con" value={stats.con} />
-                                <Stat label="dex" value={stats.dex} />
-                                <Stat label="int" value={stats.int} />
-                                <Stat label="wiz" value={stats.wiz} />
-                                <Stat label="cha" value={stats.cha} />
-                        </div>
-                        <div className="v8 fg">
-                                <Stat label="dodge" value={stats.dodge} />
-                                <Stat label="dmg" value={stats.dmg} />
-                                <Stat label="mdmg" value={stats.mdmg} />
-                                <Stat label="def" value={stats.def} />
-                                <Stat label="mdef" value={stats.mdef} />
-                        </div>
-                </div>
-                <div label="equipment" className="p8 v8">
-                        <div className="h8 f">
-                                <div className="s32"></div>
-                                <div className="s32"></div>
-                                <Equipment eql={EQL_HEAD} />
-                                <div className="s32"></div>
-                                <div className="s32"></div>
-                        </div>
-                        <div className="h8 f">
-                                <Equipment eql={EQL_RHAND} />
-                                <div className="s32"></div>
-                                <Equipment eql={EQL_NECK} />
-                                <Equipment eql={EQL_BACK} />
-                                <div className="s32"></div>
-                        </div>
-                        <div className="h8 f">
-                                <Equipment eql={EQL_RFINGER} />
-                                <div className="s32"></div>
-                                <Equipment eql={EQL_CHEST} />
-                                <div className="s32"></div>
-                                <Equipment eql={EQL_LFINGER} />
-                        </div>
-                        <div className="h8 f">
-                                <div className="s32"></div>
-                                <div className="s32"></div>
-                                <Equipment eql={EQL_PANTS} />
-                                <div className="s32"></div>
-                                <div className="s32"></div>
-                        </div>
-                </div>
-        </Tabs>);
-}
-
-function MiniMap(props) {
-        const { view, target } = useContext(GameContext);
-
-        if (target || !view)
-                return null;
-        
-        return <div className="fac"><pre id="map" dangerouslySetInnerHTML={{ __html: view }}></pre></div>;
-}
-
-function TargetTitleAndArt() {
-        const { target, objects } = useContext(GameContext);
-
-        if (!target)
-                return null;
-
-        const obj = objects[target];
-
-  const src = baseDir + "/art/" + (obj.art || "unknown_small.jpg");
-
-        return (<>
-                <div className="tm pxs tac">{obj.name}</div>
-                <img className="sr1 fac" src={src} />
-        </>);
-}
-
-function Avatar(props) {
-        const { item, size = "xl", ...rest } = props;
-
-        if (!item)
-                return null;
-
-        if (item.avatar)
-                return <img
-                        className={"sh" + size + " sv" + size}
-                        src={baseDir + "/art/" + item.avatar}
-                        { ...rest }
-                />;
-        else
-                return <span
-                        className={"s" + size + " t" + size + " tcv"}
-                        dangerouslySetInnerHTML={{ __html: item.icon }}
-                        { ...rest }
-                />;
-}
-
-function ContentsItem(props) {
-        const { item, onClick, activeItem, isShop } = props;
-        const className = "f fic pxs h8 " + (activeItem == item.dbref ? 'c0' : "");
-
-        return (<a className={className} onClick={onClick}>
-                <Avatar item={item} />
-                <span dangerouslySetInnerHTML={{
-                        __html: item.pname + (isShop ? " " + item.price + "P" : ""),
-                }}></span>
-        </a>);
-}
-
-function Contents(props) {
-        const { onItemClick, activeItem } = props;
-        const { target, here, objects } = useContext(GameContext);
-
-        const obj = target ? objects[target] : objects[here];
-
-        if (!here)
-                return null;
-
-        const contentsEl = Object.keys(obj.contents).map(k => {
-                const item = obj.contents[k];
-
-                return <ContentsItem key={item.dbref} item={item}
-                        isShop={obj.shop}
-                        activeItem={activeItem}
-                        onClick={e => onItemClick(e, item)} />;
-        });
-
-        return (<div className="v0 fg oa icec">
-                { contentsEl }
-        </div>);
-}
-
-function RB(props) {
-        const { children, onClick } = props;
-        return <a className="round ts26 p8 c0" onClick={onClick}>{ children }</a>;
-}
-
-function RBT(props) {
-        return <a className="round ts26 p8"></a>;
-}
-
-function RBI(props) {
-        const { onClick, src } = props;
-        return (<a className="round ts26 p8 c0" onClick={onClick}>
-                <img className="svl shl" src={atiles[src]} />
-        </a>);
-}
-
-function Directions() {
-        const { sendMessage } = useContext(GameContext);
-
-        return (<div className="v0 tar tnow abs ar">
-                <div className="h0 f">
-                        <RBT />
-                        <RB onClick={() => sendMessage("k")}>↑</RB>
-                        <RBI onClick={() => sendMessage("K")} src={ACTIONS.K} />
-                </div>
-                <div className="h0 f">
-                        <RB onClick={() => sendMessage("h")}>←</RB>
-                        <RBT />
-                        <RB onClick={() => sendMessage("l")}>→</RB>
-                </div>
-                <div className="h0 f">
-                        <RBT />
-                        <RB onClick={() => sendMessage("j")}>↓</RB>
-                        <RBI onClick={() => sendMessage("J")} src={ACTIONS.J} />
-                </div>
-        </div>);
-}
-
-function ContentsAndActions(props) {
-        const { sendMessage, here, me, target, objects } = useContext(GameContext);
-        const [ actions, setActions ] = useState([]);
-        const [ activeItem, setActiveItem ] = useState(null);
-
-        function onItemClick(ev, item) {
-                let newActions = [];
-
-                if (item.loc == here) {
-                        for (let p = 0; p < 9; p++) {
-                                if (!(parseInt(item.actions) & (1 << p)))
-                                        continue;
-
-                                let id = ACTIONS_LABEL[p];
-                                newActions.push([p, function () {
-                                        sendMessage(id + " #" + item.dbref);
-                                }]);
-                        }
-                } else if (item.loc == me) {
-                        // action_add(ACTIONS.PUT, function () {
-                        //         output("\nPUT is not implemented yet!");
-                        // });
-
-                        newActions.push([ACTIONS.EQUIP, function () {
-                                sendMessage("equip #" + item.dbref);
-                        }]);
-
-                        newActions.push([ACTIONS.DROP, function () {
-                                sendMessage("drop #" + item.dbref);
-                        }]);
-
-                        newActions.push([ACTIONS.EAT, function () {
-                                sendMessage("eat #" + item.dbref);
-                        }]);
-
-                        newActions.push([ACTIONS.SHOP, function () {
-                                sendMessage("sell #" + item.dbref);
-                        }]);
-
-                } else if (objects[item.loc].shop) {
-                        newActions.push([ACTIONS.SHOP, function () {
-                                sendMessage("buy #" + item.dbref);
-                        }]);
-                } else {
-                        newActions.push([ACTIONS.GET, function () {
-                                sendMessage("get #" + target + "=#" + item.dbref);
-                        }]);
-                }
-
-                setActions(newActions);
-                setActiveItem(item.dbref);
-        }
-
-        const actionsEl = actions.map(([p, cb]) => (
-                <RBI key={p} onClick={cb} src={p} />
-        ));
-
-        return (<>
-                <Contents onItemClick={onItemClick} activeItem={activeItem} />
-
-                <div className="h0 icec">
-                        { actionsEl }
-                </div>
-        </>);
-}
-
-function Help() {
-        return (<>
-                <b>Help</b><br />
-                <br />
-		Use "a" to focus the textbox input.<br />
-		Input "X" to teleport to the starting position.<br />
-
-		<br />
-                <b>More details: Normal mode:</b><br />
-                <p>
-                        <b>s</b> To chat.<br />
-                        <b>a</b> to send commands.<br />
-                        <b>i</b> to access your inventory.<br />
-                        <b>Left</b> or <b>h</b> to move west.<br />
-                        <b>Right</b> or <b>l</b> to move east.<br />
-                        <b>Up</b> or <b>k</b> to move north.<br />
-                        <b>Down</b> or <b>j</b> to move south.<br />
-                        <b>Shift+Up</b> or <b>K</b> to move up.<br />
-                        <b>Shift+Down</b> or <b>J</b> to move down.<br />
-                </p>
-                <b>Input mode:</b><br />
-                <p>
-                        <b>Esc</b> to go back to normal mode.<br />
-                        <b>Up</b> to travel up in history.<br />
-                        <b>Down</b> to travel down in history.<br />
-                        <b>Ctrl+u</b> to delete input text.<br />
-                </p>
-                <br />
-                You can check out the code <a href="https://github.com/tty-pt/neverdark">here</a>.<br />
-                <br />
-                The help command is very useful. Try issuing "help help".<br />
-
-		<p><b>also see help X, walking, w</b> </p>
-        </>);
-}
-
-function Bar(props) {
-        const { max, value, color } = props;
-        const style = {
-                width: (100 * value / max) + "%",
-        };
-
-        return (<div className="fg svs b0">
-                <div className={"svf c" + color} style={style}></div>
-        </div>);
-}
-
-function PlayerBars() {
-        const { bars } = useContext(GameContext);
-
-        if (!bars)
-                return null;
-
-        const { hp, hpMax, mp, mpMax } = bars;
-
-        return (<div className="h8 f p8">
-                <Bar value={hp} max={hpMax} color="1" />
-                <Bar value={mp} max={mpMax} color="12" />
-        </div>);
-}
-
-function Game() {
-        const { sendMessage, session } = useContext(GameContext);
-	const [ modal, isOpen, setOpen ] = useModal(Help, {});
-        const input = useRef(null);
-
-        function keyDownHandler(e) {
-                if (document.activeElement == input.current) {
-                        switch (e.keyCode) {
-                                case 27:
-                                        input.current.blur();
-                                        break;
-                                case 85: // u
-                                        if (e.ctrlKey)
-                                                input.current.value = "";
-                                        break;
-                        }
-                        return;
-                }
-
-                switch (e.keyCode) {
-                        case 83: // s
-				if (e.shiftKey)
-					sendMessage("sit");
-				else {
-					input.current.value = "say ";
-					input.current.focus();
-					e.preventDefault();
-				}
-				break;
-                        case 65: // a
-                                input.current.focus();
-                                e.preventDefault();
-                                break;
-                        case 75: // k
-                        case 38: // ArrowUp
-                                if (e.shiftKey)
-                                        sendMessage("K");
-                                else
-                                        sendMessage("k");
-                                break;
-                        case 74: // j
-                        case 40: // ArrowDown
-                                if (e.shiftKey)
-                                        sendMessage("J");
-                                else
-                                        sendMessage("j");
-                                break;
-                        case 72: // h
-                        case 37: // ArrowLeft
-                                sendMessage("h");
-                                break;
-                        case 76: // l
-                        case 39: // ArrowRight
-                                sendMessage("l");
-                                break;
-                        case 73: // i
-                                sendMessage("inventory");
-                                break;
-                        case 79: // o
-                                sendMessage("look");
-                                break;
-			default:
-				console.log(e);
-                }
-        }
-        useEffect(() => {
-                window.addEventListener('keydown', keyDownHandler);
-                return () => window.removeEventListener('keydown', keyDownHandler);
-        }, [session]);
-
-        function toggle_help() {
-                setOpen(!isOpen);
-        }
-
-        function onSubmit(e) {
-                e.preventDefault();
-                const fd = new FormData(e.target);
-                sendMessage(fd.get("cmd"));
-                input.current.value = "";
-                input.current.blur();
-        }
-
-        return (<>
-                <span className="v0 f">
-                        {/* <RB onClick={disconnect}>X</RB> */}
-                        <RB onClick={toggle_help}>?</RB>
-                        <RBI onClick={() => sendMessage('inventory')} src={ACTIONS.OPEN} />
-                        <RBI onClick={() => sendMessage('look')} src={ACTIONS.LOOK} />
-                </span>
-
-                <form className="v0 f fg shf oa" onSubmit={onSubmit}>
-                        <div className="h0 f">
-                                <PlayerTabs />
-                                <RoomTitleAndArt />
-                        </div>
-
-                        <PlayerBars />
-
-                        <Terminal />
-
-                        <input ref={input} name="cmd" className="cf"
-                                autoComplete="off" autoCapitalize="off" />
-                </form>
-
-                <div className="v0 f fg sh33">
-                        <MiniMap />
-                        <TargetTitleAndArt />
-                        <Directions />
-                        <ContentsAndActions />
-                </div>
-
-                { modal }
-        </>);
-}
-
-function getCookie(cname) {
-	let name = cname + "=";
-	let decodedCookie = decodeURIComponent(document.cookie);
-	let ca = decodedCookie.split(';');
-	for(let i = 0; i < ca.length; i++) {
-		let c = ca[i];
-		while (c.charAt(0) == ' ') {
-			c = c.substring(1);
-		}
-		if (c.indexOf(name) == 0) {
-			return c.substring(name.length, c.length);
-		}
-	}
-	return "";
-}
-
-function InnerApp() {
-        // const { sendMessage } = useContext(GameContext);
-
-        return (<Game />);
-}
-
-
-export default
-hot(function App() {
-        return (<GameContextProvider>
-                <InnerApp />
-        </GameContextProvider>);
-});
diff --git a/vss/Makefile b/vss/Makefile
index 4c105b4..350c51a 100644
--- a/vss/Makefile
+++ b/vss/Makefile
@@ -1,5 +1,4 @@
 vss-t-args := -a0.112 -b-0.61 -c3.9 -d6.1 -m0.88
 PREFIX ?= ${HOME}/vim.css
 vss-prefix ?= ${PREFIX}
-vss-out := ${srcdir}/js/vim.css
 include ${vss-prefix}/mk/vss.mk
diff --git a/vss/color.txt b/vss/c.txt
similarity index 100%
rename from vss/color.txt
rename to vss/c.txt
diff --git a/vss/vss.config.h b/vss/vss.config.h
index 0f797b5..7b94ca7 100644
--- a/vss/vss.config.h
+++ b/vss/vss.config.h
@@ -2,56 +2,36 @@
 #define CONFIG_ROUND
 #include "vss/vss.h"
 
-ROUND
-ALL_FLEX
-CALL(TEXT_SIZE, ALL_TEXT_SIZES)
-CALL(BACKGROUND_COLOR, ALL_COLORS)
-CALL(COLOR, ALL_COLORS)
-CALL(BORDER, ALL_COLORS)
-CALL(SIZE, ALL_SIZES)
-CALL(PADDING, ALL_SIZES)
-CALL(AXIS_horizontal, ALL_SIZES)
-CALL(AXIS_vertical, ALL_SIZES)
-AXIS_0
-FULL_SIZE
+CALL(TEXT_SIZE, TS)
+CALL(BG_COLOR, CS)
+CALL(COLOR, CS)
+CALL(BO_COLOR, CS)
+CALL(SIZE, SS)
+CALL(PADDING, SS)
+CALL(HORIZONTAL, SS)
+CALL(VERTICAL, SS)
 /* CALL(FLEX_VERTICAL, SS) */
-CALL(ROUND_T, ALL_TEXT_SIZES)
-CALL(ROUND_EDGE, ALL_SIZES)
-ROUND_PADDING( 4, 14)
-ROUND_PADDING( 8, 17)
-ROUND_PADDING( 8, 20)
-ROUND_PADDING( 8, 26)
-TEXT_ALIGN(right)
-FONT_WEIGHT(bold)
-DIRE(right)
+CALL(ROUND_T, TS)
+CALL(ROUND_EDGE, SS)
+ROUND_PADDING( xs, )
+ROUND_PADDING( s, m)
+ROUND_PADDING( s, l)
+ROUND_PADDING( s, xl)
 
 #define C #3c403c
 #define CF #c1c3da
-#define CM VAL(COLOR, 13)
+#define CM C_13
 #define CBM rgba(0.05, 0.05, 0.05, 0.1)
 
-.oa { overflow: auto; }
-.dn { display: none; }
 .cf { background: C; }
 .cb { color: CF; }
-body { caret-color: CM; margin: 0; }
-h1,h2,h3,h4,h5,h6 { color: VAL(COLOR, 15); }
+body { caret-color: CM; }
+h1,h2,h3,h4,h5,h6 { color: C_15; }
 img { color: CF; };
 .modal a { color: CM; }
-input { border: solid thin VAL(COLOR, 0); color: CF; }
+input { border: solid thin C_0; color: CF; }
 input:focus {
         border: solid thin CM;
         outline: CM;
 }
-.abs { position: absolute; }
-.rel { position: relative; }
 a { color: CF; }
-html { height: 100%; }
-.sr2 {
-	width: 512px;
-	height: 256px;
-}
-.sr1 {
-	width: 256px;
-	height: 256px;
-}
diff --git a/webpack.config.js b/webpack.config.js
deleted file mode 100644
index ca2bbe5..0000000
--- a/webpack.config.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const makeConfig = require("@tty-pt/scripts/webpack.config");
-const webpack = require("webpack");
-
-module.exports = function (env) {
-	const CONFIG_BASEDIR = env.development ? "." : "/neverdark";
-	const CONFIG_PROTO = env.development ? "ws" : "wss";
-
-	const config = makeConfig({
-		...env,
-		CONFIG_PROTO,
-		CONFIG_BASEDIR,
-		srcdir: "js",
-	});
-
-	config.plugins.push(
-		new webpack.DefinePlugin({
-			"process.env": JSON.stringify({
-				CONFIG_PROTO,
-				CONFIG_BASEDIR,
-				development: env.development,
-			}),
-		})
-	);
-
-	return config;
-};