diff --git a/xtrn/lord2/lord2.js b/xtrn/lord2/lord2.js
index 1f395045a7d6c39185110e1ffc7b453e1f7b8a07..86ceda8faffdf31b4ad5276827efd8ffdc16f867 100644
--- a/xtrn/lord2/lord2.js
+++ b/xtrn/lord2/lord2.js
@@ -3,6 +3,7 @@
 // TODO: More optimal horizontal lightbars
 // TODO: Multiplayer interactions
 // TODO: Save player after changes in case process crashes
+// TODO: run NOTIME in HELP.REF on idle timeout
 
 js.yield_interval = 0;
 js.load_path_list.unshift(js.exec_dir+"dorkit/");
@@ -33,7 +34,7 @@ var map;
 var world;
 var game;
 var killfiles = [];
-var enemy;
+var enemy = undefined;
 var saved_cursor = {x:0, y:0};
 var progname = '';
 var time_warnings = [];
@@ -1013,6 +1014,16 @@ function sclrscr()
 	curlinenum = 1;
 }
 
+function clearrows(start, end)
+{
+	var row;
+
+	for (row = start; row <= end; row++) {
+		dk.console.gotoxy(0, row);
+		dk.console.cleareol();
+	}
+}
+
 function foreground(col)
 {
 	if (col > 15) {
@@ -1683,7 +1694,7 @@ function run_ref(sec, fname)
 			setvar(args[0], str);
 		},
 		'rename':function(args) {
-			file_rename(getfname(getvar(args[0]).toLowerCase()), getfname(getvar(args[1]).toLowerCase()));
+			file_rename(getfname(getvar(args[0])), getfname(getvar(args[1])));
 		},
 		'addlog':function(args) {
 			var f = new File(getfname('lognow.txt'));
@@ -1697,13 +1708,13 @@ function run_ref(sec, fname)
 			}
 		},
 		'delete':function(args) {
-			file_remove(getfname(getvar(args[0]).toLowerCase()));
+			file_remove(getfname(getvar(args[0])));
 		},
 		'statbar':function(args) {
 			status_bar();
 		},
 		'trim':function(args) {
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var len = getvar(args[1]);
 			var al;
 
@@ -1882,7 +1893,7 @@ function run_ref(sec, fname)
 					break;
 				case 'exist':
 				case 'exists':
-					if (file_exists(getfname(args[0].toLowerCase())) === (args[2].toLowerCase() === 'true'))
+					if (file_exists(getfname(args[0])) === (args[2].toLowerCase() === 'true'))
 						handlers.do(args.slice(4));
 					else if (args[4].toLowerCase() === 'begin')
 						handlers.begin(args.slice(5));
@@ -1929,15 +1940,19 @@ function run_ref(sec, fname)
 		},
 		'run':function(args) {
 			var f = fname;
-			var s = replace_vars(args[0]);
+			var s = replace_vars(args[0]).toLowerCase();
 
 			if (args.length > 2 && args[1].toLowerCase() === 'in') {
-				f = getvar(args[2]);
+				f = getvar(args[2]).toLowerCase();
 			}
+			if (f.indexOf('.') === -1)
+				f += '.ref';
 			if (files[f] === undefined)
 				load_ref(f);
+			if (files[f] === undefined)
+				throw new Error('Unable to load REF "'+f+'"');
 			if (files[f].section[s] === undefined)
-				throw new Error('Unable to find run section '+sec+' in '+f+' at '+fname+':'+line);
+				throw new Error('Unable to find run section '+s+' in '+f+' at '+fname+':'+line);
 			fname = f;
 			line = files[f].section[s].line;
 		},
