shithub: sce

Download patch

ref: 6041c007423da884fffb0ec75fafdc2da3b2266b
parent: 10c73e7824f0d79cf910152355fe3d8fe0c0018e
author: qwx <qwx@sciops.net>
date: Sat Dec 18 23:52:46 EST 2021

major refactoring: use path node coordinates everywhere

this avoids a lot of stupid errors, previously 3 different
coordinates were saved and it was never clear which ones a
function is supposed to use.

also:
- use Points more
- main map is node map, not tilemap
- mobj as first argument where applicable to homogenize usage
- assume node width and height are the same (will always be the case)
- com: don't send mobj coordinates, these will often be stale
- com: use constants to avoid problems between send/recv functions
- com: additional checks to avoid moving in place
- drw: use vectors for vis and drawlists
- map: coordinate conversion functions, try to isolate map code here
- path: switch to plain a∗ to debug pathing and movement

there are A LOT of bugs there, some new, many old

--- a/bmap.c
+++ b/bmap.c
@@ -69,60 +69,60 @@
 }
 
 u64int *
-baddr(int x, int y)
+baddr(Point p)
 {
-	x >>= Bshift;
-	x += Npad;
-	y += Npad;
-	return bmap + y * bmapwidth + x;
+	p.x >>= Bshift;
+	p.x += Npad;
+	p.y += Npad;
+	return bmap + p.y * bmapwidth + p.x;
 }
 
 u64int *
-rbaddr(int y, int x)
+rbaddr(Point p)
 {
-	x >>= Bshift;
-	x += Npad;
-	y += Npad;
-	return rbmap + y * rbmapwidth + x;
+	p.x >>= Bshift;
+	p.x += Npad;
+	p.y += Npad;
+	return rbmap + p.y * rbmapwidth + p.x;
 }
 
 static u64int *