@@ -2011,7 +2026,7 @@ function run_ref(sec, fname)
 		'writefile':function(args) {
 			if (args.length < 1)
 				throw new Error('No filename for writefile at '+fname+':'+line);
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			if (!f.open('ab'))
 				throw new Error('Unable to open '+f.name+' at '+fname+':'+line);
 			getlines().forEach(function(l) {
@@ -2021,7 +2036,7 @@ function run_ref(sec, fname)
 		},
 		'readfile':function(args) {
 			var vs = getlines();
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var l;
 
 			if (f.open('r')) {
@@ -2047,7 +2062,7 @@ function run_ref(sec, fname)
 			var lines;
 			if (args.length < 1)
 				throw new Error('No filename for displayfile at '+fname+':'+line);
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			if (!file_exists(f.name)) {
 				lln('`0File '+getvar(args[0])+' missing - please inform sysop');
 				return;
@@ -2177,11 +2192,10 @@ function run_ref(sec, fname)
 			player.put();
 		},
 		'busy':function(args) {
-			// Actually, it doesn't seem this touches UPDATE.TMP *or* TRADER.DAT
 			// TODO: Turn red for other players, and run @#busy if other player interacts
 			// this toggles battle...
-			//update_rec.battle = 1;
-			//update_rec.put();
+			player.battle = 1;
+			update_update();
 		},
 		'drawmap':function(args) {
 			draw_map();
@@ -2203,10 +2217,7 @@ function run_ref(sec, fname)
 			var sattr = dk.console.attr.value;
 
 			dk.console.attr.value = 2;
-			for (i = start; i < end; i++) {
-				dk.console.gotoxy(0, start);
-				dk.console.cleareol();
-			}
+			clearrows(start, end - 1);
 			dk.console.attr.value = sattr;
 		},
 		'loadmap':function(args) {
@@ -2272,8 +2283,8 @@ function run_ref(sec, fname)
 						lw('`$Q `2to quit, `$ENTER `2to buy item.        You have `$&gold `2gold.`r0');
 						break;
 				}
-				draw_map();
 			}
+			draw_map();
 		},
 		'saveglobals':function(args) {
 			world.put();
@@ -2347,6 +2358,7 @@ function run_ref(sec, fname)
 			var itm;
 			var ch;
 			var choice;
+			var box;
 
 			dk.console.gotoxy(0, 6);
 			lw('`r5`%  Item To Sell                        Amount Owned                              `r0');
@@ -2386,8 +2398,8 @@ rescan:
 								continue rescan;
 							}
 							if (player.i[itm.Record] > 1) {
-								draw_box(14, items[itm.Record].name, ['', '   `$Sell how many?               ',''])
-								dk.console.gotoxy(38, 16);
+								box = draw_box(14, items[itm.Record].name, ['', '   `$Sell how many?               ',''])
+								dk.console.gotoxy(box.x + 21, box.y + 2);
 								// TODO: This isn't exactly right... cursor is in wrong position, and selected colour is used.
 								ch = dk.console.getstr({edit:player.i[itm.Record].toString(), integer:true, input_box:true, attr:new Attribute(47), len:11});
 								lw('`r1`0');
@@ -2396,8 +2408,8 @@ rescan:
 									continue rescan;
 								}
 							}
-							draw_box(16, itm.name ['', '`$Sell '+amt+' of \'em for '+(amt * price)+' gold?','','`r5`$Yes','`$No']);
-							dk.console.gotoxy(25,21);
+							box = draw_box(16, itm.name, ['', '`$Sell '+amt+' of \'em for '+(amt * price)+' gold?','','`r5`$Yes','`$No']);
+							dk.console.gotoxy(box.x + 3, box.y + 4);
 							yn = true;
 							do {
 								ch = getkey().toUpperCase();
@@ -2411,14 +2423,15 @@ rescan:
 									case '6':
 									case 'KEY_RIGHT':
 										yn = !yn;
-										dk.console.gotoxy(23,20);
+										dk.console.gotoxy(box.x + 3, box.y + 4);
 										if (yn)
 											lw('`r5');
 										lw('`$Yes`r1');
-										dk.console.gotoxy(23,21);
+										dk.console.gotoxy(box.x + 3, box.y + 5);
 										if (!yn)
 											lw('`r5');
 										lw('`$No`r1');
+										dk.console.gotoxy(box.x + 3, box.y + 5 - yn);
 										break;
 								}
 							} while (ch !== '\r');
@@ -2435,12 +2448,12 @@ rescan:
 							lw('`r0');
 							continue rescan;
 					}
-					draw_map();
 				}
+				draw_map();
 			}
 		},
 		'dataload':function(args) {
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var rec = getvar(args[1]);
 			var val;
 
@@ -2465,7 +2478,7 @@ rescan:
 			setvar(args[2], val);
 		},
 		'datasave':function(args) {
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var rec = getvar(args[1]);
 			var val = replace_vars(getvar(args[2]));
 
@@ -2488,7 +2501,7 @@ rescan:
 			f.close();
 		},
 		'datanewday':function(args) {
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var i;
 			var d;
 
@@ -2572,11 +2585,11 @@ rescan:
 			}
 		},
 		'copyfile':function(args) {
-			file_copy(getfname(getvar(args[0]).toLowerCase()), getfname(getvar(args[1]).toLowerCase()));
+			file_copy(getfname(getvar(args[0])), getfname(getvar(args[1])));
 		},
 		'convert_file_to_ansi':function(args) {
-			var inf = new File(getfname(getvar(args[0]).toLowerCase()));
-			var out = new File(getfname(getvar(args[1]).toLowerCase()));
+			var inf = new File(getfname(getvar(args[0])));
+			var out = new File(getfname(getvar(args[1])));
 			var l;
 
 			if (!inf.open('r'))
@@ -2592,8 +2605,8 @@ rescan:
 			out.close();
 		},
 		'convert_file_to_ascii':function(args) {
-			var inf = new File(getfname(getvar(args[0]).toLowerCase()));
-			var out = new File(getfname(getvar(args[1]).toLowerCase()));
+			var inf = new File(getfname(getvar(args[0])));
+			var out = new File(getfname(getvar(args[1])));
 			var l;
 
 			if (!inf.open('r'))
@@ -2625,7 +2638,7 @@ rescan:
 			player = op;
 		},
 		'lordrank':function(args) {
-			var f = new File(getfname(getvar(args[0]).toLowerCase()));
+			var f = new File(getfname(getvar(args[0])));
 			var rp = ranked_players(args[1]);
 
 			if (!f.open('ab'))
@@ -2767,7 +2780,6 @@ rescan:
 
 	while (1) {
 		line++;
-//log(fname+':'+line);
 		if (line >= files[fname].lines.length)
 			return;
 		cl = files[fname].lines[line].replace(/^\s*/,'');
@@ -2970,6 +2982,7 @@ function mail_check(messenger)
 				lln(l);
 		}
 	}
+	con_check();
 	if (messenger) {
 		sln('');
 		more();
@@ -3192,7 +3205,6 @@ function update(skip) {
 
 		timeout_bar();
 		mail_check(true);
-		con_check();
 	}
 	dk.console.gotoxy(player.x - 1, player.y - 1);
 	foreground(15);
@@ -3334,6 +3346,8 @@ function move_player(xoff, yoff) {
 			}
 			else if (s.reffile !== '' && s.refsection !== '') {
 				run_ref(s.refsection, s.reffile);
+				player.battle = 0;
+				update_update();
 			}
 		}
 	});
@@ -3346,8 +3360,10 @@ function move_player(xoff, yoff) {
 		}
 	}
 	if (moved && !special && map.battleodds > 0 && map.reffile !== '' && map.refsection !== '') {
-		if (random(map.battleodds) === 0)
+		if (random(map.battleodds) === 0) {
 			run_ref(map.refsection, map.reffile);
+			// TODO: Is this where we clear battle?!?!
+		}
 	}
 }
 
@@ -3385,9 +3401,9 @@ function draw_box(y, title, lines, width)
 	if (width === undefined) {
 		width = displen(title) + 6;
 		lines.forEach(function(l) {
-			var l = displen(lines) + 6;
-			if (l > width)
-				l = width;
+			var len = displen(l) + 6;
+			if (len > width)
+				width = len;
 		});
 	}
 