-breduce(u64int *p, int Δp, int ofs, int w, int h, int Δw, int Δh, int left)
+breduce(u64int *b, int Δb, int ofs, Point sz, Point Δsz, int left)
 {
 	static u64int row[Nmaxsize+2];
 	int i, j;
 	u64int u, m;
 
-	m = (1 << w - 1) - 1;
+	m = (1 << sz.x - 1) - 1;
 	if(left){
-		ofs = 64 - w - Δw - ofs;
-		m <<= 63 - w + 1;
+		ofs = 64 - sz.x - Δsz.x - ofs;
+		m <<= 63 - sz.x + 1;
 	}
 	m = ~m;
-	for(i=0; i<h+Δh; i++, p+=Δp){
-		u = p[0];
+	for(i=0; i<sz.y+Δsz.y; i++, b+=Δb){
+		u = b[0];
 		if(ofs > 0){
 			if(left){
 				u >>= ofs;
-				u |= p[-1] << 64 - ofs;
+				u |= b[-1] << 64 - ofs;
 			}else{
 				u <<= ofs;
-				u |= p[1] >> 64 - ofs;
+				u |= b[1] >> 64 - ofs;
 			}
 		}
 		if(left)
-			switch(w){
+			switch(sz.x){
 			case 4: u |= u >> 1 | u >> 2 | u >> 3; break;
 			case 2: u |= u >> 1; break;
 			}
 		else
-			switch(w){
+			switch(sz.x){
 			case 4: u |= u << 1 | u << 2 | u << 3; break;
 			case 2: u |= u << 1; break;
 			}
 		u &= m;
 		row[i] = u;
-		for(j=max(i-h+1, 0); j<i; j++)
+		for(j=max(i-sz.y+1, 0); j<i; j++)
 			row[j] |= u;
 	}
 	return row;
@@ -129,50 +129,50 @@
 }
 
 u64int *
-bload(int x, int y, int w, int h, int Δw, int Δh, int left, int rot)
+bload(Point p, Point sz, Point Δsz, int left, int rot)
 {
-	int ofs, Δp;
-	u64int *p;
+	int ofs, Δb;
+	u64int *b;
 
 	if(rot){
-		p = rbaddr(x, y);
-		Δp = rbmapwidth;
-		ofs = y & Bmask;
+		b = rbaddr(p);
+		Δb = rbmapwidth;
+		ofs = p.y & Bmask;
 	}else{
-		p = baddr(x, y);
-		Δp = bmapwidth;
-		ofs = x & Bmask;
+		b = baddr(p);
+		Δb = bmapwidth;
+		ofs = p.x & Bmask;
 	}
-	return breduce(p, Δp, ofs, w, h, Δw, Δh, left);
+	return breduce(b, Δb, ofs, sz, Δsz, left);
 }
 
 void
-bset(int x, int y, int w, int h, int set)
+bset(Point p, Point sz, int set)
 {
 	int i, Δ, n;
-	u64int *p, m, m´;
+	u64int *b, m, m´;
 
-	p = baddr(x, y);
-	n = x & Bmask;
-	m = (1ULL << w) - 1 << 64 - w;
+	b = baddr(p);
+	n = p.x & Bmask;
+	m = (1ULL << sz.x) - 1 << 64 - sz.x;
 	m >>= n;
-	Δ = n + w - 64;
+	Δ = n + sz.x - 64;
 	m´ = (1ULL << Δ) - 1 << 64 - Δ;
-	for(i=0; i<h; i++, p+=bmapwidth){
-		p[0] = set ? p[0] | m : p[0] & ~m;
+	for(i=0; i<sz.y; i++, b+=bmapwidth){
+		b[0] = set ? b[0] | m : b[0] & ~m;
 		if(Δ > 0)
-			p[1] = set ? p[1] | m´ : p[1] & ~m´;
+			b[1] = set ? b[1] | m´ : b[1] & ~m´;
 	}
-	p = rbaddr(x, y);
-	n = y & Bmask;
-	m = (1ULL << h) - 1 << 64 - h;
+	b = rbaddr(p);
+	n = p.y & Bmask;
+	m = (1ULL << sz.y) - 1 << 64 - sz.y;
 	m >>= n;
-	Δ = n + h - 64;
+	Δ = n + sz.y - 64;
 	m´ = (1ULL << Δ) - 1 << 64 - Δ;
-	for(i=0; i<w; i++, p+=rbmapwidth){
-		p[0] = set ? p[0] | m : p[0] & ~m;
+	for(i=0; i<sz.x; i++, b+=rbmapwidth){
+		b[0] = set ? b[0] | m : b[0] & ~m;
 		if(Δ > 0)
-			p[1] = set ? p[1] | m´ : p[1] & ~m´;
+			b[1] = set ? b[1] | m´ : b[1] & ~m´;
 	}
 }
 
@@ -192,14 +192,14 @@
 {
 	int i;
 
-	bmapwidth = (nodemapwidth >> Bshift) + 2 * Npad;
-	bmapheight = nodemapheight + 2 * Npad;
-	rbmapwidth = (nodemapheight >> Bshift) + 2 * Npad;
-	rbmapheight = nodemapwidth + 2 * Npad;
+	bmapwidth = (mapwidth >> Bshift) + 2 * Npad;
+	bmapheight = mapheight + 2 * Npad;
+	rbmapwidth = (mapheight >> Bshift) + 2 * Npad;
+	rbmapheight = mapwidth + 2 * Npad;
 	bmap = emalloc(bmapwidth * bmapheight * sizeof *bmap);
 	rbmap = emalloc(rbmapwidth * rbmapheight * sizeof *rbmap);
 	for(i=0; i<Npad; i++){
-		memset(bmap + i * nodemapwidth, 0xff, bmapwidth * sizeof *bmap);
+		memset(bmap + i * mapwidth, 0xff, bmapwidth * sizeof *bmap);
 		memset(bmap + (bmapheight - i - 1) * bmapwidth, 0xff,
 			bmapwidth * sizeof *bmap);
 		memset(rbmap + i * rbmapwidth, 0xff, rbmapwidth * sizeof *rbmap);
--- a/com.c
+++ b/com.c
@@ -13,6 +13,11 @@
 	ushort size;
 };
 
+#define	MGATHER		"dl dl"
+#define	MMOVENEAR	"dl dl"
+#define MMOVE		"dl dd"
+#define MSTOP		"dl"
+
 static int
 vunpack(uchar *p, uchar *e, char *fmt, va_list a)
 {
@@ -74,9 +79,7 @@
 	int n;
 	Mobj reqm, reqt, *mo, *tgt;
 
-	if((n = unpack(p, e, "dldd dldd",
-	&reqm.idx, &reqm.uuid, &reqm.x, &reqm.y,
-	&reqt.idx, &reqt.uuid, &reqt.x, &reqt.y)) < 0)
+	if((n = unpack(p, e, MGATHER, &reqm.idx, &reqm.uuid, &reqt.idx, &reqt.uuid)) < 0)
 		return -1;
 	if((mo = mobjfromreq(&reqm)) == nil)
 		return -1;
@@ -90,6 +93,10 @@
 	}
 	if((tgt = mobjfromreq(&reqt)) == nil)
 		return -1;
+	if(mo == tgt){
+		werrstr("reqgather: object %M targeting itself", mo);
+		return -1;
+	}
 	if((tgt->o->f & Fresource) == 0){
 		werrstr("reqgather: target %M not a resource", tgt);
 		return -1;
@@ -103,18 +110,10 @@
 reqmovenear(uchar *p, uchar *e)
 {
 	int n;
-	Point click;
 	Mobj reqm, reqt, *mo, *tgt;
 
-	if((n = unpack(p, e, "dldd dd dldd",
-	&reqm.idx, &reqm.uuid, &reqm.x, &reqm.y,
-	&click.x, &click.y,
-	&reqt.idx, &reqt.uuid, &reqt.x, &reqt.y)) < 0)
+	if((n = unpack(p, e, MMOVENEAR, &reqm.idx, &reqm.uuid, &reqt.idx, &reqt.uuid)) < 0)
 		return -1;
-	if(eqpt(reqm.Point, reqt.Point) || eqpt(reqm.Point, click)){
-		dprint("reqmovenear: %P [%#ux,%ld] → %P [%#ux,%ld] (%P), not moving to itself\n", reqm.Point, reqm.idx, reqm.uuid, reqt.Point, reqt.idx, reqt.uuid, click);
-		return n;
-	}
 	if((mo = mobjfromreq(&reqm)) == nil)
 		return -1;
 	if((mo->o->f & Fimmutable) || mo->o->speed == 0.0){
@@ -123,11 +122,11 @@
 	}
 	if((tgt = mobjfromreq(&reqt)) == nil)
 		return -1;
-	if(click.x >= nodemapwidth || click.y >= nodemapheight){
-		werrstr("reqmovenear: invalid location %d,%d", click.x, click.y);
+	if(mo == tgt){
+		werrstr("reqmovenear: object %M targeting itself", mo);
 		return -1;
 	}
-	if(pushmovecommand(click, mo, tgt) < 0)
+	if(pushmovecommand(mo, tgt->Point, tgt) < 0)
 		return -1;
 	return n;
 }
@@ -139,26 +138,24 @@
 	Point tgt;
 	Mobj reqm, *mo;
 
-	if((n = unpack(p, e, "dldd dd",
-	&reqm.idx, &reqm.uuid, &reqm.x, &reqm.y,
-	&tgt.x, &tgt.y)) < 0)
+	if((n = unpack(p, e, MMOVE, &reqm.idx, &reqm.uuid, &tgt.x, &tgt.y)) < 0)
 		return -1;
-	if(eqpt(reqm.Point, tgt)){
-		dprint("reqmove: %P [%#ux,%ld] → %P, not moving to itself\n", reqm.Point, reqm.idx, reqm.uuid, tgt);
-		return n;
+	if(!ptinrect(tgt, Rect(0,0,mapwidth,mapheight))){
+		werrstr("reqmove: invalid target %P", tgt);
+		return -1;
 	}
 	if((mo = mobjfromreq(&reqm)) == nil)
 		return -1;
+	if(eqpt(mo->Point, tgt)){
+		werrstr("reqmove: object %M targeting itself", mo);
+		return -1;
+	}
 	if((mo->o->f & Fimmutable) || mo->o->speed == 0.0){
 		werrstr("reqmove: object %M can't move", mo);
 		return -1;
 	}
-	if(tgt.x >= nodemapwidth || tgt.y >= nodemapheight){
-		werrstr("reqmove: invalid target %d,%d", tgt.x, tgt.y);
+	if(pushmovecommand(mo, tgt, nil) < 0)
 		return -1;
-	}
-	if(pushmovecommand(tgt, mo, nil) < 0)
-		return -1;
 	return n;
 }
 
@@ -168,8 +165,7 @@
 	int n;
 	Mobj reqm, *mo;
 
-	if((n = unpack(p, e, "dldd",
-	&reqm.idx, &reqm.uuid, &reqm.x, &reqm.y)) < 0)
+	if((n = unpack(p, e, MSTOP, &reqm.idx, &reqm.uuid)) < 0)
 		return -1;
 	if((mo = mobjfromreq(&reqm)) == nil)
 		return -1;
@@ -307,9 +303,7 @@
 	Msg *m;
 
 	m = getclbuf();
-	if(packmsg(m, "h dldd dldd", CTgather,
-	mo->idx, mo->uuid, mo->x, mo->y,
-	tgt->idx, tgt->uuid, tgt->x, tgt->y) < 0){
+	if(packmsg(m, "h" MGATHER, CTgather, mo->idx, mo->uuid, tgt->idx, tgt->uuid) < 0){
 		fprint(2, "sendgather: %r\n");
 		return -1;
 	}
@@ -317,15 +311,12 @@
 }
 
 int
-sendmovenear(Mobj *mo, Point click, Mobj *tgt)
+sendmovenear(Mobj *mo, Mobj *tgt)
 {
 	Msg *m;
 
 	m = getclbuf();
-	if(packmsg(m, "h dldd dd dldd", CTmovenear,
-	mo->idx, mo->uuid, mo->x, mo->y,
-	click.x, click.y,
-	tgt->idx, tgt->uuid, tgt->x, tgt->y) < 0){
+	if(packmsg(m, "h" MMOVENEAR, CTmovenear, mo->idx, mo->uuid, tgt->idx, tgt->uuid) < 0){
 		fprint(2, "sendmovenear: %r\n");
 		return -1;
 	}
@@ -338,9 +329,7 @@
 	Msg *m;
 
 	m = getclbuf();
-	if(packmsg(m, "h dldd dd", CTmove,
-	mo->idx, mo->uuid, mo->x, mo->y,
-	tgt.x, tgt.y) < 0){
+	if(packmsg(m, "h" MMOVE, CTmove, mo->idx, mo->uuid, tgt.x, tgt.y) < 0){
 		fprint(2, "sendmove: %r\n");
 		return -1;
 	}
@@ -353,8 +342,7 @@
 	Msg *m;
 
 	m = getclbuf();
-	if(packmsg(m, "h dldd", CTstop,
-	mo->idx, mo->uuid, mo->x, mo->y) < 0){
+	if(packmsg(m, "h" MSTOP, CTstop, mo->idx, mo->uuid) < 0){
 		fprint(2, "sendstop: %r\n");
 		return -1;
 	}
--- a/dat.h
+++ b/dat.h
@@ -1,6 +1,7 @@
 typedef struct Node Node;
 typedef struct Pairheap Pairheap;
 typedef struct Attack Attack;
+typedef struct Size Size;
 typedef struct Pic Pic;
 typedef struct Pics Pics;
 typedef struct Obj Obj;
@@ -10,8 +11,8 @@
 typedef struct Mresource Mresource;
 typedef struct Mobj Mobj;
 typedef struct Mobjl Mobjl;
+typedef struct Tilepic Tilepic;
 typedef struct Tile Tile;
-typedef struct Map Map;
 typedef struct Resource Resource;
 typedef struct Team Team;
 typedef struct Cbuf Cbuf;
@@ -28,13 +29,12 @@
 	Nteam = 1 << Nteambits,
 	Teamshift = 32 - Nteambits,
 	Teamidxmask = ~(Nteam - 1 << Teamshift),
-	Tilewidth = 32,
-	Tileheight = Tilewidth,
+	Tilesz = 32,
 	Node2Tile = 4,
-	Nodewidth = Tilewidth / Node2Tile,
-	Nodeheight = Tileheight / Node2Tile,
-	Subpxshift = 16,
-	Subpxmask = (1 << Subpxshift) - 1,
+	Nodesz = Tilesz / Node2Tile,
+	Subshift = 16,
+	Submask = (1 << Subshift) - 1,
+	Pixelshift = 16 - 3,
 };
 
 struct Vector{
@@ -70,16 +70,18 @@
 	Node *from;
 	Pairheap *p;
 };
-extern Node *nodemap;
-extern int nodemapwidth, nodemapheight;
+extern Node *map;
+extern int mapwidth, mapheight;
 
-struct Pic{
-	u32int *p;
+struct Size{
 	int w;
 	int h;
-	int dx;
-	int dy;
 };
+struct Pic{
+	u32int *p;
+	Size;
+	Point Δ;
+};
 struct Pics{
 	Pic **pic;
 	int teamcol;
@@ -152,9 +154,8 @@
 };
 struct Obj{
 	char *name;
+	Size;
 	Pics pics[OSend][PTend];
-	int w;
-	int h;
 	int f;
 	Attack *atk[2];
 	int hp;
@@ -215,10 +216,7 @@
 	Command cmds[Ncmd];
 	int ctail;
 	Point;
-	int px;
-	int py;
-	int subpx;
-	int subpy;
+	Point sub;
 	Munit;
 	Mresource;
 };
@@ -229,15 +227,15 @@
 };
 extern char *statename[OSend];
 
-struct Tile{
+struct Tilepic{
 	Pic *p;
 };
-struct Map{
-	Tile *t;
+struct Tile{
+	Tilepic *t;
 	Mobjl ml;
 };
-extern Map *map;
-extern int mapwidth, mapheight;
+extern Tile *tilemap;
+extern int tilemapwidth, tilemapheight;
 
 enum{
 	Ngatheramount = 8,
--- a/drw.c
+++ b/drw.c
@@ -15,8 +15,7 @@
 static Rectangle selr;
 static Point panmax;
 static Mobj *selected[Nselect];
-static Mobj **visbuf;
-static int nvisbuf, nvis;
+static Vector vis;
 
 enum{
 	DLgndshad,
@@ -29,10 +28,8 @@
 };
 typedef struct Drawlist Drawlist;
 struct Drawlist{
-	Mobj **mo;
-	Pic **pics;
-	int n;
-	int sz;
+	Vector mobj;
+	Vector pics;
 	int noalpha;
 };
 static Drawlist drawlist[DLend] = {
@@ -55,51 +52,57 @@
 		pan.y = panmax.y;
 }
 
-void
-doselect(Point p)
+static Mobj *
+vismobj(Point p)
 {
 	int i;
+	Mobj **mp;
 
+	if((i = fbvis[p.y * fbw + p.x]) < 0)
+		return nil;
+	mp = vis.p;
+	assert(i < vis.n);
+	return mp[i];
+}
+
+void
+doselect(Point p)
+{
 	if(!ptinrect(p, selr))
 		return;
 	p = divpt(subpt(p, selr.min), scale);
-	i = fbvis[p.y * fbw + p.x];
-	selected[0] = i == -1 ? nil : visbuf[i];
+	selected[0] = vismobj(p);
 }
 
 void
 doaction(Point p, int clearcmds)
 {
-	int i;
-	Point vp;
-	Mobj *mo, *it;
+	Mobj *mo, *tgt;
 
-	it = selected[0];
-	if(it == nil || it->o->f & Fimmutable || !ptinrect(p, selr))
+	mo = selected[0];
+	if(mo == nil || mo->o->f & Fimmutable || !ptinrect(p, selr))
 		return;
-	vp = divpt(subpt(p, selr.min), scale);
-	i = fbvis[vp.y * fbw + vp.x];
-	mo = i == -1 ? nil : visbuf[i];
-	p = divpt(addpt(subpt(p, selr.min), pan), scale);
-	p.x /= Nodewidth;
-	p.y /= Nodeheight;
-	if(nodemapwidth - p.x < it->o->w || nodemapheight - p.y < it->o->h){
-		dprint("doaction: %M destination beyond map edge\n", it);
+	p = subpt(p, selr.min);
+	tgt = vismobj(divpt(p, scale));
+	p = divpt(addpt(p, pan), scale);
+	p = divpt(p, Nodesz);
+	if(p.x + mo->o->w > mapwidth || p.y + mo->o->h > mapheight){
+		dprint("doaction: %M target %P beyond map edge\n", mo, p);
 		return;
 	}
-	if(mo == it || eqpt(it->Point, p)){
-		dprint("doaction: %M targeting itself\n", it);
+	if(tgt == mo || eqpt(mo->Point, p)){
+		dprint("doaction: %M targeting moself\n", mo);
 		return;
 	}
 	if(clearcmds)
-		sendstop(it);
-	if(mo != nil){
-		if((mo->o->f & Fresource) && (it->o->f & Fgather))
-			sendgather(it, mo);
+		sendstop(mo);
+	if(tgt != nil){
+		if((tgt->o->f & Fresource) && (mo->o->f & Fgather))
+			sendgather(mo, tgt);
 		else
-			sendmovenear(it, p, mo);
+			sendmovenear(mo, tgt);
 	}else
-		sendmove(it, p);
+		sendmove(mo, p);
 }
 
 static void
@@ -136,62 +139,57 @@
 	string(screen, p, display->white, ZP, font, s);
 }
 
-static int
-addvis(Mobj *mo)
+static void
+clearvis(void)
 {
-	int i;
-
-	if((i = nvis++) >= nvisbuf){
-		visbuf = erealloc(visbuf, (nvisbuf + 16) * sizeof *visbuf,
-			nvisbuf * sizeof *visbuf);
-		nvisbuf += 16;
-	}
-	visbuf[i] = mo;
-	return i;
+	clearvec(&vis, sizeof(Mobj*));
 }
 
-static void
-clearvis(void)
+static int
+addvis(Mobj *mo)
 {
-	if(visbuf != nil)
-		memset(visbuf, 0, nvisbuf * sizeof *visbuf);
-	nvis = 0;
+	pushvec(&vis, &mo, sizeof mo);
+	return vis.n - 1;
 }
 
 static int
-boundpic(Rectangle *r, u32int **q)
+boundpic(Rectangle *rp, Point o, u32int **q)
 {
 	int w;
+	Rectangle r;
 
-	r->min.x -= pan.x / scale;
-	r->min.y -= pan.y / scale;
-	if(r->min.x + r->max.x < 0 || r->min.x >= fbw
-	|| r->min.y + r->max.y < 0 || r->min.y >= fbh)
+	r = *rp;
+	r.min = addpt(r.min, o);
+	r.min.x -= pan.x / scale;
+	r.min.y -= pan.y / scale;
+	if(r.min.x + r.max.x < 0 || r.min.x >= fbw
+	|| r.min.y + r.max.y < 0 || r.min.y >= fbh)
 		return -1;
-	w = r->max.x;
-	if(r->min.x < 0){
+	w = r.max.x;
+	if(r.min.x < 0){
 		if(q != nil)
-			*q -= r->min.x;
-		r->max.x += r->min.x;
-		r->min.x = 0;
+			*q -= r.min.x;
+		r.max.x += r.min.x;
+		r.min.x = 0;
 	}
-	if(r->min.x + r->max.x > fbw)
-		r->max.x -= r->min.x + r->max.x - fbw;
-	if(r->min.y < 0){
+	if(r.min.x + r.max.x > fbw)
+		r.max.x -= r.min.x + r.max.x - fbw;
+	if(r.min.y < 0){
 		if(q != nil)
-			*q -= w * r->min.y;
-		r->max.y += r->min.y;
-		r->min.y = 0;
+			*q -= w * r.min.y;
+		r.max.y += r.min.y;
+		r.min.y = 0;
 	}
-	if(r->min.y + r->max.y > fbh)
-		r->max.y -= r->min.y + r->max.y - fbh;
-	r->min.x *= scale;
-	r->max.x *= scale;
+	if(r.min.y + r.max.y > fbh)
+		r.max.y -= r.min.y + r.max.y - fbh;
+	r.min.x *= scale;
+	r.max.x *= scale;
+	*rp = r;
 	return 0;
 }
 
 static void
-drawpic(int x, int y, Pic *pic, int ivis)
+drawpic(Point o, Pic *pic, int ivis)
 {
 	int n, Δp, Δsp, Δq;
 	u32int v, *p, *e, *sp, *q;
@@ -200,8 +198,8 @@
 	if(pic->p == nil)
 		sysfatal("drawpic: empty pic");
 	q = pic->p;
-	r = Rect(x + pic->dx, y + pic->dy, pic->w, pic->h);
-	if(boundpic(&r, &q) < 0)
+	r = Rect(pic->Δ.x, pic->Δ.y, pic->w, pic->h);
+	if(boundpic(&r, o, &q) < 0)
 		return;
 	Δq = pic->w - r.max.x / scale;
 	p = fb + r.min.y * fbws + r.min.x;
@@ -226,18 +224,18 @@
 }
 
 static void
-drawpicalpha(int x, int y, Pic *pic)
+drawpicalpha(Point o, Pic *pic)
 {
 	int n, Δp, Δq;
 	u8int k, a, b;
-	u32int o, A, B, *p, *e, *q;
+	u32int f, A, B, *p, *e, *q;
 	Rectangle r;
 
 	if(pic->p == nil)
-		sysfatal("drawpic: empty pic");
+		sysfatal("drawpicalpha: empty pic");
 	q = pic->p;
-	r = Rect(x + pic->dx, y + pic->dy, pic->w, pic->h);
-	if(boundpic(&r, &q) < 0)
+	r = Rect(pic->Δ.x, pic->Δ.y, pic->w, pic->h);
+	if(boundpic(&r, o, &q) < 0)
 		return;
 	Δq = pic->w - r.max.x / scale;
 	p = fb + r.min.y * fbws + r.min.x;
@@ -251,9 +249,9 @@
 			for(n=0; n<24; n+=8){
 				a = A >> n;
 				b = B >> n;
-				o = k * (a - b);
-				o = (o + 1 + (o >> 8)) >> 8;
-				B = B & ~(0xff << n) | (o + b & 0xff) << n;
+				f = k * (a - b);
+				f = (f + 1 + (f >> 8)) >> 8;
+				B = B & ~(0xff << n) | (f + b & 0xff) << n;
 			}
 			for(n=0; n<scale; n++)
 				*p++ = B;
@@ -264,14 +262,14 @@
 }
 
 void
-compose(int x, int y, u32int c)
+compose(Point o, u32int c)
 {
 	int n, Δp;
 	u32int v, *p, *e;
 	Rectangle r;
 
-	r = Rect(x * Nodewidth, y * Nodeheight, Nodewidth, Nodeheight);
-	if(boundpic(&r, nil) < 0)
+	r = Rpt(ZP, Pt(Nodesz, Nodesz));
+	if(boundpic(&r, o, nil) < 0)
 		return;
 	p = fb + r.min.y * fbws + r.min.x;
 	Δp = fbws - r.max.x;
@@ -329,54 +327,31 @@
 {
 	Drawlist *dl;
 
-	for(dl=drawlist; dl<drawlist+DLend; dl++)
-		dl->n = 0;
+	for(dl=drawlist; dl<drawlist+DLend; dl++){
+		clearvec(&dl->mobj, sizeof(Mobj*));
+		clearvec(&dl->pics, sizeof(Pic*));
+	}
 }
 
 static void
-drawmobjs(void)
-{
-	int n;
-	Mobj *mo;
-	Drawlist *dl;
-
-	for(dl=drawlist; dl<drawlist+DLend; dl++)
-		for(n=0; n<dl->n; n++){
-			mo = dl->mo[n];
-			if(dl->noalpha)
-				drawpic(mo->px, mo->py, dl->pics[n], addvis(mo));
-			else
-				drawpicalpha(mo->px, mo->py, dl->pics[n]);
-		}
-}
-
-static void
 addpic(Drawlist *dl, Mobj *mo, int type)
 {
-	int n;
 	Pic *p;
 
 	if((p = frm(mo, type)) == nil)
 		return;
-	if(dl->n >= dl->sz){
-		n = dl->sz * sizeof *dl->pics;
-		dl->pics = erealloc(dl->pics, n + 16 * sizeof *dl->pics, n);
-		dl->mo = erealloc(dl->mo, n + 16 * sizeof *dl->mo, n);
-		dl->sz += 16;
-	}
-	n = dl->n++;
-	dl->pics[n] = p;
-	dl->mo[n] = mo;
+	pushvec(&dl->mobj, &mo, sizeof mo);
+	pushvec(&dl->pics, &p, sizeof p);
 }
 
 static void
-addmobjs(Map *m)
+addmobjs(Tile *t)
 {
 	int air;
 	Mobj *mo;
 	Mobjl *ml;
 
-	for(ml=m->ml.l; ml!=&m->ml; ml=ml->l){
+	for(ml=t->ml.l; ml!=&t->ml; ml=ml->l){
 		mo = ml->mo;
 		air = mo->o->f & Fair;
 		addpic(drawlist + (air ? DLairshad : DLgndshad), mo, PTshadow);
@@ -386,44 +361,65 @@
 	}
 }
 
-static Rectangle
-setdrawrect(void)
+static void
+drawmobjs(void)
 {
+	int n;
+	Mobj *mo, **mp;
+	Pic **pp;
+	Drawlist *dl;
+
+	for(dl=drawlist; dl<drawlist+DLend; dl++)
+		for(mp=dl->mobj.p, pp=dl->pics.p, n=0; n<dl->mobj.n; n++, mp++, pp++){
+			mo = *mp;
+			if(dl->noalpha)
+				drawpic(Pt(mo->sub.x >> Pixelshift,
+					mo->sub.y >> Pixelshift), *pp, addvis(mo));
+			else
+				drawpicalpha(Pt(mo->sub.x >> Pixelshift,
+					mo->sub.y >> Pixelshift), *pp);
+		}
+}
+
+static void
+mapdrawrect(Rectangle *rp)
+{
 	Rectangle r;
 
-	r.min.x = pan.x / scale / Tilewidth;
-	r.min.y = pan.y / scale / Tileheight;
-	r.max.x = r.min.x + (pan.x / scale % Tilewidth != 0);
-	r.max.x += fbw / Tilewidth + (fbw % Tilewidth != 0);
-	if(r.max.x > mapwidth)
-		r.max.x = mapwidth;
-	r.max.y = r.min.y + (pan.y / scale % Tileheight != 0);
-	r.max.y += fbh / Tileheight + (fbh % Tilewidth != 0);
-	if(r.max.y > mapheight)
-		r.max.y = mapheight;
+	r.min = divpt(pan, scale);
+	r.min = divpt(r.min, Tilesz);
+	r.max.x = r.min.x + (pan.x / scale % Tilesz != 0);
+	r.max.x += fbw / Tilesz + (fbw % Tilesz != 0);
+	if(r.max.x > tilemapwidth)
+		r.max.x = tilemapwidth;
+	r.max.y = r.min.y + (pan.y / scale % Tilesz != 0);
+	r.max.y += fbh / Tilesz + (fbh % Tilesz != 0);
+	if(r.max.y > tilemapheight)
+		r.max.y = tilemapheight;
 	/* enlarge window to capture units overlapping multiple tiles;
 	 * seems like the easiest way to take this into account */
 	r.min.x = max(r.min.x - 4, 0);
 	r.min.y = max(r.min.y - 4, 0);
-	return r;
+	*rp = r;
 }
 
 void
 redraw(void)
 {
-	int x, y;
+	Point p;
 	Rectangle r;
-	Map *m;
+	Tile *t;
 
 	clearvis();
 	clearlists();
-	r = setdrawrect();
-	for(y=r.min.y, m=map+y*mapwidth+r.min.x; y<r.max.y; y++){
-		for(x=r.min.x; x<r.max.x; x++, m++){
-			drawpic(x*Tilewidth, y*Tileheight, m->t->p, -1);
-			addmobjs(m);
+	mapdrawrect(&r);
+	t = tilemap + p.y * tilemapwidth + r.min.x;
+	for(p.y=r.min.y; p.y<r.max.y; p.y++){
+		for(p.x=r.min.x; p.x<r.max.x; p.x++, t++){
+			drawpic(mulpt(p, Tilesz), t->t->p, -1);
+			addmobjs(t);
 		}
-		m += mapwidth - (r.max.x - r.min.x);
+		t += tilemapwidth - (r.max.x - r.min.x);
 	}
 	drawmobjs();
 	if(debugmap)
@@ -443,13 +439,13 @@
 void
 resetfb(void)
 {
-	fbws = min(nodemapwidth * Nodewidth * scale, Dx(screen->r));
-	fbh = min(nodemapheight * Nodeheight * scale, Dy(screen->r));
+	fbws = min(mapwidth * Nodesz * scale, Dx(screen->r));
+	fbh = min(mapheight * Nodesz * scale, Dy(screen->r));
 	selr = Rpt(screen->r.min, addpt(screen->r.min, Pt(fbws, fbh)));
 	p0 = Pt(screen->r.min.x + 8, screen->r.max.y - 3 * font->height);
 	p0.y -= (p0.y - screen->r.min.y) % scale;
-	panmax.x = max(Nodewidth * nodemapwidth * scale - Dx(screen->r), 0);
-	panmax.y = max(Nodeheight * nodemapheight * scale - Dy(screen->r), 0);
+	panmax.x = max(Nodesz * mapwidth * scale - Dx(screen->r), 0);
+	panmax.y = max(Nodesz * mapheight * scale - Dy(screen->r), 0);
 	if(p0.y < selr.max.y){
 		panmax.y += selr.max.y - p0.y;
 		fbh -= selr.max.y - p0.y;
--- a/fns.h
+++ b/fns.h
@@ -4,17 +4,21 @@
 int	parsemsg(Msg*);
 void	endmsg(Msg*);
 int	sendgather(Mobj*, Mobj*);
-int	sendmovenear(Mobj*, Point, Mobj*);
+int	sendmovenear(Mobj*, Mobj*);
 int	sendstop(Mobj*);
 int	sendmove(Mobj*, Point);
 int	sendpause(void);
 void	stepsnd(void);
 void	initsnd(void);
+void	setpos(Mobj*, Point);
+void	setsubpos(Mobj*, Point);
+void	snaptomapgrid(Mobj*);
+Tile*	tilepos(Point);
 void	linktomap(Mobj*);
 int	pushreturncommand(Mobj*, Mobj*);
 int	pushgathercommand(Mobj*, Mobj*);
 int	pushmove(Mobj*);
-int	pushmovecommand(Point, Mobj*, Mobj*);
+int	pushmovecommand(Mobj*, Point, Mobj*);
 void	resourcestate(Mobj*);
 void	depleteresource(Mobj*, int);
 void	freezefrm(Mobj*, int);
@@ -36,7 +40,7 @@
 void	dopan(Point);
 void	doselect(Point);
 void	doaction(Point, int);
-void	compose(int, int, u32int);
+void	compose(Point, u32int);
 void	redraw(void);
 void	updatefb(void);
 void	resetfb(void);
@@ -43,19 +47,20 @@
 void	drawfb(void);
 void	initimg(void);
 void	initfs(void);
+double	eucdist(Point, Point);
 double	octdist(Point, Point);
-void	setgoal(Point*, Mobj*, Mobj*);
-Mobj*	unitat(int, int);
+void	setgoal(Mobj*, Point*, Mobj*);
+Mobj*	unitat(Point);
 int	isblocked(Point, Obj*);
 void	markmobj(Mobj*, int);
 int	isnextto(Mobj*, Mobj*);
-int	findpath(Point, Mobj*);
+int	findpath(Mobj*, Point);
 void	drawnodemap(Rectangle, Mobj*);
-Mobj*	mapspawn(Point, Obj*);
+Mobj*	mapspawn(Obj*, Point);
 void	initmap(void);
 Mobj*	derefmobj(int, long);
-int	spawnunit(Point, Obj*, int);
-int	spawnresource(Point, Obj*, int);
+int	spawnunit(Obj*, Point, int);
+int	spawnresource(Obj*, Point, int);
 void	nukequeue(Pairheap**);
 Pairheap*	popqueue(Pairheap**);
 void	decreasekey(Pairheap*, double, Pairheap**);
@@ -62,10 +67,10 @@
 void	pushqueue(Node*, Pairheap**);
 int	lsb(uvlong);
 int	msb(uvlong);
-u64int*	baddr(int, int);
-u64int*	rbaddr(int, int);
-u64int*	bload(int, int, int, int, int, int, int, int);
-void	bset(int, int, int, int, int);
+u64int*	baddr(Point);
+u64int*	rbaddr(Point);
+u64int*	bload(Point, Point, Point, int, int);
+void	bset(Point, Point, int);
 void	initbmap(void);
 int	mobjfmt(Fmt*);
 void	dprint(char *, ...);
--- a/fs.c
+++ b/fs.c
@@ -39,7 +39,7 @@
 };
 struct Tilel{
 	int id;
-	Tile *t;
+	Tilepic *t;
 	Tilel *l;
 };
 static Tilel tilel0 = {.l = &tilel0}, *tilel = &tilel0;
@@ -80,8 +80,8 @@
 	pic->p = p;
 	pic->w = dx;
 	pic->h = dy;
-	pic->dx = i->r.min.x;
-	pic->dy = i->r.min.y;
+	pic->Δ.x = i->r.min.x;
+	pic->Δ.y = i->r.min.y;
 	m = i->depth / 8;
 	freeimage(i);
 	s = b;
@@ -215,7 +215,7 @@
 	return pl->p;
 }
 
-static Tile *
+static Tilepic *
 pushtile(int id)
 {
 	Tilel *tl;
@@ -248,6 +248,7 @@
 		switch(*fmt++){
 		default: sysfatal("unknown format %c", fmt[-1]);
 		case 0: return;
+		case ' ': break;
 		case 'd':
 			if((n = strtol(*fld++, nil, 0)) < 0)
 				sysfatal("vunpack: illegal positive integer %d", n);
@@ -301,7 +302,7 @@
 				sysfatal("vunpack: empty tile");
 			if((n = strtol(s, nil, 0)) <= 0)
 				sysfatal("vunpack: illegal tile index %d", n);
-			*va_arg(a, Tile**) = pushtile(n);
+			*va_arg(a, Tilepic**) = pushtile(n);
 			break;
 		}
 	}
@@ -323,7 +324,7 @@
 	Obj *o, **os;
 	Resource *r;
 
-	unpack(fld, "ro", &r, &o);
+	unpack(fld, "r o", &r, &o);
 	if(o->res != nil && o->res != r)
 		sysfatal("readgather %s: obj %s already assigned to %s",
 			r->name, o->name, o->res->name);
@@ -362,16 +363,16 @@
 readmap(char **fld, int n, Table *tab)
 {
 	int x;
-	Map *m;
+	Tile *t;
 
 	if(tab->row == 0){
 		tab->ncol = n;
-		mapwidth = n;
-		map = emalloc(mapheight * n * sizeof *map);
+		tilemapwidth = n;
+		tilemap = emalloc(tilemapheight * n * sizeof *tilemap);
 	}
-	m = map + tab->row * mapwidth;
-	for(x=0; x<n; x++, m++)
-		unpack(fld++, "t", &m->t);
+	t = tilemap + tab->row * tilemapwidth;
+	for(x=0; x<n; x++, t++)
+		unpack(fld++, "t", &t->t);
 }
 
 static void
@@ -383,7 +384,8 @@
 	if(objp == nil)
 		objp = emalloc(nobjp * sizeof *objp);
 	op = objp + tab->row;
-	unpack(fld, "oddd", &op->o, &op->x, &op->y, &arg);
+	unpack(fld, "o dd d", &op->o, &op->x, &op->y, &arg);
+	op->Point = mulpt(op->Point, Node2Tile);
 	if(op->o->f & Fresource){
 		op->team = 0;
 		op->resource = 1;
@@ -443,10 +445,11 @@
 		obj = emalloc(nobj * sizeof *obj);
 	o = obj + tab->row;
 	o->name = estrdup(*fld++);
-	unpack(fld, "ddddddddddaaffff", &o->f, &o->w, &o->h,
+	unpack(fld, "d dd ddd dddd aa ffff", &o->f, &o->w, &o->h,
 		&o->hp, &o->def, &o->vis,
 		o->cost, o->cost+1, o->cost+2, &o->time,
-		o->atk, o->atk+1, &o->speed, &o->accel, &o->halt, &o->turn);
+		o->atk, o->atk+1,
+		&o->speed, &o->accel, &o->halt, &o->turn);
 	if(o->f & Fresource)
 		o->f |= Fimmutable;
 	else{
@@ -453,9 +456,9 @@
 		o->accel /= 256.0;
 		o->halt /= 256.0;
 		/* halting distance in path node units */
-		o->halt /= Nodewidth;
+		o->halt /= Nodesz;
 	}
-	if(o->w < 1 || o->h < 1)
+	if(o->w < 1 || o->h < 1 || o->w > 4 * Node2Tile || o->h > 4 * Node2Tile)
 		sysfatal("readobj: %s invalid dimensions %d,%d", o->name, o->w, o->h);
 }
 
@@ -469,7 +472,7 @@
 
 	if(n < 4)
 		sysfatal("readspr %s: %d fields < 4 mandatory columns", o->name, n);
-	unpack(fld, "odd", &o, &type, &nr);
+	unpack(fld, "o dd", &o, &type, &nr);
 	fld += 3;
 	n -= 3;
 	state = type & PFstatemask;
@@ -519,7 +522,7 @@
 	[TBresource] {"resource", readresource, -1, &nresource},
 	[TBspawn] {"spawn", readspawn, -1, nil},
 	[TBtileset] {"tileset", readtileset, 1, nil},
-	[TBmap] {"map", readmap, -1, &mapheight},
+	[TBmap] {"map", readmap, -1, &tilemapheight},
 	[TBspr] {"spr", readspr, -1, nil},
 	[TBgather] {"gather", readgather, -1, nil},
 };
@@ -605,17 +608,15 @@
 initmapobj(void)
 {
 	Objp *op;
-	Map *m;
-	Point p;
+	Tile *t;
 
-	for(m=map; m<map+mapwidth*mapheight; m++)
-		m->ml.l = m->ml.lp = &m->ml;
+	for(t=tilemap; t<tilemap+tilemapwidth*tilemapheight; t++)
+		t->ml.l = t->ml.lp = &t->ml;
 	for(op=objp; op<objp+nobjp; op++){
-		p = mulpt(op->Point, Node2Tile);
 		if(op->resource){
-			if(spawnresource(p, op->o, op->amount) < 0)
+			if(spawnresource(op->o, op->Point, op->amount) < 0)
 				sysfatal("initmapobj: %s at %P: %r", op->o->name, op->Point);
-		}else if(spawnunit(p, op->o, op->team) < 0)
+		}else if(spawnunit(op->o, op->Point, op->team) < 0)
 			sysfatal("initmapobj: %s team %d at %P: %r", op->o->name, op->team, op->Point);
 	}
 	free(objp);
@@ -649,9 +650,10 @@
 		sysfatal("checkdb: no tileset defined");
 	if(nresource != Nresource)
 		sysfatal("checkdb: incomplete resource specification");
-	if(mapwidth % 16 != 0 || mapheight % 16 != 0 || mapwidth * mapheight == 0)
+	if(tilemapwidth % 16 != 0 || tilemapheight % 16 != 0
+	|| tilemapwidth * tilemapheight <= 0)
 		sysfatal("checkdb: map size %d,%d not in multiples of 16",
-			mapwidth, mapheight);
+			tilemapwidth, tilemapheight);
 	if(nteam < 2)
 		sysfatal("checkdb: not enough teams");
 }
--- a/map.c
+++ b/map.c
@@ -4,11 +4,52 @@
 #include "dat.h"
 #include "fns.h"
 
-Map *map;
+Tile *tilemap;
+int tilemapwidth, tilemapheight;
+Node *map;
 int mapwidth, mapheight;
-Node *nodemap;
-int nodemapwidth, nodemapheight;
 
+void
+setpos(Mobj *mo, Point p)
+{
+	assert(p.x < mapwidth && p.y < mapheight);
+	mo->Point = p;
+	mo->sub.x = mo->x << Subshift;
+	mo->sub.y = mo->y << Subshift;
+}
+
+void
+setsubpos(Mobj *mo, Point p)
+{
+	mo->sub = p;
+	mo->x = p.x >> Subshift;
+	mo->y = p.y >> Subshift;
+}
+
+void
+snaptomapgrid(Mobj *mo)
+{
+	markmobj(mo, 0);
+	setpos(mo, mo->Point);
+	markmobj(mo, 1);
+}
+
+Tile *
+tilepos(Point p)
+{
+	p = divpt(p, Node2Tile);
+	return tilemap + p.y * tilemapwidth + p.x;
+}
+
+void
+linktomap(Mobj *mo)
+{
+	Tile *t;
+
+	t = tilepos(mo->Point);
+	mo->mapl = linkmobj(mo->o->f & Fair ? t->ml.lp : &t->ml, mo, mo->mapl);
+}
+
 static void
 updatemap(Mobj *mo)
 {
@@ -15,7 +56,7 @@
 	Mobj *bmo;
 
 	if(isblocked(mo->Point, mo->o)){
-		bmo = unitat(mo->x, mo->y);
+		bmo = unitat(mo->Point);
 		sysfatal("markmobj: attempt to place %s at %P, non-free block having %s at %P",
 			mo->o->name, mo->Point, bmo->o->name, bmo->Point);
 	}
@@ -26,24 +67,24 @@
 static int
 findspawn(Point *pp, int ofs, Obj *o, Obj *spawn)
 {
-	int minx, miny, maxx, maxy;
+	Rectangle r;
 	Point p;
 
 	p = *pp;
-	minx = p.x - (ofs+1) * o->w;
-	miny = p.y - (ofs+1) * o->h;
-	maxx = p.x + spawn->w + ofs * o->w;
-	maxy = p.y + spawn->h + ofs * o->h;
-	for(p.x=minx+o->w, p.y=maxy; p.x<maxx; p.x++)
+	r.min.x = p.x - (ofs+1) * o->w;
+	r.min.y = p.y - (ofs+1) * o->h;
+	r.max.x = p.x + spawn->w + ofs * o->w;
+	r.max.y = p.y + spawn->h + ofs * o->h;
+	for(p.x=r.min.x+o->w, p.y=r.max.y; p.x<r.max.x; p.x++)
 		if(!isblocked(p, o))
 			goto found;
-	for(p.x=maxx, p.y=maxy; p.y>miny; p.y--)
+	for(p.x=r.max.x, p.y=r.max.y; p.y>r.min.y; p.y--)
 		if(!isblocked(p, o))
 			goto found;
-	for(p.x=maxx, p.y=miny; p.x>minx; p.x--)
+	for(p.x=r.max.x, p.y=r.min.y; p.x>r.min.x; p.x--)
 		if(!isblocked(p, o))
 			goto found;
-	for(p.x=minx, p.y=miny; p.y<=maxy; p.y++)
+	for(p.x=r.min.x, p.y=r.min.y; p.y<=r.max.y; p.y++)
 		if(!isblocked(p, o))
 			goto found;
 	return -1;
@@ -57,7 +98,7 @@
 {
 	int n;
 	Point p;
-	Map *m;
+	Tile *t;
 	Mobjl *ml;
 	Mobj *mo;
 	Obj **os;
@@ -64,13 +105,17 @@
 
 	p = *pp;
 	if(o->f & (Fbuild|Fimmutable)){
+		if((p.x & Node2Tile - 1) || (p.y & Node2Tile - 1)){
+			werrstr("getspawn: unaligned building placement %P", p);
+			return -1;
+		}
 		if(isblocked(p, o)){
 			werrstr("getspawn: building placement at %P blocked", p);
 			return -1;
 		}
 	}else{
-		m = map + p.y / Node2Tile * mapwidth + p.x / Node2Tile;
-		for(mo=nil, ml=m->ml.l; ml!=&m->ml; ml=ml->l){
+		t = tilepos(p);
+		for(mo=nil, ml=t->ml.l; ml!=&t->ml; ml=ml->l){
 			mo = ml->mo;
 			for(os=mo->o->spawn, n=mo->o->nspawn; n>0; n--, os++)
 				if(*os == o)
@@ -78,7 +123,7 @@
 			if(n > 0)
 				break;
 		}
-		if(ml == &m->ml){
+		if(ml == &t->ml){
 			werrstr("getspawn: no spawn object at %P", p);
 			return -1;
 		}
@@ -96,23 +141,15 @@
 }
 
 Mobj *
-mapspawn(Point p, Obj *o)
+mapspawn(Obj *o, Point p)
 {
 	Mobj *mo;
 
-	if(o->f & (Fbuild|Fimmutable) && (p.x & Node2Tile-1 || p.y & Node2Tile-1)){
-		werrstr("mapspawn: building spawn %P not aligned to tile map", p);
-		return nil;
-	}
 	if(getspawn(&p, o) < 0)
 		return nil;
 	mo = emalloc(sizeof *mo);
 	mo->uuid = lrand();
-	mo->Point = p;
-	mo->px = p.x * Nodewidth;
-	mo->py = p.y * Nodeheight;
-	mo->subpx = mo->px << Subpxshift;
-	mo->subpy = mo->py << Subpxshift;
+	setpos(mo, p);
 	mo->o = o;
 	updatemap(mo);
 	return mo;
@@ -121,8 +158,8 @@
 void
 initmap(void)
 {
-	nodemapwidth = mapwidth * Node2Tile;
-	nodemapheight = mapheight * Node2Tile;
-	nodemap = emalloc(nodemapwidth * nodemapheight * sizeof *nodemap);
+	mapwidth = tilemapwidth * Node2Tile;
+	mapheight = tilemapheight * Node2Tile;
+	map = emalloc(mapwidth * mapheight * sizeof *map);
 	initbmap();
 }
--- a/path.c
+++ b/path.c
@@ -61,39 +61,38 @@
 void
 drawnodemap(Rectangle r, Mobj *sel)
 {
-	int x, y;
 	u64int *row, v, m;
-	Point *p;
-	Path *pp;
+	Point p, *pp;
+	Path *path;
 	Node *n;
 
 	r = Rpt(mulpt(r.min, Node2Tile), mulpt(r.max, Node2Tile));
-	for(y=r.min.y, n=nodemap+y*nodemapwidth+r.min.x; y<r.max.y; y++){
-		x = r.min.x;
-		row = baddr(x, y);
+	for(p.y=r.min.y, n=map+p.y*mapwidth+r.min.x; p.y<r.max.y; p.y++){
+		p.x = r.min.x;
+		row = baddr(p);
 		v = *row++;
-		m = 1ULL << 63 - (x & Bmask);
-		for(; x<r.max.x; x++, n++, m>>=1){
+		m = 1ULL << 63 - (p.x & Bmask);
+		for(; p.x<r.max.x; p.x++, n++, m>>=1){
 			if(m == 0){
 				v = *row++;
 				m = 1ULL << 63;
 			}
 			if(v & m)
-				compose(x, y, 0xff0000);
+				compose(mulpt(p, Nodesz), 0xff0000);
 			if(n->closed)
-				compose(x, y, 0x000077);
+				compose(mulpt(p, Nodesz), 0x000077);
 			else if(n->open)
-				compose(x, y, 0x007777);
+				compose(mulpt(p, Nodesz), 0x007777);
 		}
-		n += nodemapwidth - (r.max.x - r.min.x);
+		n += mapwidth - (r.max.x - r.min.x);
 	}
 	if(sel != nil){
-		pp = &sel->path;
-		if(pp->step == nil)
+		path = &sel->path;
+		if(path->step == nil)
 			return;
-		for(p=pp->step; p>=pp->moves.p; p--)
-			compose(p->x / Nodewidth, p->y / Nodeheight, 0x00ff00);
-		compose(pp->target.x, pp->target.y, 0x00ff77);
+		for(pp=path->step; pp>=path->moves.p; pp--)
+			compose(mulpt(*pp, Nodesz), 0x00ff00);
+		compose(mulpt(path->target, Nodesz), 0x00ff77);
 	}
 }
 
@@ -101,7 +100,7 @@
 clearpath(void)
 {
 	nukequeue(&queue);
-	memset(nodemap, 0, nodemapwidth * nodemapheight * sizeof *nodemap);
+	memset(map, 0, mapwidth * mapheight * sizeof *map);
 	nearest = nil;
 }
 
@@ -112,34 +111,32 @@
 
 	if(o->f & Fair)
 		return 0;
-	row = bload(p.x, p.y, o->w, o->h, 0, 0, 0, 0);
+	row = bload(p, Pt(o->w, o->h), ZP, 0, 0);
 	return (*row & 1ULL << 63) != 0;
 }
 
 Mobj *
-unitat(int px, int py)
+unitat(Point p)
 {
-	int x, y;
+	Point mp;
 	Rectangle r, mr;
-	Map *m;
+	Tile *t;
 	Mobjl *ml;
 	Mobj *mo;
 
-	x = px / Node2Tile;
-	y = py / Node2Tile;
-	r = Rect(x-4, y-4, x, y);
-	for(; y>=r.min.y; y--)
-		for(x=r.max.x, m=map+y*mapwidth+x; x>=r.min.x; x--)
-			for(ml=m->ml.l; ml!=&m->ml; ml=ml->l){
+	mp = divpt(p, Node2Tile);
+	r = Rpt(subpt(mp, Pt(4, 4)), mp);
+	for(; mp.y>=r.min.y; mp.y--){
+		mp.x = r.max.x;
+		t = tilemap + mp.y * tilemapwidth + mp.x;
+		for(; mp.x>=r.min.x; mp.x--, t--)
+			for(ml=t->ml.l; ml!=&t->ml; ml=ml->l){
 				mo = ml->mo;
-				mr.min.x = mo->x;
-				mr.min.y = mo->y;
-				mr.max.x = mr.min.x + mo->o->w;
-				mr.max.y = mr.min.y + mo->o->h;
-				if(px >= mo->x && px <= mo->x + mo->o->w
-				&& py >= mo->y && py <= mo->y + mo->o->h)
+				mr = Rect(mo->x, mo->y, mo->x+mo->o->w, mo->y+mo->o->h);
+				if(ptinrect(p, mr))
 					return mo;
 			}
+	}
 	return nil;
 }
 
@@ -146,26 +143,29 @@
 void
 markmobj(Mobj *mo, int set)
 {
-	int w, h;
+	Point sz;
 
 	if(mo->o->f & Fair)
 		return;
-	w = mo->o->w;
-	if((mo->subpx & Subpxmask) != 0 && mo->x != (mo->px + 1) / Nodewidth)
-		w++;
-	h = mo->o->h;
-	if((mo->subpy & Subpxmask) != 0 && mo->y != (mo->py + 1) / Nodewidth)
-		h++;
-	bset(mo->x, mo->y, w, h, set);
+	sz = Pt(mo->o->w, mo->o->h);
+/*
+	if((mo->sub.x & Submask) != 0 && mo->x != ((mo->sub.x>>Pixelshift) + 1) / Nodesz)
+		sz.x++;
+	if((mo->sub.y & Submask) != 0 && mo->y != ((mo->sub.y>>Pixelshift) + 1) / Nodesz)
+		sz.y++;
+*/
+	sz.x += (mo->sub.x & Submask) != 0 && mo->x != mo->sub.x + (1<<Pixelshift) >> Subshift;
+	sz.y += (mo->sub.y & Submask) != 0 && mo->y != mo->sub.y + (1<<Pixelshift) >> Subshift;
+	bset(mo->Point, sz, set);
 }
 
-static double
-eucdist(Node *a, Node *b)
+double
+eucdist(Point a, Point b)
 {
-	double dx, dy;
+	int dx, dy;
 
-	dx = a->x - b->x;
-	dy = a->y - b->y;
+	dx = a.x - b.x;
+	dy = a.y - b.y;
 	return sqrt(dx * dx + dy * dy);
 }
 
@@ -203,7 +203,7 @@
 	ss = left ? -1 : 1;
 	(*v)--;
 	for(;;){
-		row = bload(x, y, w, h, 0, 2, left, rot);
+		row = bload(Pt(x, y), Pt(w, h), Pt(0, 2), left, rot);
 		bs = row[1];
 		if(left){
 			bs |= row[0] << 1 & ~row[0];
@@ -240,8 +240,8 @@
 	}
 	if(end)
 		return nil;
-	assert(x < nodemapwidth && y < nodemapheight);
-	n = nodemap + y * nodemapwidth + x;
+	assert(x < mapwidth && y < mapheight);
+	n = map + y * mapwidth + x;
 	n->x = x;
 	n->y = y;
 	n->Δg = steps;
@@ -267,7 +267,7 @@
 		steps++;
 		x += Δx;
 		y += Δy;
-		if(*bload(x, y, w, h, 0, 0, 0, 0) & 1ULL << 63)
+		if(*bload(Pt(x, y), Pt(w, h), ZP, 0, 0) & 1ULL << 63)
 			return nil;
 		if(jumpeast(x, y, w, h, b, &ofs1, left1, 1) != nil
 		|| jumpeast(x, y, w, h, b, &ofs2, left2, 0) != nil)
@@ -275,8 +275,8 @@
 		if(ofs1 == 0 || ofs2 == 0)
 			return nil;
 	}
-	assert(x < nodemapwidth && y < nodemapheight);
-	n = nodemap + y * nodemapwidth + x;
+	assert(x < mapwidth && y < mapheight);
+	n = map + y * mapwidth + x;
 	n->x = x;
 	n->y = y;
 	n->Δg = steps;
@@ -367,12 +367,12 @@
 {
 	u64int *row;
 
-	row = bload(x-1, y-1, w, h, 2, 2, 1, 0);
+	row = bload(Pt(x-1,y-1), Pt(w,h), Pt(2,2), 1, 0);
 	return (row[2] & 7) << 6 | (row[1] & 7) << 3 | row[0] & 7;
 }
 
 static Node **
-successors(Node *n, int w, int h, Node *b)
+jpssuccessors(Node *n, Size sz, Node *b)
 {
 	static Node *dir[8+1];
 	static dtab[2*(nelem(dir)-1)]={
@@ -382,13 +382,13 @@
 	int i, ns;
 	Node *s, **p;
 
-	ns = neighbors(n->x, n->y, w, h);
+	ns = neighbors(n->x, n->y, sz.w, sz.h);
 	ns = prune(ns, n->dir);
 	memset(dir, 0, sizeof dir);
 	for(i=0, p=dir; i<nelem(dtab); i+=2){
 		if(ns & dtab[i])
 			continue;
-		if((s = jump(n->x, n->y, w, h, b, dtab[i+1])) != nil){
+		if((s = jump(n->x, n->y, sz.w, sz.h, b, dtab[i+1])) != nil){
 			s->dir = dtab[i+1];
 			*p++ = s;
 		}
@@ -396,8 +396,33 @@
 	return dir;
 }
 
+static Node **
+successors(Node *n, Size, Node *)
+{
+	static Node *dir[8+1];
+	static dtab[2*(nelem(dir)-1)]={
+		-1,-1, 0,-1, 1,-1,
+		-1,0, 0,1,
+		-1,1, 0,1, 1,1,
+	};
+	int i;
+	Node *s, **p;
+
+	memset(dir, 0, sizeof dir);
+	for(i=0, p=dir; i<nelem(dtab); i+=2){
+		s = n + dtab[i+1] * mapwidth + dtab[i];
+		if(s >= map && s < map + mapwidth * mapheight){
+			s->Point = addpt(n->Point, Pt(dtab[i], dtab[i+1]));
+			s->Δg = 1;
+			s->Δlen = dtab[i] != 0 && dtab[i+1] != 0 ? SQRT2 : 1;
+			*p++ = s;
+		}
+	}
+	return dir;
+}
+
 static Node *
-a∗(Node *a, Node *b, Mobj *mo)
+a∗(Mobj *mo, Node *a, Node *b)
 {
 	double g, Δg;
 	Node *x, *n, **dp;
@@ -416,10 +441,12 @@
 		if(x == b)
 			break;
 		x->closed = 1;
-		dp = successors(x, mo->o->w, mo->o->h, b);
+		dp = successors(x, mo->o->Size, b);
 		for(n=*dp++; n!=nil; n=*dp++){
 			if(n->closed)
 				continue;
+			if(isblocked(n->Point, mo->o))
+				continue;
 			g = x->g + n->Δg;
 			Δg = n->g - g;
 			if(!n->open){
@@ -445,33 +472,28 @@
 }
 
 static void
-directpath(Node *a, Node *g, Mobj *mo)
+directpath(Mobj *mo, Node *a, Node *g)
 {
-	Point p;
 	Path *pp;
 
 	pp = &mo->path;
-	pp->dist = eucdist(a, g);
-	clearvec(&pp->moves, sizeof p);
-	p = Pt(g->x * Nodewidth, g->y * Nodeheight);
-	pushvec(&pp->moves, &p, sizeof p);
+	pp->dist = eucdist(a->Point, g->Point);
+	clearvec(&pp->moves, sizeof g->Point);
+	pushvec(&pp->moves, &g->Point, sizeof g->Point);
 	pp->step = (Point *)pp->moves.p + pp->moves.n - 1;
 }
 
 static void
-backtrack(Node *n, Node *a, Mobj *mo)
+backtrack(Mobj *mo, Node *n, Node *a)
 {
-	Point p;
 	Path *pp;
 
 	pp = &mo->path;
 	assert(n != a && n->step > 0);
 	pp->dist = n->len;
-	clearvec(&pp->moves, sizeof p);
-	for(; n!=a; n=n->from){
-		p = Pt(n->x * Nodewidth, n->y * Nodeheight);
-		pushvec(&pp->moves, &p, sizeof p);
-	}
+	clearvec(&pp->moves, sizeof n->Point);
+	for(; n!=a; n=n->from)
+		pushvec(&pp->moves, &n->Point, sizeof n->Point);
 	pp->step = (Point *)pp->moves.p + pp->moves.n - 1;
 }
 
@@ -491,7 +513,7 @@
 
 /* FIXME: completely broken */
 static Node *
-nearestnonjump(Node *n, Node *b, Mobj *mo)
+nearestnonjump(Mobj *mo, Node *n, Node *b)
 {
 	static Point dirtab[] = {
 		{0,-1},
@@ -499,23 +521,21 @@
 		{0,1},
 		{-1,0},
 	};
-	int i, x, y;
+	int i;
+	Point p;
 	Node *m, *min;
 
 	min = n;
 	for(i=0; i<nelem(dirtab); i++){
-		x = n->x + dirtab[i].x;
-		y = n->y + dirtab[i].y;
-		while(!isblocked(Pt(x, y), mo->o)){
-			m = nodemap + y * nodemapwidth + x;
-			m->x = x;
-			m->y = y;
+		p = addpt(n->Point, dirtab[i]);
+		while(!isblocked(p, mo->o)){
+			m = map + p.y * mapwidth + p.x;
+			m->Point = p;
 			m->h = octdist(m->Point, b->Point);
 			if(min->h < m->h)
 				break;
 			min = m;
-			x += dirtab[i].x;
-			y += dirtab[i].y;
+			p = addpt(p, dirtab[i]);
 		}
 	}
 	if(min != n){
@@ -528,92 +548,82 @@
 
 /* FIXME: completely broken */
 void
-setgoal(Point *p, Mobj *mo, Mobj *block)
+setgoal(Mobj *mo, Point *gp, Mobj *block)
 {
-	int x, y, e;
+	int e;
 	double Δ, Δ´;
-	Node *n1, *n2, *pm;
+	Point p, g;
+	Node *n1, *n2, *gn;
 
 	if(mo->o->f & Fair || block == nil){
 		mo->path.blocked = 0;
 		return;
 	}
+	g = *gp;
 	mo->path.blocked = 1;
-	dprint("%M setgoal: moving goal %d,%d in block %#p ", mo, p->x, p->y, block);
-	pm = nodemap + p->y * nodemapwidth + p->x;
-	pm->x = p->x;
-	pm->y = p->y;
+	dprint("%M setgoal: moving goal %P in block %#p ", mo, g, block);
+	gn = map + g.y * mapwidth + g.x;
+	gn->Point = g;
 	Δ = 0x7ffffff;
-	x = block->x;
-	y = block->y;
-	n1 = nodemap + y * nodemapwidth + x;
-	n2 = n1 + (block->o->h - 1) * nodemapwidth;
-	for(e=x+block->o->w; x<e; x++, n1++, n2++){
-		n1->x = x;
-		n1->y = y;
-		Δ´ = octdist(pm->Point, n1->Point);
+	p = block->Point;
+	n1 = map + p.y * mapwidth + p.x;
+	n2 = n1 + (block->o->h - 1) * mapwidth;
+	for(e=p.x+block->o->w; p.x<e; p.x++, n1++, n2++){
+		n1->Point = p;
+		Δ´ = octdist(gn->Point, n1->Point);
 		if(Δ´ < Δ){
 			Δ = Δ´;
-			p->x = x;
-			p->y = y;
+			g = p;
 		}
-		n2->x = x;
-		n2->y = y + block->o->h - 1;
-		Δ´ = octdist(pm->Point, n2->Point);
+		n2->Point = addpt(p, Pt(0, block->o->h-1));
+		Δ´ = octdist(gn->Point, n2->Point);
 		if(Δ´ < Δ){
 			Δ = Δ´;
-			p->x = x;
-			p->y = y + block->o->h - 1;
+			g = n2->Point;
 		}
 	}
-	x = block->x;
-	y = block->y + 1;
-	n1 = nodemap + y * nodemapwidth + x;
+	p = addpt(block->Point, Pt(0,1));
+	n1 = map + p.y * mapwidth + p.x;
 	n2 = n1 + block->o->w - 1;
-	for(e=y+block->o->h-2; y<e; y++, n1+=nodemapwidth, n2+=nodemapwidth){
-		n1->x = x;
-		n1->y = y;
-		Δ´ = octdist(pm->Point, n1->Point);
+	for(e=p.y+block->o->h-2; p.y<e; p.y++, n1+=mapwidth, n2+=mapwidth){
+		n1->Point = p;
+		Δ´ = octdist(gn->Point, n1->Point);
 		if(Δ´ < Δ){
 			Δ = Δ´;
-			p->x = x;
-			p->y = y;
+			g = p;
 		}
-		n2->x = x + block->o->w - 1;
-		n2->y = y;
-		Δ´ = octdist(pm->Point, n2->Point);
+		n2->Point = addpt(p, Pt(block->o->w-1, 0));
+		Δ´ = octdist(gn->Point, n2->Point);
 		if(Δ´ < Δ){
 			Δ = Δ´;
-			p->x = x + block->o->w - 1;
-			p->y = y;
+			g = n2->Point;
 		}
 	}
-	dprint("to %d,%d\n", p->x, p->y);
+	dprint("to %P\n", g);
+	*gp = g;
 }
 
 int
-findpath(Point p, Mobj *mo)
+findpath(Mobj *mo, Point p)
 {
 	Node *a, *b, *n;
 
-	dprint("%M findpath to %P\n", mo, p);
 	if(eqpt(p, mo->Point)){
 		werrstr("not moving to itself");
 		return -1;
 	}
 	clearpath();
-	a = nodemap + mo->y * nodemapwidth + mo->x;
-	a->x = mo->x;
-	a->y = mo->y;
-	b = nodemap + p.y * nodemapwidth + p.x;
-	b->x = p.x;
-	b->y = p.y;
+	a = map + mo->y * mapwidth + mo->x;
+	a->Point = mo->Point;
+	b = map + p.y * mapwidth + p.x;
+	b->Point = p;
+	dprint("%M findpath from %P to %P dist %f\n", mo, a->Point, b->Point, octdist(a->Point, b->Point));
 	if(mo->o->f & Fair){
-		directpath(a, b, mo);
+		directpath(mo, a, b);
 		return 0;
 	}
 	markmobj(mo, 0);
-	n = a∗(a, b, mo);
+	n = a∗(mo, a, b);
 	if(n != b){
 		dprint("%M findpath: goal unreachable\n", mo);
 		if((n = nearest) == a || n == nil || a->h < n->h){
@@ -621,8 +631,15 @@
 			markmobj(mo, 1);
 			return -1;
 		}
-		dprint("%M nearest: %#p %P dist %f\n", mo, n, n->Point, n->h);
-		b = nearestnonjump(n, b, mo);
+		dprint("%M findpath: nearest is %#p %P dist %f\n", mo, n, n->Point, n->h);
+		n = nearest;
+		if(n == a){
+			werrstr("a∗: really can't move");
+			markmobj(mo, 1);
+			return -1;
+		}
+		/*
+		b = nearestnonjump(mo, n, b);
 		if(b == a){
 			werrstr("a∗: really can't move");
 			markmobj(mo, 1);
@@ -629,16 +646,16 @@
 			return -1;
 		}
 		clearpath();
-		a->x = mo->x;
-		a->y = mo->y;
-		b->x = (b - nodemap) % nodemapwidth;
-		b->y = (b - nodemap) / nodemapwidth;
-		if((n = a∗(a, b, mo)) != b){
+		a->Point = mo->Point;
+		b->Point = Pt((b - map) % mapwidth, (b - map) / mapwidth);
+		if((n = a∗(mo, a, b)) != b){
 			werrstr("bug: failed to find path to nearest non-jump point");
 			return -1;
 		}
+		*/
 	}
+	dprint("%M found %#p at %P dist %f\n", mo, n, n->Point, n->h);
 	markmobj(mo, 1);
-	backtrack(n, a, mo);
+	backtrack(mo, n, a);
 	return 0;
 }
--- a/pheap.c
+++ b/pheap.c
@@ -58,6 +58,7 @@
 	if(p == nil)
 		return nil;
 	*queue = mergepairs(p->left);
+	dprint("pop %#p %P g %f sum %f\n", p->n, p->n->Point, p->n->g, p->sum);
 	return p;
 }
 
@@ -66,6 +67,7 @@
 {
 	p->sum -= Δ;
 	p->n->g -= Δ;
+	dprint("decrease %#p %P g %f sum %f\n", p->n, p->n->Point, p->n->g, p->sum);
 	if(p->parent != nil && p->sum < p->parent->sum){
 		p->parent->left = nil;
 		p->parent = nil;
@@ -82,5 +84,6 @@
 	p->n = n;
 	p->sum = n->h + n->g;
 	n->p = p;
+	dprint("push %#p %P g %f sum %f\n", p->n, p->n->Point, p->n->g, p->sum);
 	*queue = mergequeue(p, *queue);
 }
--- a/sim.move.c
+++ b/sim.move.c
@@ -4,32 +4,14 @@
 #include "dat.h"
 #include "fns.h"
 
-void
-linktomap(Mobj *mo)
-{
-	Map *m;
-
-	m = map + mo->y / Node2Tile * mapwidth + mo->x / Node2Tile;
-	mo->mapl = linkmobj(mo->o->f & Fair ? m->ml.lp : &m->ml, mo, mo->mapl);
-}
-
-static void
-resetcoords(Mobj *mo)
-{
-	markmobj(mo, 0);
-	mo->subpx = mo->px << Subpxshift;
-	mo->subpy = mo->py << Subpxshift;
-	markmobj(mo, 1);
-}
-
 static double
-facegoal(Point p, Mobj *mo)
+facegoal(Mobj *mo, Point p)
 {
 	int dx, dy;
 	double vx, vy, d, θ, θ256, Δθ;
 
-	dx = p.x - mo->px;
-	dy = p.y - mo->py;
+	dx = p.x - mo->x;
+	dy = p.y - mo->y;
 	d = sqrt(dx * dx + dy * dy);
 	if(d == 0.0)
 		sysfatal("facegoal: %M → %P: moving in place, shouldn't happen", mo, p);
@@ -60,22 +42,21 @@
 {
 	Point p;
 
-	p.x = tgt->px + tgt->o->w * Nodewidth / 2;
-	p.y = tgt->py + tgt->o->h * Nodeheight / 2;
-	return facegoal(p, mo);
+	p = addpt(tgt->Point, Pt(tgt->o->w / 2, tgt->o->h / 2));
+	return facegoal(mo, p);
 }
 
 static void
 nextpathnode(Mobj *mo)
 {
-	resetcoords(mo);
-	facegoal(*mo->path.step, mo);
+	snaptomapgrid(mo);
+	facegoal(mo, *mo->path.step);
 }
 
 static void
 clearpath(Mobj *mo)
 {
-	resetcoords(mo);
+	snaptomapgrid(mo);
 	mo->speed = 0.0;
 	mo->path.step = nil;
 	clearvec(&mo->path.moves, sizeof *mo->path.step);
@@ -85,7 +66,7 @@
 cleanup(Mobj *mo)
 {
 	clearpath(mo);
-	mo->path.target = (Point){0,0};
+	mo->path.target = ZP;
 	mo->path.blocked = 0;
 	mo->path.dist = 0.0;
 	mo->path.nerr = 0;
@@ -122,15 +103,13 @@
 	abortcommands(mo);
 }
 
-/* FIXME: kind of weird to mix up argument order,
- * mo should be first like elsewhere */
 static int
-repath(Point p, Mobj *mo)
+repath(Mobj *mo, Point p)
 {
 	clearpath(mo);
 	mo->path.target = p;
-	if(findpath(p, mo) < 0){
-		mo->θ = facegoal(p, mo);
+	if(findpath(mo, p) < 0){
+		mo->θ = facegoal(mo, p);
 		return -1;
 	}
 	nextpathnode(mo);
@@ -149,28 +128,26 @@
 		if(mo->speed > mo->o->speed)
 			mo->speed = mo->o->speed;
 	}
+	mo->speed /= 8;
 }
 
 static int
 trymove(Mobj *mo)
 {
-	int px, py, sx, sy, Δx, Δy, Δu, Δv, Δrx, Δry, Δpx, Δpy;
-	double dx, dy;
-	Point p;
+	int Δx, Δy, Δu, Δv, Δrx, Δry, Δpx, Δpy;
+	Point p, from, sub;
 
 	markmobj(mo, 0);
-	px = mo->px;
-	py = mo->py;
-	sx = mo->subpx;
-	sy = mo->subpy;
-	Δu = mo->u * (1 << Subpxshift);
-	Δv = mo->v * (1 << Subpxshift);
+	from = mo->Point;
+	sub = mo->sub;
+	Δu = mo->u * (1 << Subshift);
+	Δv = mo->v * (1 << Subshift);
 	Δx = abs(Δu);
 	Δy = abs(Δv);
-	Δrx = fabs(mo->u * mo->speed) * (1 << Subpxshift);
-	Δry = fabs(mo->v * mo->speed) * (1 << Subpxshift);
-	Δpx = abs((mo->path.step->x << Subpxshift) - sx);
-	Δpy = abs((mo->path.step->y << Subpxshift) - sy);
+	Δrx = fabs(mo->u * mo->speed) * (1 << Subshift);
+	Δry = fabs(mo->v * mo->speed) * (1 << Subshift);
+	Δpx = abs((mo->path.step->x << Subshift) - sub.x);
+	Δpy = abs((mo->path.step->y << Subshift) - sub.y);
 	if(Δpx < Δrx)
 		Δrx = Δpx;
 	if(Δpy < Δry)
@@ -178,20 +155,18 @@
 	while(Δrx > 0 || Δry > 0){
 		p = mo->Point;
 		if(Δrx > 0){
-			sx += Δu;
+			sub.x += Δu;
 			Δrx -= Δx;
 			if(Δrx < 0)
-				sx += mo->u < 0 ? -Δrx : Δrx;
-			p.x = (sx >> Subpxshift) + ((sx & Subpxmask) != 0);
-			p.x /= Nodewidth;
+				sub.x += mo->u < 0 ? -Δrx : Δrx;
+			p.x = (sub.x >> Subshift) + ((sub.x & Submask) != 0);
 		}
 		if(Δry > 0){
-			sy += Δv;
+			sub.y += Δv;
 			Δry -= Δy;
 			if(Δry < 0)
-				sy += mo->v < 0 ? -Δry : Δry;
-			p.y = (sy >> Subpxshift) + ((sy & Subpxmask) != 0);
-			p.y /= Nodewidth;
+				sub.y += mo->v < 0 ? -Δry : Δry;
+			p.y = (sub.y >> Subshift) + ((sub.y & Submask) != 0);
 		}
 		if(isblocked(p, mo->o))
 			goto end;
@@ -202,30 +177,16 @@
 			dprint("%M detected corner coasting at %P\n", mo, p);
 			goto end;
 		}
-		mo->subpx = sx;
-		mo->subpy = sy;
-		mo->px = sx >> Subpxshift;
-		mo->py = sy >> Subpxshift;
-		mo->x = mo->px / Nodewidth;
-		mo->y = mo->py / Nodeheight;
+		setsubpos(mo, sub);
 	}
 	markmobj(mo, 1);
-	dx = mo->px - px;
-	dx *= dx;
-	dy = mo->py - py;
-	dy *= dy;
-	mo->path.dist -= sqrt(dx + dy) / Nodewidth;
+	mo->path.dist -= eucdist(mo->Point, from);
 	return 0;
 end:
 	werrstr("trymove: can't move to %P", p);
-	mo->subpx = mo->px << Subpxshift;
-	mo->subpy = mo->py << Subpxshift;
+	setpos(mo, mo->Point);
 	markmobj(mo, 1);
-	dx = mo->px - px;
-	dx *= dx;
-	dy = mo->py - py;
-	dy *= dy;
-	mo->path.dist -= sqrt(dx + dy) / Nodewidth;
+	mo->path.dist -= eucdist(mo->Point, from);
 	return -1;
 }
 
@@ -272,7 +233,7 @@
 static int
 nodereached(Mobj *mo)
 {
-	return mo->px == mo->path.step->x && mo->py == mo->path.step->y;
+	return eqpt(mo->Point, *mo->path.step);
 }
 
 static void
@@ -289,9 +250,9 @@
 			fprint(2, "%M stepmove: bug: infinite loop!\n", mo);
 			return;
 		}
-		dprint("%M stepmove: failed moving from %d,%d to %P: %r\n",
-			mo, mo->px, mo->py, *mo->path.step);
-		if(repath(mo->path.target, mo) < 0){
+		dprint("%M stepmove: failed moving from %P to %P: %r\n",
+			mo, mo->Point, *mo->path.step);
+		if(repath(mo, mo->path.target) < 0){
 			dprint("%M stepmove: failed moving towards target: %r\n", mo);
 			abortcommands(mo);
 			return;
@@ -322,7 +283,7 @@
 		return;
 	}
 	dprint("%M stepmove: trying again\n", mo);
-	if(mo->path.nerr++ > 1 || repath(mo->path.target, mo) < 0){
+	if(mo->path.nerr++ > 1 || repath(mo, mo->path.target) < 0){
 		dprint("%M stepmove: still can't reach target: %r\n", mo);
 		abortmove(mo);
 		return;
@@ -338,10 +299,10 @@
 	c = mo->cmds;
 	c->cleanupfn = cleanup;
 	goal = c->goal;
-	setgoal(&goal, mo, c->target1);
-	if(repath(goal, mo) < 0)
+	setgoal(mo, &goal, c->target1);
+	if(repath(mo, goal) < 0)
 		return -1;
-	if(eqpt(goal, mo->Point)){
+	if(eqpt(mo->Point, goal)){
 		mo->state = OSskymaybe;
 		return 0;
 	}
@@ -351,7 +312,7 @@
 }
 
 int
-pushmovecommand(Point goal, Mobj *mo, Mobj *target)
+pushmovecommand(Mobj *mo, Point goal, Mobj *target)
 {
 	Command *c;
 
--- a/sim.return.c
+++ b/sim.return.c
@@ -55,7 +55,7 @@
 		werrstr("no drops");
 		return nil;
 	}
-	d = nodemapwidth * nodemapheight;
+	d = mapwidth * mapheight;
 	for(wp=t->drops.p, we=wp+t->drops.n, wo=nil; wp<we; wp++){
 		w = *wp;
 		d´ = octdist(mo->Point, w->Point);
--- a/sim.spawn.c
+++ b/sim.spawn.c
@@ -31,11 +31,11 @@
 }
 
 int
-spawnunit(Point p, Obj *o, int team)
+spawnunit(Obj *o, Point p, int team)
 {
 	Mobj *mo;
 
-	if((mo = mapspawn(p, o)) == nil)
+	if((mo = mapspawn(o, p)) == nil)
 		return -1;
 	mo->team = team;
 	mo->θ = frand() * 256;
@@ -46,7 +46,7 @@
 }
 
 int
-spawnresource(Point p, Obj *o, int amount)
+spawnresource(Obj *o, Point p, int amount)
 {
 	Mobj *mo;
 
@@ -54,7 +54,7 @@
 		werrstr("spawnresource: invalid amount");
 		return -1;
 	}
-	if((mo = mapspawn(p, o)) == nil)
+	if((mo = mapspawn(o, p)) == nil)
 		return -1;
 	mo->team = 0;
 	mo->amount = amount;