@@ -3401,6 +3417,7 @@ function draw_box(y, title, lines, width)
 	});
 	dk.console.gotoxy(x, y + lines.length + 1);
 	lw(box_bottom(width));
+	return {width:width, y:y, x:x};
 }
 
 // Assume width of 36
@@ -3435,7 +3452,7 @@ function popup_menu(title, opts)
 	});
 
 	ch = vbar(otxt, {drawall:false, x:x + 3, y:y + 1, highlight:'`r5`0', norm:'`r1`0'});
-	return ch.cur;
+	return opts[ch.cur].ret;
 }
 
 function decorate_item(it)
@@ -3551,6 +3568,8 @@ newpage:
 								player.armournumber = 0;
 								break;
 							case 'S':
+								dk.console.attr.value = 2;
+								clearrows(12, 22);
 								ret = run_ref(items[inv[cur] - 1].refsection, 'items.ref');
 								if (items[inv[cur] - 1].useonce) {
 									player.i[inv[cur] - 1]--;
@@ -3795,6 +3814,7 @@ function offline_battle()
 				if (player.p[1] < 1) {
 					if (enm.lose_reffile !== '' && enm.lose_refname !== '')
 						run_ref(enm.lose_refname, enm.lose_reffile);
+					break;
 				}
 			}
 		}
@@ -4016,20 +4036,20 @@ function items_menu(itms, cur, buying, selling, extras, starty, endy)
 		lw('`r0`2');
 		for (i = 0; i < cnt; i++) {
 			idx = i + off;
-			if (idx >= itms.length) {
-				str = '';
-			}
-			else {
+			str = '';
+			if (idx < itms.length) {
 				it = itms[idx] - 1;
 				desc = items[it].description;
 				choices.push(((selling && (items[it].sell === false)) ? '`8  ' : '`2  ')+items[it].name);
 				if (cur === idx)
-					str = '`r1`2';
+					str += '`r1';
 				else
-					str = '`r0`2';
+					str += '`r0';
 				if (selling && items[it].sell === false)
 					str += '`8';
-				str = '  '+items[it].name;
+				else
+					str += '`2';
+				str += '  '+items[it].name;
 				if (cur === idx)
 					str += '`r0';
 				str += decorate_item(items[it]);
@@ -4253,6 +4273,16 @@ function hail()
 		op.put(false);
 		update_bar('You find `0'+op.name+'`2 sleeping like a baby. (hit a key)', true);
 		switch(hbar(2, 23, ['Leave', 'Attack', 'Give Item', 'Transfer Gold', 'Write Mail'])) {
+			case 0:
+				break;
+			case 1: // Attack
+				break;
+			case 2: // Give Item
+				break;
+			case 3: // Transfer Gold
+				break;
+			case 4: // Write Mail
+				break;
 		}
 		// TODO: Offline battles, giving things, etc...
 	}
@@ -4379,9 +4409,8 @@ function do_map()
 
 	ch = ''
 	while (ch != 'Q') {
-		if (enemy !== undefined) {
+		if (enemy !== undefined)
 			offline_battle();
-		}
 		while (!dk.console.waitkey(game.delay)) {
 			update();
 		};
@@ -4638,6 +4667,7 @@ function load_time()
 		f.truncate(0);
 		f.write(state.time+'\r\n');
 		f.close;
+		// TODO: Delete inactive players after 15 days.
 		run_ref('maint', 'maint.ref');
 	}
 }
@@ -4770,6 +4800,10 @@ if (!cfile.open('w+b'))
 killfiles.push(cfile);
 cfile.close();
 
+if (player.battle) {
+	run_ref('busy', 'gametxt.ref');
+}
+
 run_ref('startgame', 'gametxt.ref');
 
 js.on_exit('if (player !== undefined) { update_rec.onnow = 0; update_rec.busy = 0; update_rec.battle = 0; update_rec.map = player.map; update_rec.x = player.x; update_rec.y = player.y; update_rec.put(); ufile.file.close(); player.onnow = 0; player.busy = 0; player.battle = 0; player.lastsaved = savetime(); player.put(); pfile.file.close() }');