shithub: wl3d

ref: 35e90d35971631b97c76cb3a28ae222f8e6ba79b
dir: /gm.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include "dat.h"
#include "fns.h"

extern Channel *csc;
extern QLock inlck;

Rune keys[Ke] = {
	[Kfire] Kctl,
	[Kstrafe] Kalt,
	[Krun] Kshift,
	[Kopen] ' ',
	[Kknife] '1',
	[Kpistol] '2',
	[Kmg] '3',
	[Kgatling] '4',
	[K↑] Kup,
	[K↓] Kdown,
	[K←] Kleft,
	[K→] Kright,
	[Kmenu] Kesc
};
Game gm;
int msense;
int allrecv, god, noclip, slomo;
int loaded;

typedef struct Crm Crm;
enum{
	Ncrm = 7
};
struct Crm{
	char s[Ncrm];
	void (*f)(void);
};
static char crs[Ncrm];

static int rndi, rndt[] = {
  0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21,
  211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48,
  212, 140, 211, 249, 22, 79, 200, 50, 28, 188, 52, 140, 202, 120, 68, 145,
  62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178, 252, 182, 202, 182,
  141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156, 198, 225, 193, 219, 93,
  122, 175, 249, 0, 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135,
  2, 235, 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113,
  94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75, 136, 156,
  11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196, 135, 106, 63, 197,
  195, 86, 96, 203, 113, 101, 170, 247, 181, 113, 80, 250, 108, 7, 255, 237,
  129, 226, 79, 107, 112, 166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82,
  128, 3, 184, 66, 143, 224, 145, 224, 81, 206, 163, 45, 63, 90, 168, 114,
  59, 33, 159, 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14,
  109, 226, 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36,
  17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242,
  98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, 120, 163, 236, 249
};
static int demfrm;
static int kon, kold, kΔx, kΔy;
static s16int kΔθ;
static int firing, noise, dirty;
static char *dem, *deme;
static int dmgtc, bonustc, facetc, funtc;
static Obj *camobj;
static int killx, killy;
static int dieΔθ, diedir;
static int spearx, speary, spearθ;
static int atk[][4] = {
	{0, 2, 0, -1},
	{0, 1, 0, -1},
	{0, 1, 3, -1},
	{0, 1, 4, -1}
};
static int atkfrm, atktc;
static int dofizz;

static void
givea(int n)
{
	if(gm.ammo == 0 && atkfrm == 0){
		gm.w = gm.lastw;
		hudw();
	}
	gm.ammo += n;
	if(gm.ammo > 99)
		gm.ammo = 99;
	huda();
}

static void
givek(int n)
{
	gm.keys |= 1 << n;
	hudk();
}

static void
givel(void)
{
	if(gm.lives < 9)
		gm.lives++;
	hudl();
	sfx(S1up);
}

static void
giveh(int n)
{
	gm.hp += n;
	if(gm.hp > 100)
		gm.hp = 100;
	hudh();
	hudf();
}

static void
slurp(Obj *)
{
	sfx(Sslurp);
}

static void
mechsfx(Obj *o)
{
	if(plrarea[o->areaid])
		sfxatt(Smechwalk, 1, o->x, o->y);
}

static void
tiredsfx(Obj *)
{
	sfx(Sangeltired);
}

static void
victory(Obj *)
{
	gm.end = EDwon;
}

static void
oattack(Obj *o)
{
	switch(o->type){
	case Ogd:
		sfxatt(Sgd, 1, o->x, o->y);
		ostate(o, stt+GSgdchase1);
		o->v *= 3;
		break;
	case Oofc:
		sfxatt(Sofc, 1, o->x, o->y);
		ostate(o, stt+GSofcchase1);
		o->v *= 5;
		break;
	case Omut:
		ostate(o, stt+GSmutchase1);
		o->v *= 3;
		break;
	case Oss:
		sfxatt(Sss, 1, o->x, o->y);
		ostate(o, stt+GSsschase1);
		o->v *= 4;
		break;
	case Odog:
		sfxatt(Sdog, 1, o->x, o->y);
		ostate(o, stt+GSdogchase1);
		o->v *= 2;
		break;
	case Ohans:
		sfx(Shans);
		ostate(o, stt+GShanschase1);
		o->v = 512 * 3;
		break;
	case Oschb:
		sfx(Sschb);
		ostate(o, stt+GSschbchase1);
		o->v = 512 * 3;
		break;
	case Ogretel:
		sfx(Sgretel);
		ostate(o, stt+GSgretelchase1);
		o->v = 512 * 3;
		break;
	case Ootto:
		sfx(Sotto);
		ostate(o, stt+GSottochase1);
		o->v = 512 * 3;
		break;
	case Ofett:
		sfx(Sfett);
		ostate(o, stt+GSfettchase1);
		o->v = 512 * 3;
		break;
	case Ofake:
		sfx(Sfake);
		ostate(o, stt+GSfakechase1);
		o->v = 512 * 3;
		break;
	case Omech:
		sfx(Shitlerdie);
		ostate(o, stt+GSmechchase1);
		o->v = 512 * 3;
		break;
	case Ohitler:
		sfx(Shitlerdie);
		ostate(o, stt+GShitlerchase1);
		o->v *= 5;
		break;
	case Oghost:
		ostate(o, stt+GSgh1chase1);
		o->v *= 2;
		break;
	case Ospectre:
		sfx(Sghost);
		ostate(o, stt+GSspectrechase1);
		o->v = 800;
		break;
	case Oangel:
		sfx(Sangel);
		ostate(o, stt+GSangelchase1);
		o->v = 1536;
		break;
	case Otrans:
		sfx(Strans);
		ostate(o, stt+GStranschase1);
		o->v = 1536;
		break;
	case Ouber:
		ostate(o, stt+GSuberchase1);
		o->v = 3000;
		break;
	case Owilh:
		sfx(Swilh);
		ostate(o, stt+GSwilhchase1);
		o->v = 2048;
		break;
	case Oknight:
		sfx(Sknight);
		ostate(o, stt+GSknightchase1);
		o->v = 2048;
		break;
	}
	if(o->Δr < 0)
		o->Δr = 0;
	o->f |= OFattack | OFflip;
}

static void
mechblow(Obj *o)
{
	Obj *p;

	p = ospawn(o->tl, stt+GShitlerchase1);
	p->v = 2560;
	p->x = o->x;
	p->y = o->y;
	p->Δr = o->Δr;
	p->θ = o->θ;
	p->f = o->f | OFshootable;
	p->type = Ohitler;
	p->hp = gm.difc>GDbaby ? 600 + gm.difc * 100 : 500;
}

static void
yelp(Obj *o)
{
	static int s[] = {
		Sscream1, Sscream2, Sscream3, Sscream4,
		Sscream5, Sscream7, Sscream8, Sscream9
	};
	int n;

	n = gm.map;
	if((ver == WL6 && n % 10 == 9 || ver == SOD && (n == 18 || n == 19)) && !rnd())
		switch(o->type){
		case Ogd:
		case Oofc:
		case Oss:
		case Odog:
		case Omut:
			sfxatt(Sscream6, 1, o->x, o->y);
			return;
		}
	switch(o->type){
	case Omut: sfxatt(Smutdie, 1, o->x, o->y); break;
	case Ogd: sfxatt(s[rnd()%(ver==WL1?2:8)], 1, o->x, o->y); break;
	case Oofc: sfxatt(Sofcdie, 1, o->x, o->y); break;
	case Oss: sfxatt(Sssdie, 1, o->x, o->y); break;
	case Odog: sfxatt(Sdogdie, 1, o->x, o->y); break;
	case Ohans: sfx(Shansdie); break;
	case Oschb: sfx(Sschbdie); break;
	case Ogretel: sfx(Sgreteldie); break;
	case Ootto: sfx(Sottodie); break;
	case Ofett: sfx(Sfettdie); break;
	case Ofake: sfx(Shitler); break;
	case Omech: sfx(Smechdie); break;
	case Ohitler: sfx(Seva); break;
	case Otrans: sfx(Stransdie); break;
	case Owilh: sfx(Swilhdie); break;
	case Ouber: sfx(Suberdie); break;
	case Oknight: sfx(Sknightdie); break;
	case Ospectre: sfx(Sghostdie); break;
	case Oangel: sfx(Sangeldie); break;
	}
}

static void
odie(Obj *o)
{
	Tile *tl;

	o->tx = o->x >> Dtlshift;	/* drop item on center */
	o->ty = o->y >> Dtlshift;
	tl = tiles + o->ty*Mapdxy + o->tx;
	o->tl = tl;
	switch(o->type){
	case Ogd:
		givep(100);
		ostate(o, stt+GSgddie1);
		drop(tl, Rclip2);
		break;
	case Oofc:
		givep(400);
		ostate(o, stt+GSofcdie1);
		drop(tl, Rclip2);
		break;
	case Omut:
		givep(700);
		ostate(o, stt+GSmutdie1);
		drop(tl, Rclip2);
		break;
	case Oss:
		givep(500);
		ostate(o, stt+GSssdie1);
		drop(tl, gm.bestw < WPmg ? Rmg : Rclip2);
		break;
	case Odog:
		givep(200);
		ostate(o, stt+GSdogdie1);
		break;
	case Ohans:
		givep(5000);
		ostate(o, stt+GShansdie1);
		drop(tl, Rkey1);
		break;
	case Oschb:
		givep(5000);
		killx = oplr->x;
		killy = oplr->y;
		ostate(o, stt+GSschbdie1);
		yelp(o);
		break;
	case Ogretel:
		givep(5000);
		ostate(o, stt+GSgreteldie1);
		drop(tl, Rkey1);
		break;
	case Ootto:
		givep(5000);
		killx = oplr->x;
		killy = oplr->y;
		ostate(o, stt+GSottodie1);
		break;
	case Ofett:
		givep(5000);
		killx = oplr->x;
		killy = oplr->y;
		ostate(o, stt+GSfettdie1);
		break;
	case Ofake:
		givep(2000);
		ostate(o, stt+GSfakedie1);
		break;
	case Omech:
		givep(5000);
		ostate(o, stt+GSmechdie1);
		break;
	case Ohitler:
		givep(5000);
		killx = oplr->x;
		killy = oplr->y;
		ostate(o, stt+GShitlerdie1);
		yelp(o);
		break;
	case Ospectre:
		givep(200);
		ostate(o, stt+GSspectredie1);
		break;
	case Oangel:
		givep(5000);
		ostate(o, stt+GSangeldie1);
		break;
	case Otrans:
		givep(5000);
		ostate(o, stt+GStransdie1);
		drop(tl, Rkey1);
		break;
	case Ouber:
		givep(5000);
		ostate(o, stt+GSuberdie1);
		drop(tl, Rkey1);
		break;
	case Owilh:
		givep(5000);
		ostate(o, stt+GSwilhdie1);
		drop(tl, Rkey1);
		break;
	case Oknight:
		givep(5000);
		ostate(o, stt+GSknightdie1);
		drop(tl, Rkey1);
		break;
	}
	gm.kp++;
	o->f &= ~OFshootable;
	o->f |= OFnomark;
	tl->o = nil;
}

static void
opain(Obj *o, int n)
{
	noise++;
	if(~o->f & OFattack)
		n *= 2;
	o->hp -= n;
	if(o->hp <= 0){
		odie(o);
		return;
	}
	if(~o->f & OFattack)
		oattack(o);
	switch(o->type){
	case Ogd: ostate(o, stt + (o->hp & 1 ? GSgdpain1 : GSgdpain2)); break;
	case Oofc: ostate(o, stt + (o->hp & 1 ? GSofcpain1 : GSofcpain2)); break;
	case Omut: ostate(o, stt + (o->hp & 1 ? GSmutpain1 : GSmutpain2)); break;
	case Oss: ostate(o, stt + (o->hp & 1 ? GSsspain1 : GSsspain2)); break;
	}
}

static void
hurt(int n, Obj *from)
{
	if(gm.won)
		return;
	if(gm.difc == GDbaby)
		n >>= 2;
	if(!god)
		gm.hp -= n;
	if(gm.hp <= 0){
		gm.hp = 0;
		gm.end = gm.demo || gm.record ? EDdem : EDdie;
		killx = from->x;
		killy = from->y;
		if(ver >= SDM && from->type == Oneedle)
			gm.mut++;
	}
	dmgtc += n;
	hudh();
	hudf();
	if(ver >= SDM && n > 30 && gm.hp != 0 && !god){
		pic(136, 164, pict[Pouch]);
		facetc = 0;
	}
}

static void
omove(Obj *o, int Δr)
{
	int x, y;

	x = o->x;
	y = o->y;
	switch(o->θ){
	case θN: y -= Δr; break;
	case θNE: x += Δr; y -= Δr; break;
	case θE: x += Δr; break;
	case θSE: x += Δr; y += Δr; break;
	case θS: y += Δr; break;
	case θSW: x -= Δr; y += Δr; break;
	case θW: x -= Δr; break;
	case θNW: x -= Δr; y -= Δr; break;
	default: return;
	}
	if(!plrarea[o->areaid]
	|| abs(x - oplr->x) > Domin || abs(y - oplr->y) > Domin){
		o->x = x;
		o->y = y;
		o->Δr -= Δr;
	}else if(o->type == Oghost || o->type == Ospectre)
		hurt(Δtc * 2, o);
}

static int
spotline(Obj *o)
{
	int ox, oy, otx, oty, px, py, ptx, pty, x, y, dx, dy, δ;
	u16int t;

	ox = o->x >> 8;	/* 1/256 tile precision */
	oy = o->y >> 8;
	otx = ox >> 8;
	oty = oy >> 8;
	px = oplr->x >> 8;
	py = oplr->y >> 8;
	ptx = oplr->tx;
	pty = oplr->ty;
	if(ptx != otx){
		if(ptx > otx){
			δ = 256 - (ox & 0xff);
			dx = 1;
		}else{
			δ = ox & 0xff;
			dx = -1;
		}
		dy = (py - oy << 8) / abs(px - ox);
		if(dy > 0x7fff)
			dy = 0x7fff;
		else if(dy < -0x7fff)
			dy = -0x7fff;
		y = oy + (dy * δ >> 8);
		x = otx + dx;
		ptx += dx;
		do{
			t = tiles[(y >> 8) * Mapdxy + x].tl;
			y += dy;
			x += dx;
			if(t == 0)
				continue;
			if(t < 128 || y - dy / 2 > doors[t & ~0x80].dopen)
				return 0;
		}while(x != ptx);
	}
	if(pty != oty){
		if(pty > oty){
			δ = 256 - (oy & 0xff);
			dy = 1;
		}else{
			δ = oy & 0xff;
			dy = -1;
		}
		dx = (px - ox << 8) / abs(py - oy);
		if(dx > 0x7fff)
			dx = 0x7fff;
		else if(dx < -0x7fff)
			dx = -0x7fff;
		x = ox + (dx * δ >> 8);
		y = oty + dy;
		pty += dy;
		do{
			t = tiles[y * Mapdxy + (x >> 8)].tl;
			x += dx;
			y += dy;
			if(t == 0)
				continue;
			if(t < 128 || x - dx / 2 > doors[t & ~0x80].dopen)
				return 0;
		}while(y != pty);
	}
	return 1;
}

static void
fire(Obj *o)
{
	int dx, dy, Δr, tohit, s;

	if(!plrarea[o->areaid] || !spotline(o))
		return;
	dx = abs(o->tx - oplr->tx);
	dy = abs(o->ty - oplr->ty);
	Δr = dx > dy ? dx : dy;
	if(o->type == Oss || o->type == Ohans)
		Δr = Δr * 2 / 3;
	tohit = (oplr->v >= 6000 ? 160 : 256) - Δr * (o->f & OFvis ? 16 : 8);
	if(rnd() < tohit)
		hurt(rnd() >> (Δr < 2 ? 2 : Δr < 4 ? 3 : 4), o);
	switch(o->type){
	case Oss: s = Sssfire; break;
	case Ootto:
	case Ofett: s = Smissile; break;
	case Omech:
	case Ohitler:
	case Ohans: s = Shansfire; break;
	default: s = Sgdfire;
	}
	sfxatt(s, 1, o->x, o->y);
}

static void
smoke(Obj *o)
{
	Obj *p;

	p = onew();
	p->tx = o->tx;
	p->ty = o->ty;
	p->tl = o->tl;
	p->x = o->x;
	p->y = o->y;
	p->s = o->type == Orocket ? stt+GSrsmoke1 : stt+GSmsmoke1;
	p->type = Oinert;
	p->tc = 6;
	p->on = 1;
	p->f = OFnevermark;
}

static void
launch(Obj *o)
{
	int Δx, Δy;
	double θ;
	Obj *p;

	Δx = oplr->x - o->x;
	Δy = o->y - oplr->y;
	θ = atan2(Δy, Δx);
	if(θ < 0)
		θ = Fpi * 2 + θ;
	p = onew();
	p->tl = o->tl;
	p->tx = o->tx;
	p->ty = o->ty;
	p->x = o->x;
	p->y = o->y;
	p->θ = θ / (Fpi * 2) * 360;
	p->f = OFnomark;
	p->v = 0x2000;
	p->tc = 1;
	p->on = 1;
	switch(o->type){
	case Oschb:
		p->s = stt+GSneedle1;
		p->type = Oneedle;
		sfxatt(Srocket, 1, p->x, p->y);
		break;
	case Ootto:
	case Ofett:
		p->s = stt+GSmissile;
		p->type = Omissile;
		sfxatt(Smissile, 1, p->x, p->y);
		break;
	case Ofake:
		p->s = stt+GSflame1;
		p->type = Oflame;
		p->v = 0x1200;
		p->f = OFnevermark;
		sfxatt(Sflame, 1, p->x, p->y);
		break;
	case Owilh:
		p->s = stt+GSmissile;
		p->type = Omissile;
		sfxatt(Srocket, 1, p->x, p->y);
		break;
	case Oknight:
		fire(o);
		if(o->s == stt+GSknightfire2)
			p->θ = (p->θ + 356) % 360;
		else
			p->θ = (p->θ + 4) % 360;
		p->s = stt+GSrocket;
		p->type = Orocket;
		sfxatt(Sknightmissile, 1, p->x, p->y);
		break;
	case Oangel:
		p->s = stt+GSspark1;
		p->type = Ospark;
		sfxatt(Sspark, 1, p->x, p->y);
		break;
	}
}

static void
uberfire(Obj *o)
{
	int Δx, Δy, Δr;

	fire(o);
	Δx = abs(o->tx - oplr->tx);
	Δy = abs(o->ty - oplr->ty);
	Δr = Δx > Δy ? Δx : Δy;
	if(Δr <= 1)
		hurt(10, o);
}

static void
bite(Obj *o)
{
	sfxatt(Sdogfire, 1, o->x, o->y);
	if(abs(oplr->x - o->x) - Dtlglobal <= Domin
	&& abs(oplr->y - o->y) - Dtlglobal <= Domin
	&& rnd() < 180)
		hurt(rnd() >> 4, o);
}

static void
relaunch(Obj *o)
{
	if(++o->sdt == 3)
		ostate(o, stt+GSangeltired1);
	else if(rnd() & 1)
		ostate(o, stt+GSangelchase1);
}

static void
prelaunch(Obj *o)
{
	o->sdt = 0;
}

static int
spot(Obj *o)
{
	int dx, dy;

	if(!plrarea[o->areaid])
		return 0;
	dx = oplr->x - o->x;
	dy = oplr->y - o->y;
	if(dx > -0x18000 && dx < 0x18000 && dy > -0x18000 && dy < 0x18000)
		return 1;
	switch(o->θ){
	case θN: if(dy > 0) return 0; break;
	case θE: if(dx < 0) return 0; break;
	case θS: if(dy < 0) return 0; break;
	case θW: if(dx > 0) return 0; break;
	}
	return spotline(o);
}

static int
sawplr(Obj *o)
{
	if(o->f & OFattack)
		sysfatal("sawplr: confused attack node");
	if(o->atkdt <= 0){
		if(!plrarea[o->areaid])
			return 0;
		if((o->f & OFambush || !noise) && !spot(o))
			return 0;
		o->f &= ~OFambush;
		switch(o->type){
		case Ogd: o->atkdt = 1 + rnd() / 4; break;
		case Oofc: o->atkdt = 2; break;
		case Omut: o->atkdt = 1 + rnd() / 6; break;
		case Oss: o->atkdt = 1 + rnd() / 6; break;
		case Odog: o->atkdt = 1 + rnd() / 8; break;
		case Ohans:
		case Oschb:
		case Ogretel:
		case Ootto:
		case Ofett:
		case Ofake:
		case Omech:
		case Ohitler:
		case Otrans:
		case Owilh:
		case Ouber:
		case Oknight:
		case Ospectre:
		case Oangel: o->atkdt = 1; break;
		}
		return 0;
	}
	o->atkdt -= Δtc;
	if(o->atkdt <= 0){
		oattack(o);
		return 1;
	}
	return 0;
}

static int
diagok(Tile *tl)
{
	return tl->to == 0 && (tl->o == nil || ~tl->o->f & OFshootable);
}
static int
sideok(Tile *tl, s16int *dn)
{
	if(tl->o != nil && tl->o->f & OFshootable || tl->to > 0 && tl->to < 128)
		return 0;
	if(tl->to & 0x80)
		*dn = tl->to & 0x3f;
	return 1;
}
static int
trywalk(Obj *o, int θ)
{
	s16int dn;

	dn = -1;
	o->θ = θnil;
	switch(θ){
	case θN:
		if((o->type == Odog || o->type == Ofake)
		&& !diagok(o->tl - Mapdxy)
		|| o->type != Oinert && !sideok(o->tl - Mapdxy, &dn))
			return 0;
		o->ty--;
		o->tl -= Mapdxy;
		break;
	case θNE:
		if(o->type != Oinert && (!diagok(o->tl - Mapdxy + 1)
		|| !diagok(o->tl + 1) || !diagok(o->tl - Mapdxy)))
			return 0;
		o->tx++, o->ty--;
		o->tl -= Mapdxy - 1;
		break;
	case θE:
		if((o->type == Odog || o->type == Ofake)
		&& !diagok(o->tl + 1)
		|| o->type != Oinert && !sideok(o->tl + 1, &dn))
			return 0;
		o->tx++;
		o->tl++;
		break;
	case θSE:
		if(o->type != Oinert && (!diagok(o->tl + Mapdxy + 1)
		|| !diagok(o->tl + 1) || !diagok(o->tl + Mapdxy)))
			return 0;
		o->tx++, o->ty++;
		o->tl += Mapdxy + 1;
		break;
	case θS:
		if((o->type == Odog || o->type == Ofake)
		&& !diagok(o->tl + Mapdxy)
		|| o->type != Oinert && !sideok(o->tl + Mapdxy, &dn))
			return 0;
		o->ty++;
		o->tl += Mapdxy;
		break;
	case θSW:
		if(o->type != Oinert && (!diagok(o->tl + Mapdxy - 1)
		|| !diagok(o->tl - 1) || !diagok(o->tl + Mapdxy)))
			return 0;
		o->tx--, o->ty++;
		o->tl += Mapdxy - 1;
		break;
	case θW:
		if((o->type == Odog || o->type == Ofake)
		&& !diagok(o->tl - 1)
		|| o->type != Oinert && !sideok(o->tl - 1, &dn))
			return 0;
		o->tx--;
		o->tl--;
		break;
	case θNW:
		if(o->type != Oinert && (!diagok(o->tl - Mapdxy - 1)
		|| !diagok(o->tl - 1) || !diagok(o->tl - Mapdxy)))
			return 0;
		o->tx--; o->ty--;
		o->tl -= Mapdxy + 1;
		break;
	case θnil:
		return 0;
	default:
		sysfatal("trywalk: bad angle");
	}
	o->θ = θ;
	if(dn != -1){
		dropen(doors + dn);
		o->Δr = -dn - 1;
	}else{
		o->areaid = o->tl->p0 - MTfloor;
		o->Δr = Dtlglobal;
	}
	return 1;
}

static int
walkθ(Obj *o)
{
	int n, θ;

	n = o->tl->p1 - MTarrows;
	θ = n >= 0 && n < 8 ? n * 45 : o->θ;
	o->Δr = Dtlglobal;
	return trywalk(o, θ);
}

static int
jaywalk(Obj *o)
{
	walkθ(o);
	if(--o->sdt == 0){
		ostate(o, stt+GSjump1);
		return 0;
	}
	return 1;
}

static int
trychase(Obj *o)
{
	int Δx, Δy, i, r, d, θ[3], spin, *p;

	Δx = oplr->tx - o->tx;
	Δy = oplr->ty - o->ty;
	r = abs(Δy) > abs(Δx);
	θ[r ? 1 : 0] = Δx > 0 ? θE : Δx < 0 ? θW : θnil;
	θ[r ? 0 : 1] = Δy > 0 ? θS : Δy < 0 ? θN : θnil;
	θ[2] = o->θ;
	spin = θ[2] == θnil ? θnil : (θ[2] + 180) % 360;
	for(p=θ; p<θ+nelem(θ);){
		r = *p++;
		if(r != spin && trywalk(o, r))
			return 1;
	}
	if(rnd() > 128){
		r = 45;
		d = θN;
	}else{
		r = -45;
		d = θW;
	}
	for(i=0; i<3; d+=r, i++)
		if(d != spin && trywalk(o, d))
			return 1;
	if(trywalk(o, spin))
		return 1;
	return 0;
}

static int
tryrun(Obj *o)
{
	int Δx, Δy, i, r, d, θ[2], *p;

	Δx = oplr->tx - o->tx;
	Δy = oplr->ty - o->ty;
	r = abs(Δy) > abs(Δx);
	θ[r ? 1 : 0] = Δx < 0 ? θE : θW;
	θ[r ? 0 : 1] = Δy < 0 ? θS : θN;
	for(p=θ; p<θ+nelem(θ);){
		r = *p++;
		if(trywalk(o, r))
			return 1;
	}
	if(rnd() > 128){
		r = 45;
		d = θN;
	}else{
		r = -45;
		d = θW;
	}
	for(i=0; i<3; d+=r, i++)
		if(trywalk(o, d))
			return 1;
	return 0;
}

static int
trydodge(Obj *o)
{
	int r, Δx, Δy, θ[5], spin, *p;

	spin = θnil;
	if(o->f & OFflip)
		o->f &= ~OFflip;
	else if(o->θ != θnil)
		spin = (o->θ + 180) % 360;
	Δx = oplr->tx - o->tx;
	Δy = oplr->ty - o->ty;
	r = (abs(Δx) > abs(Δy)) ^ (rnd() < 128);
	θ[r ? 2 : 1] = Δx > 0 ? θE : θW;
	θ[r ? 4 : 3] = Δx > 0 ? θW : θE;
	θ[r ? 1 : 2] = Δy > 0 ? θS : θN;
	θ[r ? 3 : 4] = Δy > 0 ? θN : θS;
	θ[0] = θ[1] + (θ[1]<θ[2] ? (θ[2]-θ[1]==90 ? 45 : 315) : (θ[1]-θ[2]==90 ? 315 : 45));
	θ[0] %= 360;
	for(p=θ; p<θ+nelem(θ);){
		r = *p++;
		if(r != spin && trywalk(o, r))
			return 1;
	}
	if(trywalk(o, spin))
		return 1;
	return 0;
}
static void
odisplace(Obj *o, int walk, int run, int dodge)
{
	int n, Δr;
	Door *d;

	if(o->θ == θnil){
		if(walk)
			n = walkθ(o);
		else if(dodge)
			n = trydodge(o);
		else
			n = trychase(o);
		if(!n)
			return;
	}
	Δr = o->v * Δtc;
	while(Δr != 0){
		switch(o->type){
		case Odog:
			if(!walk && abs(oplr->x - o->x) - Δr <= Domin
			&& abs(oplr->y - o->y) - Δr <= Domin){
				ostate(o, stt+GSdogfire1);
				return;
			}
			break;
		case Oblaz:
		case Ofake:
		case Oghost:
		case Ospectre:
			break;
		default:
			if(o->Δr >= 0)
				break;
			d = doors - o->Δr - 1;
			dropen(d);
			if(d->φ != DRopen)
				return;
			o->Δr = Dtlglobal;
			break;
		}
		if(Δr < o->Δr){
			omove(o, Δr);
			break;
		}
		osetglobal(o);
		Δr -= o->Δr;
		if(walk)
			n = walkθ(o);
		else if(run)
			n = tryrun(o);
		else if(dodge)
			n = trydodge(o);
		else if(o->type == Oblaz)
			n = jaywalk(o);
		else
			n = trychase(o);
		if(!n)
			break;
	}
}

static void
uwait(Obj *o)
{
	sawplr(o);
}

static void
uwalk(Obj *o)
{
	if(sawplr(o))
		return;
	odisplace(o, 1, 0, 0);
}

static void
udogchase(Obj *o)
{
	odisplace(o, 0, 0, 1);
}

static void
ufake(Obj *o)
{
	if(spotline(o) && rnd() < Δtc << 1){
		ostate(o, stt+GSfakefire1);
		return;
	}
	odisplace(o, 0, 0, 1);
}

static void
ughost(Obj *o)
{
	odisplace(o, 0, 0, 0);
}

static void
uboss(Obj *o)
{
	int dodge, Δr, Δx, Δy;

	dodge = 0;
	Δx = abs(o->tx - oplr->tx);
	Δy = abs(o->ty - oplr->ty);
	Δr = Δx > Δy ? Δx : Δy;
	if(spotline(o)){
		if(rnd() < Δtc << 3){
			switch(o->type){
			case Oschb: ostate(o, stt+GSschbfire1); break;
			case Ootto: ostate(o, stt+GSottofire1); break;
			case Ofett: ostate(o, stt+GSfettfire1); break;
			case Owilh: ostate(o, stt+GSwilhfire1); break;
			case Oknight: ostate(o, stt+GSknightfire1); break;
			case Oangel: ostate(o, stt+GSangelfire1); break;
			}
			return;
		}
		dodge++;
	}
	odisplace(o, 0, Δr < 4, dodge);
}

static void
uchase(Obj *o)
{
	int dx, dy, dodge, fire, Δr;

	if(gm.won)
		return;
	dodge = 0;
	if(spotline(o)){
		dx = abs(o->tx - oplr->tx);
		dy = abs(o->ty - oplr->ty);
		Δr = dx > dy ? dx : dy;
		if(Δr == 0 || Δr == 1 && o->Δr < 0x4000)
			fire = 256;
		else
			fire = (Δtc << 4) / Δr;
		if(rnd() < fire){
			switch(o->type){
			case Ogd: ostate(o, stt+GSgdfire1); break;
			case Oss: ostate(o, stt+GSssfire1); break;
			case Oofc: ostate(o, stt+GSofcfire1); break;
			case Omut: ostate(o, stt+GSmutfire1); break;
			case Ohans: ostate(o, stt+GShansfire1); break;
			case Ogretel: ostate(o, stt+GSgretelfire1); break;
			case Omech: ostate(o, stt+GSmechfire1); break;
			case Ohitler: ostate(o, stt+GShitlerfire1); break;
			case Otrans: ostate(o, stt+GStransfire1); break;
			case Ouber: ostate(o, stt+GSuberfire1); break;
			}
			return;
		}
		dodge++;
	}
	odisplace(o, 0, 0, dodge);
}

static void
spawnrun(void)
{
	Obj *o;

	o = ospawn(oplr->tl+Mapdxy, stt+GSblaz1);
	o->x = oplr->x;
	o->y = oplr->y;
	o->type = Oblaz;
	o->v = 2048;
	o->θ = θN;
	o->sdt = 6;
}
static void
urun(Obj *o)
{
	odisplace(o, 0, 0, 0);
}
static void
ujump(Obj *o)
{
	omove(o, 680 * Δtc);
}
static void
runyell(Obj *o)
{
	sfxatt(Syeah, 1, o->x, o->y);
}

static int
trymove(Obj *o, int r, int wallsonly, int noobj)
{
	int dw, ds, x1, x2, y1, y2;
	Tile *tl, *te, *tw;

	x1 = o->x - r >> Dtlshift;
	x2 = o->x + r >> Dtlshift;
	y1 = o->y - r >> Dtlshift;
	y2 = o->y + r >> Dtlshift;
	tl = tiles + y1 * Mapdxy + x1;
	te = tiles + y2 * Mapdxy + x2;
	dw = x2 - x1;
	ds = Mapdxy - dw - 1;
	while(tl <= te){
		tw = tl + dw;
		while(tl <= tw){
			if(tl->to != 0
			|| noobj && tl->o != nil && tl->o->f & OFshootable)
				return 0;
			tl++;
		}
		tl += ds;
	}
	if(wallsonly)
		return 1;

	if(x1 > 0)
		x1--, dw++, ds--;
	if(x2 < Mapdxy - 1)
		te++, dw++, ds--;
	if(y1 > 0)
		y1--;
	if(y2 < Mapdxy - 1)
		te += Mapdxy;
	tl = tiles + y1 * Mapdxy + x1;
	while(tl <= te){
		tw = tl + dw;
		while(tl <= tw){
			if(tl->o != nil && tl->o->f & OFshootable
			&& abs(o->x - tl->o->x) <= Domin
			&& abs(o->y - tl->o->y) <= Domin)
				return 0;
			tl++;
		}
		tl += ds;
	}
	return 1;
}

static void
uprj(Obj *o)
{
	int Δx, Δy, v, dmg;

	v = o->v * Δtc;
	Δx = ffs(v, cost[o->θ]);
	Δy = -ffs(v, sint[o->θ]);
	if(Δx > 0x10000)
		Δx = 0x10000;
	if(Δy > 0x10000)
		Δy = 0x10000;
	o->x += Δx;
	o->y += Δy;
	Δx = o->x - oplr->x;
	if(Δx < 0)
		Δx = -Δx;
	Δy = o->y - oplr->y;
	if(Δy < 0)
		Δy = -Δy;
	if(!trymove(o, 0x2000, 1, 0)){
		if(o->type == Omissile){
			o->s = stt+GSmboom1;
			sfxatt(ver<SDM ? Smissilehit : Srockethit, 1, o->x, o->y);
		}else if(o->type == Orocket){
			o->s = stt+GSrboom1;
			sfxatt(Srockethit, 1, o->x, o->y);
		}else
			o->s = nil;
		return;
	}
	if(Δx < 0xc000 && Δy < 0xc000){
		dmg = 0;
		switch(o->type){
		case Oneedle: dmg = (rnd() >> 3) + 20; break;
		case Omissile:
		case Orocket:
		case Ospark: dmg = (rnd() >> 3) + 30; break;
		case Oflame: dmg = rnd() >> 3; break;
		}
		hurt(dmg, o);
		o->s = nil;
		return;
	}
	o->tx = o->x >> Dtlshift;
	o->ty = o->y >> Dtlshift;
	o->tl = tiles + o->ty * Mapdxy + o->tx;
}

static void
wake(Obj *o)
{
	if(abs(o->x - oplr->x) <= Domin && abs(o->y - oplr->y) <= Domin)
		return;
	if(!trymove(o, Dmin, 1, 1))
		return;
	o->f |= OFambush | OFshootable;
	o->f &= ~OFattack;
	o->θ = θnil;
	ostate(o, stt+GSspectrewait1);
}

static void
cam(Obj *o)
{
	if(gm.won){
		gm.end = EDcam2;
		return;
	}
	gm.end = EDcam;
	qtc = 0;
	dofizz++;
	camobj = o;
}

static void
clipmove(int Δx, int Δy)
{
	int x, y;

	x = oplr->x;
	y = oplr->y;
	oplr->x = x + Δx;
	oplr->y = y + Δy;
	if(trymove(oplr, Dplr, 0, 0))
		return;
	if(noclip && oplr->x > 2 * Dtlglobal && oplr->y > 2 * Dtlglobal
	&& oplr->x < Mapdxy - 1 << Dtlshift && oplr->y < Mapdxy - 1 << Dtlshift)
		return;
	if(lastsfx() < 0)
		sfx(Shitwall);
	oplr->x = x + Δx;
	oplr->y = y;
	if(trymove(oplr, Dplr, 0, 0))
		return;
	oplr->x = x;
	oplr->y = y + Δy;
	if(trymove(oplr, Dplr, 0, 0))
		return;
	oplr->x = x;
	oplr->y = y;
}

static void
thrust(int θ, int v)
{
	int Δx, Δy;

	if(v != 0)
		funtc = 0;
	oplr->v += v;
	if(v >= Dmin * 2)
		v = Dmin * 2 - 1;
	Δx = ffs(v, cost[θ]);
	Δy = -ffs(v, sint[θ]);
	clipmove(Δx, Δy);
	oplr->tx = oplr->x >> Dtlshift;
	oplr->ty = oplr->y >> Dtlshift;
	oplr->tl = tiles + oplr->ty * Mapdxy + oplr->tx;
	oplr->areaid = oplr->tl->p0 - MTfloor;
	if(oplr->tl->p1 == MTexit){
		if(ver < SDM)
			spawnrun();
		gm.won++;
	}
}

/* there is an angle hack because at 70Hz the roundoff becomes significant */
static void
kmove(void)
{
	s16int u;

	oplr->v = 0;
	if(kon & 1<<Kstrafe){
		if(kΔx > 0)
			thrust((oplr->θ + 270) % 360, kΔx * 150);
		else if(kΔx < 0)
			thrust((oplr->θ + 90) % 360, -kΔx * 150);
	}else{
		kΔθ += kΔx;
		u = kΔθ / 20;
		kΔθ -= u * 20;
		oplr->θ = (oplr->θ - u + 360) % 360;
	}
	if(kΔy < 0)
		thrust(oplr->θ, -kΔy * 150);
	else if(kΔy > 0)
		thrust((oplr->θ + 180) % 360, kΔy * 100);
}

static void
push(Tile *tl, int θ)
{
	Tile *c;

	if(pusher.φ != 0 || tl->tl == 0)
		return;
	c = nil;
	switch(θ){
	case θN: c = tl - Mapdxy; break;
	case θE: c = tl + 1; break;
	case θS: c = tl + Mapdxy; break;
	case θW: c = tl - 1; break;
	}
	if(c->o != nil || c->to != 0){
		sfx(Snoway);
		return;
	}
	c->to = tl->tl;
	c->tl = tl->tl;
	gm.sp++;
	pusher.tl = tl;
	pusher.isvert = θ;
	pusher.φ = 1;
	pusher.dopen = 0;
	tl->tl |= 0xc0;
	tl->p1 = 0;
	sfx(Spushwall);
}

static void
kopen(void)
{
	Tile *tl;
	int upok, dn, θ;

	tl = oplr->tl;
	upok = 0;
	if(oplr->θ < 360/8 || oplr->θ > 7*360/8){
		tl++;
		θ = θE;
		upok++;
	}else if(oplr->θ < 3*360/8){
		tl -= Mapdxy;
		θ = θN;
	}else if (oplr->θ < 5*360/8){
		tl--;
		θ = θW;
		upok++;
	}else{
		tl += Mapdxy;
		θ = θS;
	}
	dn = tl->tl;
	if(tl->p1 == MTpush){
		push(tl, θ);
		return;
	}
	if(~kold & 1<<Kopen){
		if(dn == MTgoup && upok){
			kold |= 1<<Kopen;
			tl->tl++;	/* flip switch */
			gm.end = oplr->tl->p0 == MTsetec ? EDsetec : EDup;
			stopmus();
			sfx(Slvlend);
			sfxlck++;
			return;
		/* bug: 1<<6 may be set around pushwalls and cause memory
		 * corruption if this check goes through by writing past the
		 * doors array */
		}else if((dn & 0xc0) == 0x80){
			kold |= 1<<Kopen;
			druse(doors + (dn & ~0x80));
			return;
		}
	}
	sfx(Snope);
}

static void
kfire(void)
{
	kold |= 1<<Kfire;
	firing++;
	atkfrm = 0;
	atktc = 6;
	gm.wfrm = 1;
}

static void
winspin(void)
{
	int y;

	if(oplr->θ > 270){
		oplr->θ -= Δtc * 3;
		if(oplr->θ < 270)
			oplr->θ = 270;
	}else if(oplr->θ < 270){
		oplr->θ += Δtc * 3;
		if(oplr->θ > 270)
			oplr->θ = 270;
	}
	y = (oplr->ty - 5 << Dtlshift) - 0x3000;
	if(oplr->y > y){
		oplr->y -= Δtc * 4096;
		if(oplr->y < y)
			oplr->y = y;
	}
}

static void
face(void)
{
	/* bug: demos must be played back with the same sound settings as they
	 * were recorded, namely adlib sfx (wl6) and pcm (sod) or desync when
	 * lastsfx is called */
	if(lastsfx() == Sgetgatling)
		return;
	facetc += Δtc;
	if(facetc > rnd())
	{
		gm.facefrm = rnd() >> 6;
		if(gm.facefrm == 3)
			gm.facefrm = 1;
		facetc = 0;
		hudf();
	}
}

static void
idleface(void)
{
	funtc += Δtc;
	if(funtc > 30 * Tb){
		funtc = 0;
		pic(17*8, 164, pict[Pwait] + (rnd() & 1));
		facetc = 0;
	}
}

static void
wep(void)
{
	int i;

	if(gm.ammo == 0)
		return;
	for(i=0; i<=gm.bestw; i++)
		if(kon & 1 << Kknife + i){
			gm.w = gm.lastw = i;
			hudw();
			return;
		}
}

static void
knife(void)
{
	int Δx;
	Obj *o, *hit;

	sfx(Sknife);
	Δx = 0x7fffffff;
	hit = nil;
	for(o=oplr->n; o!=objs; o=o->n)
		if((o->f & (OFshootable | OFvis)) == (OFshootable | OFvis)
		&& abs(o->vwdx - vw.mid) < vw.Δhit && o->vwx < Δx){
			Δx = o->vwx;
			hit = o;
		}
	if(hit == nil || Δx > 0x18000)
		return;
	opain(hit, rnd() >> 4);
}

static void
gun(void)
{
	int n, dx, dy, Δx;
	Obj *o, *hit, *last;

	switch(gm.w){
	case WPpistol: sfx(Spistol); break;
	case WPmg: sfx(Smg); break;
	case WPgatling: sfx(Sgatling); break;
	}
	noise++;

	Δx = 0x7fffffff;
	hit = nil;
	for(;;){
		last = hit;
		for(o=oplr->n; o!=objs; o=o->n)
			if((o->f & (OFshootable | OFvis)) == (OFshootable | OFvis)
			&& abs(o->vwdx - vw.mid) < vw.Δhit && o->vwx < Δx){
				Δx = o->vwx;
				hit = o;
			}
		if(hit == last)
			return;
		if(spotline(hit))
			break;
	}
	dx = abs(hit->tx - oplr->tx);
	dy = abs(hit->ty - oplr->ty);
	Δx = dx > dy ? dx : dy;
	if(Δx < 2)
		n = rnd() / 4;
	else if(Δx < 4)
		n = rnd() / 6;
	else{
		if(rnd() / 12 < Δx)
			return;
		n = rnd() / 6;
	}
	opain(hit, n);
}

static void
uplr(Obj *)
{
	int n, shot;

	if(firing)
		face();
	if(gm.won){
		winspin();
		return;
	}
	shot = 0;
	if(!firing){
		face();
		wep();
		if(kon & 1<<Kopen)
			kopen();
		if(kon & 1<<Kfire & ~kold){
			kfire();
			shot++;
		}
	}
	kmove();
	if(gm.won || !firing || shot)
		return;

	atktc -= Δtc;
	while(atktc <= 0){
		n = atk[gm.w][atkfrm];
		switch(n){
		case -1:
			firing = 0;
			if(gm.ammo == 0){
				gm.w = WPknife;
				hudw();
			}else if(gm.w != gm.lastw){
				gm.w = gm.lastw;
				hudw();
			};
			atkfrm = 0;
			gm.wfrm = 0;
			return;
		case 4:
			if(gm.ammo == 0)
				break;
			if(kon & 1<<Kfire)
				atkfrm -= 2;
			/* wet floor */
		case 1:
			if(gm.ammo == 0){
				atkfrm++;
				break;
			}
			gun();
			gm.ammo--;
			huda();
			break;
		case 2:
			knife();
			break;
		case 3:
			if(gm.ammo != 0 && (kon & 1<<Kfire))
				atkfrm -= 2;
			break;
		}
		atktc += 6;
		atkfrm++;
		gm.wfrm = atkfrm + 1;
	}
}

static void
gamein(void)
{
	int mx, my, scale;

	qlock(&inlck);
	mx = mΔx;
	my = mΔy;
	mΔx = mΔy = 0;
	kon = kb | mb & 1 | (mb & 2) << 2 | (mb & 4) >> 1;
	qunlock(&inlck);

	if(kon & 1<<Kmenu){
		gm.end = EDkey;
		return;
	}
	if(autorun)
		kon |= 1<<Krun;
	kΔx = kΔy = 0;
	scale = Δtc * (kon & 1<<Krun ? 70 : 35);
	if(kon & 1<<K↑)
		kΔy -= scale;
	if(kon & 1<<K↓)
		kΔy += scale;
	if(kon & 1<<K←)
		kΔx -= scale;
	if(kon & 1<<K→)
		kΔx += scale;
	kΔx += mx * 10 / (13 - msense);
	kΔy += my * 20 / (13 - msense);

	scale = 100 * Δtc;
	if(kΔx > scale)
		kΔx = scale;
	else if(kΔx < -scale)
		kΔx = -scale;
	if(kΔy > scale)
		kΔy = scale;
	else if(kΔy < -scale)
		kΔy = -scale;
}
static void
demoin(void)
{
	if(dem >= deme)
		sysfatal("demoin: read past end of lump");
	kon = *dem++;
	kΔx = *dem++ * Δtc;
	kΔy = *dem++ * Δtc;
	if(dem >= deme)
		gm.end = EDdem;
}
static void
input(void)
{
	kold = kon;
	if(gm.demo)
		demoin();
	else
		gamein();
	if(gm.record){
		if(dem+3 >= deme)
			sysfatal("demo overflow");
		*dem++ = kon & 0xff;
		*dem++ = kΔx / Δtc & 0xff;
		*dem++ = kΔy / Δtc & 0xff;
	}
	if(firing){
		if(kon & 1<<Kopen & ~kold)
			kon &= ~(1<<Kopen);
		if(kon & 1<<Kfire & ~kold)
			kon &= ~(1<<Kfire);
	}
}

State stt[] = {
	[GSplr] {uplr, nil, 0, 0, nil, 0},
	[GSplrcam] {nil, nil, 0, 0, nil, 0},
	[GSblaz1] {urun, nil, 12, SPbjwalk1, stt+GSblaz2, 0},
	[GSblaz2] {nil, nil, 3, SPbjwalk1, stt+GSblaz3, 0},
	[GSblaz3] {urun, nil, 8, SPbjwalk2, stt+GSblaz4, 0},
	[GSblaz4] {urun, nil, 12, SPbjwalk3, stt+GSblaz5, 0},
	[GSblaz5] {nil, nil, 3, SPbjwalk3, stt+GSblaz6, 0},
	[GSblaz6] {urun, nil, 8, SPbjwalk4, stt+GSblaz1, 0},
	[GSjump1] {ujump, nil, 14, SPbjjump1, stt+GSjump2, 0},
	[GSjump2] {ujump, runyell, 14, SPbjjump2, stt+GSjump3, 0},
	[GSjump3] {ujump, nil, 14, SPbjjump3, stt+GSjump4, 0},
	[GSjump4] {nil, victory, 300, SPbjjump4, stt+GSjump4, 0},
	[GSgd] {uwait, nil, 0, SPgd, stt+GSgd, 1},
	[GSgdwalk1] {uwalk, nil, 20, SPgdwalk1, stt+GSgdwalk2, 1},
	[GSgdwalk2] {nil, nil, 5, SPgdwalk1, stt+GSgdwalk3, 1},
	[GSgdwalk3] {uwalk, nil, 15, SPgdwalk2, stt+GSgdwalk4, 1},
	[GSgdwalk4] {uwalk, nil, 20, SPgdwalk3, stt+GSgdwalk5, 1},
	[GSgdwalk5] {nil, nil, 5, SPgdwalk3, stt+GSgdwalk6, 1},
	[GSgdwalk6] {uwalk, nil, 15, SPgdwalk4, stt+GSgdwalk1, 1},
	[GSgdpain1] {nil, nil, 10, SPgdpain1, stt+GSgdchase1, 2},
	[GSgdpain2] {nil, nil, 10, SPgdpain2, stt+GSgdchase1, 2},
	[GSgdchase1] {uchase, nil, 10, SPgdwalk1, stt+GSgdchase2, 1},
	[GSgdchase2] {nil, nil, 3, SPgdwalk1, stt+GSgdchase3, 1},
	[GSgdchase3] {uchase, nil, 8, SPgdwalk2, stt+GSgdchase4, 1},
	[GSgdchase4] {uchase, nil, 10, SPgdwalk3, stt+GSgdchase5, 1},
	[GSgdchase5] {nil, nil, 3, SPgdwalk3, stt+GSgdchase6, 1},
	[GSgdchase6] {uchase, nil, 8, SPgdwalk4, stt+GSgdchase1, 1},
	[GSgdfire1] {nil, nil, 20, SPgdfire1, stt+GSgdfire2, 0},
	[GSgdfire2] {nil, fire, 20, SPgdfire2, stt+GSgdfire3, 0},
	[GSgdfire3] {nil, nil, 20, SPgdfire3, stt+GSgdchase1, 0},
	[GSgddie1] {nil, yelp, 15, SPgddie1, stt+GSgddie2, 0},
	[GSgddie2] {nil, nil, 15, SPgddie2, stt+GSgddie3, 0},
	[GSgddie3] {nil, nil, 15, SPgddie3, stt+GSgddie4, 0},
	[GSgddie4] {nil, nil, 0, SPgddead, stt+GSgddie4, 0},
	[GSss] {uwait, nil, 0, SPss, stt+GSss, 1},
	[GSsswalk1] {uwalk, nil, 20, SPsswalk1, stt+GSsswalk2, 1},
	[GSsswalk2] {nil, nil, 5, SPsswalk1, stt+GSsswalk3, 1},
	[GSsswalk3] {uwalk, nil, 15, SPsswalk2, stt+GSsswalk4, 1},
	[GSsswalk4] {uwalk, nil, 20, SPsswalk3, stt+GSsswalk5, 1},
	[GSsswalk5] {nil, nil, 5, SPsswalk3, stt+GSsswalk6, 1},
	[GSsswalk6] {uwalk, nil, 15, SPsswalk4, stt+GSsswalk1, 1},
	[GSsspain1] {nil, nil, 10, SPsspain1, stt+GSsschase1, 2},
	[GSsspain2] {nil, nil, 10, SPsspain2, stt+GSsschase1, 2},
	[GSsschase1] {uchase, nil, 10, SPsswalk1, stt+GSsschase2, 1},
	[GSsschase2] {nil, nil, 3, SPsswalk1, stt+GSsschase3, 1},
	[GSsschase3] {uchase, nil, 8, SPsswalk2, stt+GSsschase4, 1},
	[GSsschase4] {uchase, nil, 10, SPsswalk3, stt+GSsschase5, 1},
	[GSsschase5] {nil, nil, 3, SPsswalk3, stt+GSsschase6, 1},
	[GSsschase6] {uchase, nil, 8, SPsswalk4, stt+GSsschase1, 1},
	[GSssfire1] {nil, nil, 20, SPssfire1, stt+GSssfire2, 0},
	[GSssfire2] {nil, fire, 20, SPssfire2, stt+GSssfire3, 0},
	[GSssfire3] {nil, nil, 10, SPssfire3, stt+GSssfire4, 0},
	[GSssfire4] {nil, fire, 10, SPssfire2, stt+GSssfire5, 0},
	[GSssfire5] {nil, nil, 10, SPssfire3, stt+GSssfire6, 0},
	[GSssfire6] {nil, fire, 10, SPssfire2, stt+GSssfire7, 0},
	[GSssfire7] {nil, nil, 10, SPssfire3, stt+GSssfire8, 0},
	[GSssfire8] {nil, fire, 10, SPssfire2, stt+GSssfire9, 0},
	[GSssfire9] {nil, nil, 10, SPssfire3, stt+GSsschase1, 0},
	[GSssdie1] {nil, yelp, 15, SPssdie1, stt+GSssdie2, 0},
	[GSssdie2] {nil, nil, 15, SPssdie2, stt+GSssdie3, 0},
	[GSssdie3] {nil, nil, 15, SPssdie3, stt+GSssdie4, 0},
	[GSssdie4] {nil, nil, 0, SPssdead, stt+GSssdie4, 0},
	[GSofc] {uwait, nil, 0, SPofc, stt+GSofc, 1},
	[GSofcwalk1] {uwalk, nil, 20, SPofcwalk1, stt+GSofcwalk2, 1},
	[GSofcwalk2] {nil, nil, 5, SPofcwalk1, stt+GSofcwalk3, 1},
	[GSofcwalk3] {uwalk, nil, 15, SPofcwalk2, stt+GSofcwalk4, 1},
	[GSofcwalk4] {uwalk, nil, 20, SPofcwalk3, stt+GSofcwalk5, 1},
	[GSofcwalk5] {nil, nil, 5, SPofcwalk3, stt+GSofcwalk6, 1},
	[GSofcwalk6] {uwalk, nil, 15, SPofcwalk4, stt+GSofcwalk1, 1},
	[GSofcpain1] {nil, nil, 10, SPofcpain1, stt+GSofcchase1, 2},
	[GSofcpain2] {nil, nil, 10, SPofcpain2, stt+GSofcchase1, 2},
	[GSofcchase1] {uchase, nil, 10, SPofcwalk1, stt+GSofcchase2, 1},
	[GSofcchase2] {nil, nil, 3, SPofcwalk1, stt+GSofcchase3, 1},
	[GSofcchase3] {uchase, nil, 8, SPofcwalk2, stt+GSofcchase4, 1},
	[GSofcchase4] {uchase, nil, 10, SPofcwalk3, stt+GSofcchase5, 1},
	[GSofcchase5] {nil, nil, 3, SPofcwalk3, stt+GSofcchase6, 1},
	[GSofcchase6] {uchase, nil, 8, SPofcwalk4, stt+GSofcchase1, 1},
	[GSofcfire1] {nil, nil, 6, SPofcfire1, stt+GSofcfire2, 0},
	[GSofcfire2] {nil, fire, 20, SPofcfire2, stt+GSofcfire3, 0},
	[GSofcfire3] {nil, nil, 10, SPofcfire3, stt+GSofcchase1, 0},
	[GSofcdie1] {nil, yelp, 11, SPofcdie1, stt+GSofcdie2, 0},
	[GSofcdie2] {nil, nil, 11, SPofcdie2, stt+GSofcdie3, 0},
	[GSofcdie3] {nil, nil, 11, SPofcdie3, stt+GSofcdie4, 0},
	[GSofcdie4] {nil, nil, 11, SPofcdie4, stt+GSofcdie5, 0},
	[GSofcdie5] {nil, nil, 0, SPofcdead, stt+GSofcdie5, 0},
	[GSmut] {uwait, nil, 0, SPmut, stt+GSmut, 1},
	[GSmutwalk1] {uwalk, nil, 20, SPmutwalk1, stt+GSmutwalk2, 1},
	[GSmutwalk2] {nil, nil, 5, SPmutwalk1, stt+GSmutwalk3, 1},
	[GSmutwalk3] {uwalk, nil, 15, SPmutwalk2, stt+GSmutwalk4, 1},
	[GSmutwalk4] {uwalk, nil, 20, SPmutwalk3, stt+GSmutwalk5, 1},
	[GSmutwalk5] {nil, nil, 5, SPmutwalk3, stt+GSmutwalk6, 1},
	[GSmutwalk6] {uwalk, nil, 15, SPmutwalk4, stt+GSmutwalk1, 1},
	[GSmutpain1] {nil, nil, 10, SPmutpain1, stt+GSmutchase1, 2},
	[GSmutpain2] {nil, nil, 10, SPmutpain2, stt+GSmutchase1, 2},
	[GSmutchase1] {uchase, nil, 10, SPmutwalk1, stt+GSmutchase2, 1},
	[GSmutchase2] {nil, nil, 3, SPmutwalk1, stt+GSmutchase3, 1},
	[GSmutchase3] {uchase, nil, 8, SPmutwalk2, stt+GSmutchase4, 1},
	[GSmutchase4] {uchase, nil, 10, SPmutwalk3, stt+GSmutchase5, 1},
	[GSmutchase5] {nil, nil, 3, SPmutwalk3, stt+GSmutchase6, 1},
	[GSmutchase6] {uchase, nil, 8, SPmutwalk4, stt+GSmutchase1, 1},
	[GSmutfire1] {nil, fire, 6, SPmutfire1, stt+GSmutfire2, 0},
	[GSmutfire2] {nil, nil, 20, SPmutfire2, stt+GSmutfire3, 0},
	[GSmutfire3] {nil, fire, 10, SPmutfire3, stt+GSmutfire4, 0},
	[GSmutfire4] {nil, nil, 20, SPmutfire4, stt+GSmutchase1, 0},
	[GSmutdie1] {nil, yelp, 7, SPmutdie1, stt+GSmutdie2, 0},
	[GSmutdie2] {nil, nil, 7, SPmutdie2, stt+GSmutdie3, 0},
	[GSmutdie3] {nil, nil, 7, SPmutdie3, stt+GSmutdie4, 0},
	[GSmutdie4] {nil, nil, 7, SPmutdie4, stt+GSmutdie5, 0},
	[GSmutdie5] {nil, nil, 0, SPmutdead, stt+GSmutdie5, 0},
	[GSdogwalk1] {uwalk, nil, 20, SPdogwalk1, stt+GSdogwalk2, 1},
	[GSdogwalk2] {nil, nil, 5, SPdogwalk1, stt+GSdogwalk3, 1},
	[GSdogwalk3] {uwalk, nil, 15, SPdogwalk2, stt+GSdogwalk4, 1},
	[GSdogwalk4] {uwalk, nil, 20, SPdogwalk3, stt+GSdogwalk5, 1},
	[GSdogwalk5] {nil, nil, 5, SPdogwalk3, stt+GSdogwalk6, 1},
	[GSdogwalk6] {uwalk, nil, 15, SPdogwalk4, stt+GSdogwalk1, 1},
	[GSdogchase1] {udogchase, nil, 10, SPdogwalk1, stt+GSdogchase2, 1},
	[GSdogchase2] {nil, nil, 3, SPdogwalk1, stt+GSdogchase3, 1},
	[GSdogchase3] {udogchase, nil, 8, SPdogwalk2, stt+GSdogchase4, 1},
	[GSdogchase4] {udogchase, nil, 10, SPdogwalk3, stt+GSdogchase5, 1},
	[GSdogchase5] {nil, nil, 3, SPdogwalk3, stt+GSdogchase6, 1},
	[GSdogchase6] {udogchase, nil, 8, SPdogwalk4, stt+GSdogchase1, 1},
	[GSdogfire1] {nil, nil, 10, SPdogfire1, stt+GSdogfire2, 0},
	[GSdogfire2] {nil, bite, 10, SPdogfire2, stt+GSdogfire3, 0},
	[GSdogfire3] {nil, nil, 10, SPdogfire3, stt+GSdogfire4, 0},
	[GSdogfire4] {nil, nil, 10, SPdogfire1, stt+GSdogfire5, 0},
	[GSdogfire5] {nil, nil, 10, SPdogwalk1, stt+GSdogchase1, 0},
	[GSdogdie1] {nil, yelp, 15, SPdogdie1, stt+GSdogdie2, 0},
	[GSdogdie2] {nil, nil, 15, SPdogdie2, stt+GSdogdie3, 0},
	[GSdogdie3] {nil, nil, 15, SPdogdie3, stt+GSdogdie4, 0},
	[GSdogdie4] {nil, nil, 15, SPdogdead, stt+GSdogdie4, 0},
	[GShans] {uwait, nil, 0, SPhanswalk1, stt+GShans, 0},
	[GShanschase1] {uchase, nil, 10, SPhanswalk1, stt+GShanschase2, 0},
	[GShanschase2] {nil, nil, 3, SPhanswalk1, stt+GShanschase3, 0},
	[GShanschase3] {uchase, nil, 8, SPhanswalk2, stt+GShanschase4, 0},
	[GShanschase4] {uchase, nil, 10, SPhanswalk3, stt+GShanschase5, 0},
	[GShanschase5] {nil, nil, 3, SPhanswalk3, stt+GShanschase6, 0},
	[GShanschase6] {uchase, nil, 8, SPhanswalk4, stt+GShanschase1, 0},
	[GShansfire1] {nil, nil, 30, SPhansfire1, stt+GShansfire2, 0},
	[GShansfire2] {nil, fire, 10, SPhansfire2, stt+GShansfire3, 0},
	[GShansfire3] {nil, fire, 10, SPhansfire3, stt+GShansfire4, 0},
	[GShansfire4] {nil, fire, 10, SPhansfire2, stt+GShansfire5, 0},
	[GShansfire5] {nil, fire, 10, SPhansfire3, stt+GShansfire6, 0},
	[GShansfire6] {nil, fire, 10, SPhansfire2, stt+GShansfire7, 0},
	[GShansfire7] {nil, fire, 10, SPhansfire3, stt+GShansfire8, 0},
	[GShansfire8] {nil, nil, 10, SPhansfire1, stt+GShanschase1, 0},
	[GShansdie1] {nil, yelp, 15, SPhansdie1, stt+GShansdie2, 0},
	[GShansdie2] {nil, nil, 15, SPhansdie2, stt+GShansdie3, 0},
	[GShansdie3] {nil, nil, 15, SPhansdie3, stt+GShansdie4, 0},
	[GShansdie4] {nil, nil, 0, SPhansdead, stt+GShansdie4, 0},
	[GSschb] {uwait, nil, 0, SPschbwalk1, stt+GSschb, 0},
	[GSschbchase1] {uboss, nil, 10, SPschbwalk1, stt+GSschbchase2, 0},
	[GSschbchase2] {nil, nil, 3, SPschbwalk1, stt+GSschbchase3, 0},
	[GSschbchase3] {uboss, nil, 8, SPschbwalk2, stt+GSschbchase4, 0},
	[GSschbchase4] {uboss, nil, 10, SPschbwalk3, stt+GSschbchase5, 0},
	[GSschbchase5] {nil, nil, 3, SPschbwalk3, stt+GSschbchase6, 0},
	[GSschbchase6] {uboss, nil, 8, SPschbwalk4, stt+GSschbchase1, 0},
	[GSschbfire1] {nil, nil, 30, SPschbfire1, stt+GSschbfire2, 0},
	[GSschbfire2] {nil, launch, 10, SPschbfire2, stt+GSschbchase1, 0},
	[GSschbcam] {nil, nil, 1, SPschbwalk1, stt+GSschbdie1, 0},
	[GSschbdie1] {nil, yelp, 10, SPschbwalk1, stt+GSschbdie2, 0},
	[GSschbdie2] {nil, nil, 10, SPschbwalk1, stt+GSschbdie3, 0},
	[GSschbdie3] {nil, nil, 10, SPschbdie1, stt+GSschbdie4, 0},
	[GSschbdie4] {nil, nil, 10, SPschbdie2, stt+GSschbdie5, 0},
	[GSschbdie5] {nil, nil, 10, SPschbdie3, stt+GSschbdie6, 0},
	[GSschbdie6] {nil, cam, 20, SPschbdead, stt+GSschbdie6, 0},
	[GSgretel] {uwait, nil, 0, SPgretelwalk1, stt+GSgretel, 0},
	[GSgretelchase1] {uchase, nil, 10, SPgretelwalk1, stt+GSgretelchase2, 0},
	[GSgretelchase2] {nil, nil, 3, SPgretelwalk1, stt+GSgretelchase3, 0},
	[GSgretelchase3] {uchase, nil, 8, SPgretelwalk2, stt+GSgretelchase4, 0},
	[GSgretelchase4] {uchase, nil, 10, SPgretelwalk3, stt+GSgretelchase5, 0},
	[GSgretelchase5] {nil, nil, 3, SPgretelwalk3, stt+GSgretelchase6, 0},
	[GSgretelchase6] {uchase, nil, 8, SPgretelwalk4, stt+GSgretelchase1, 0},
	[GSgretelfire1] {nil, nil, 30, SPgretelfire1, stt+GSgretelfire2, 0},
	[GSgretelfire2] {nil, fire, 10, SPgretelfire2, stt+GSgretelfire3, 0},
	[GSgretelfire3] {nil, fire, 10, SPgretelfire3, stt+GSgretelfire4, 0},
	[GSgretelfire4] {nil, fire, 10, SPgretelfire2, stt+GSgretelfire5, 0},
	[GSgretelfire5] {nil, fire, 10, SPgretelfire3, stt+GSgretelfire6, 0},
	[GSgretelfire6] {nil, fire, 10, SPgretelfire2, stt+GSgretelfire7, 0},
	[GSgretelfire7] {nil, fire, 10, SPgretelfire3, stt+GSgretelfire8, 0},
	[GSgretelfire8] {nil, nil, 10, SPgretelfire1, stt+GSgretelchase1, 0},
	[GSgreteldie1] {nil, yelp, 15, SPgreteldie1, stt+GSgreteldie2, 0},
	[GSgreteldie2] {nil, nil, 15, SPgreteldie2, stt+GSgreteldie3, 0},
	[GSgreteldie3] {nil, nil, 15, SPgreteldie3, stt+GSgreteldie4, 0},
	[GSgreteldie4] {nil, nil, 0, SPgreteldead, stt+GSgreteldie4, 0},
	[GSotto] {uwait, nil, 0, SPottowalk1, stt+GSotto, 0},
	[GSottochase1] {uboss, nil, 10, SPottowalk1, stt+GSottochase2, 0},
	[GSottochase2] {nil, nil, 3, SPottowalk1, stt+GSottochase3, 0},
	[GSottochase3] {uboss, nil, 8, SPottowalk2, stt+GSottochase4, 0},
	[GSottochase4] {uboss, nil, 10, SPottowalk3, stt+GSottochase5, 0},
	[GSottochase5] {nil, nil, 3, SPottowalk3, stt+GSottochase6, 0},
	[GSottochase6] {uboss, nil, 8, SPottowalk4, stt+GSottochase1, 0},
	[GSottofire1] {nil, nil, 30, SPottofire1, stt+GSottofire2, 0},
	[GSottofire2] {nil, launch, 10, SPottofire2, stt+GSottochase1, 0},
	[GSottocam] {nil, nil, 1, SPottowalk1, stt+GSottodie1, 0},
	[GSottodie1] {nil, yelp, 1, SPottowalk1, stt+GSottodie2, 0},
	[GSottodie2] {nil, nil, 10, SPottowalk1, stt+GSottodie3, 0},
	[GSottodie3] {nil, nil, 10, SPottodie1, stt+GSottodie4, 0},
	[GSottodie4] {nil, nil, 10, SPottodie2, stt+GSottodie5, 0},
	[GSottodie5] {nil, nil, 10, SPottodie3, stt+GSottodie6, 0},
	[GSottodie6] {nil, cam, 20, SPottodead, stt+GSottodie6, 0},
	[GSfett] {uwait, nil, 0, SPfettwalk1, stt+GSfett, 0},
	[GSfettchase1] {uboss, nil, 10, SPfettwalk1, stt+GSfettchase2, 0},
	[GSfettchase2] {nil, nil, 3, SPfettwalk1, stt+GSfettchase3, 0},
	[GSfettchase3] {uboss, nil, 8, SPfettwalk2, stt+GSfettchase4, 0},
	[GSfettchase4] {uboss, nil, 10, SPfettwalk3, stt+GSfettchase5, 0},
	[GSfettchase5] {nil, nil, 3, SPfettwalk3, stt+GSfettchase6, 0},
	[GSfettchase6] {uboss, nil, 8, SPfettwalk4, stt+GSfettchase1, 0},
	[GSfettfire1] {nil, nil, 30, SPfettfire1, stt+GSfettfire2, 0},
	[GSfettfire2] {nil, launch, 10, SPfettfire2, stt+GSfettfire3, 0},
	[GSfettfire3] {nil, fire, 10, SPfettfire3, stt+GSfettfire4, 0},
	[GSfettfire4] {nil, fire, 10, SPfettfire4, stt+GSfettfire5, 0},
	[GSfettfire5] {nil, fire, 10, SPfettfire3, stt+GSfettfire6, 0},
	[GSfettfire6] {nil, fire, 10, SPfettfire4, stt+GSfettchase1, 0},
	[GSfettcam] {nil, nil, 1, SPfettwalk1, stt+GSfettdie1, 0},
	[GSfettdie1] {nil, yelp, 1, SPfettwalk1, stt+GSfettdie2, 0},
	[GSfettdie2] {nil, nil, 10, SPfettwalk1, stt+GSfettdie3, 0},
	[GSfettdie3] {nil, nil, 10, SPfettdie1, stt+GSfettdie4, 0},
	[GSfettdie4] {nil, nil, 10, SPfettdie2, stt+GSfettdie5, 0},
	[GSfettdie5] {nil, nil, 10, SPfettdie3, stt+GSfettdie6, 0},
	[GSfettdie6] {nil, cam, 20, SPfettdead, stt+GSfettdie6, 0},
	[GSfake] {uwait, nil, 0, SPfakewalk1, stt+GSfake, 0},
	[GSfakechase1] {ufake, nil, 10, SPfakewalk1, stt+GSfakechase2, 0},
	[GSfakechase2] {nil, nil, 3, SPfakewalk1, stt+GSfakechase3, 0},
	[GSfakechase3] {ufake, nil, 8, SPfakewalk2, stt+GSfakechase4, 0},
	[GSfakechase4] {ufake, nil, 10, SPfakewalk3, stt+GSfakechase5, 0},
	[GSfakechase5] {nil, nil, 3, SPfakewalk3, stt+GSfakechase6, 0},
	[GSfakechase6] {ufake, nil, 8, SPfakewalk4, stt+GSfakechase1, 0},
	[GSfakefire1] {nil, launch, 8, SPfakefire, stt+GSfakefire2, 0},
	[GSfakefire2] {nil, launch, 8, SPfakefire, stt+GSfakefire3, 0},
	[GSfakefire3] {nil, launch, 8, SPfakefire, stt+GSfakefire4, 0},
	[GSfakefire4] {nil, launch, 8, SPfakefire, stt+GSfakefire5, 0},
	[GSfakefire5] {nil, launch, 8, SPfakefire, stt+GSfakefire6, 0},
	[GSfakefire6] {nil, launch, 8, SPfakefire, stt+GSfakefire7, 0},
	[GSfakefire7] {nil, launch, 8, SPfakefire, stt+GSfakefire8, 0},
	[GSfakefire8] {nil, launch, 8, SPfakefire, stt+GSfakefire9, 0},
	[GSfakefire9] {nil, nil, 8, SPfakefire, stt+GSfakechase1, 0},
	[GSfakedie1] {nil, yelp, 10, SPfakedie1, stt+GSfakedie2, 0},
	[GSfakedie2] {nil, nil, 10, SPfakedie2, stt+GSfakedie3, 0},
	[GSfakedie3] {nil, nil, 10, SPfakedie3, stt+GSfakedie4, 0},
	[GSfakedie4] {nil, nil, 10, SPfakedie4, stt+GSfakedie5, 0},
	[GSfakedie5] {nil, nil, 10, SPfakedie5, stt+GSfakedie6, 0},
	[GSfakedie6] {nil, nil, 0, SPfakedead, stt+GSfakedie6, 0},
	[GSmech] {uwait, nil, 0, SPmechwalk1, stt+GSmech, 0},
	[GSmechchase1] {uchase, mechsfx, 10, SPmechwalk1, stt+GSmechchase2, 0},
	[GSmechchase2] {nil, nil, 6, SPmechwalk1, stt+GSmechchase3, 0},
	[GSmechchase3] {uchase, nil, 8, SPmechwalk2, stt+GSmechchase4, 0},
	[GSmechchase4] {uchase, mechsfx, 10, SPmechwalk3, stt+GSmechchase5, 0},
	[GSmechchase5] {nil, nil, 6, SPmechwalk3, stt+GSmechchase6, 0},
	[GSmechchase6] {uchase, nil, 8, SPmechwalk4, stt+GSmechchase1, 0},
	[GSmechfire1] {nil, nil, 30, SPmechfire1, stt+GSmechfire2, 0},
	[GSmechfire2] {nil, fire, 10, SPmechfire2, stt+GSmechfire3, 0},
	[GSmechfire3] {nil, fire, 10, SPmechfire3, stt+GSmechfire4, 0},
	[GSmechfire4] {nil, fire, 10, SPmechfire2, stt+GSmechfire5, 0},
	[GSmechfire5] {nil, fire, 10, SPmechfire3, stt+GSmechfire6, 0},
	[GSmechfire6] {nil, fire, 10, SPmechfire2, stt+GSmechchase1, 0},
	[GSmechdie1] {nil, yelp, 10, SPmechdie1, stt+GSmechdie2, 0},
	[GSmechdie2] {nil, nil, 10, SPmechdie2, stt+GSmechdie3, 0},
	[GSmechdie3] {nil, mechblow, 10, SPmechdie3, stt+GSmechdie4, 0},
	[GSmechdie4] {nil, nil, 0, SPmechdead, stt+GSmechdie4, 0},
	[GShitlerchase1] {uchase, nil, 6, SPhitlerwalk1, stt+GShitlerchase2, 0},
	[GShitlerchase2] {nil, nil, 4, SPhitlerwalk1, stt+GShitlerchase3, 0},
	[GShitlerchase3] {uchase, nil, 2, SPhitlerwalk2, stt+GShitlerchase4, 0},
	[GShitlerchase4] {uchase, nil, 6, SPhitlerwalk3, stt+GShitlerchase5, 0},
	[GShitlerchase5] {nil, nil, 4, SPhitlerwalk3, stt+GShitlerchase6, 0},
	[GShitlerchase6] {uchase, nil, 2, SPhitlerwalk4, stt+GShitlerchase1, 0},
	[GShitlerfire1] {nil, nil, 30, SPhitlerfire1, stt+GShitlerfire2, 0},
	[GShitlerfire2] {nil, fire, 10, SPhitlerfire2, stt+GShitlerfire3, 0},
	[GShitlerfire3] {nil, fire, 10, SPhitlerfire3, stt+GShitlerfire4, 0},
	[GShitlerfire4] {nil, fire, 10, SPhitlerfire2, stt+GShitlerfire5, 0},
	[GShitlerfire5] {nil, fire, 10, SPhitlerfire3, stt+GShitlerfire6, 0},
	[GShitlerfire6] {nil, fire, 10, SPhitlerfire2, stt+GShitlerchase1, 0},
	[GShitlercam] {nil, nil, 10, SPhitlerwalk1, stt+GShitlerdie1, 0},
	[GShitlerdie1] {nil, yelp, 1, SPhitlerwalk1, stt+GShitlerdie2, 0},
	[GShitlerdie2] {nil, nil, 10, SPhitlerwalk1, stt+GShitlerdie3, 0},
	[GShitlerdie3] {nil, slurp, 10, SPhitlerdie1, stt+GShitlerdie4, 0},
	[GShitlerdie4] {nil, nil, 10, SPhitlerdie2, stt+GShitlerdie5, 0},
	[GShitlerdie5] {nil, nil, 10, SPhitlerdie3, stt+GShitlerdie6, 0},
	[GShitlerdie6] {nil, nil, 10, SPhitlerdie4, stt+GShitlerdie7, 0},
	[GShitlerdie7] {nil, nil, 10, SPhitlerdie5, stt+GShitlerdie8, 0},
	[GShitlerdie8] {nil, nil, 10, SPhitlerdie6, stt+GShitlerdie9, 0},
	[GShitlerdie9] {nil, nil, 10, SPhitlerdie7, stt+GShitlerdie10, 0},
	[GShitlerdie10] {nil, cam, 20, SPhitlerdead, stt+GShitlerdie10, 0},
	[GSgh1chase1] {ughost, nil, 10, SPgh1walk1, stt+GSgh1chase2, 0},
	[GSgh2chase1] {ughost, nil, 10, SPgh3walk1, stt+GSgh2chase2, 0},
	[GSgh3chase1] {ughost, nil, 10, SPgh2walk1, stt+GSgh3chase2, 0},
	[GSgh4chase1] {ughost, nil, 10, SPgh2walk1, stt+GSgh4chase2, 0},
	[GSgh1chase2] {ughost, nil, 10, SPgh1walk2, stt+GSgh1chase1, 0},
	[GSgh2chase2] {ughost, nil, 10, SPgh3walk2, stt+GSgh2chase1, 0},
	[GSgh3chase2] {ughost, nil, 10, SPgh2walk2, stt+GSgh3chase1, 0},
	[GSgh4chase2] {ughost, nil, 10, SPgh2walk2, stt+GSgh4chase1, 0},
	[GStrans] {uwait, nil, 0, SPtranswalk1, stt+GStrans, 0},
	[GStranschase1] {uchase, nil, 10, SPtranswalk1, stt+GStranschase2, 0},
	[GStranschase2] {nil, nil, 3, SPtranswalk1, stt+GStranschase3, 0},
	[GStranschase3] {uchase, nil, 8, SPtranswalk2, stt+GStranschase4, 0},
	[GStranschase4] {uchase, nil, 10, SPtranswalk3, stt+GStranschase5, 0},
	[GStranschase5] {nil, nil, 3, SPtranswalk3, stt+GStranschase6, 0},
	[GStranschase6] {uchase, nil, 8, SPtranswalk4, stt+GStranschase1, 0},
	[GStransfire1] {nil, nil, 30, SPtransfire1, stt+GStransfire2, 0},
	[GStransfire2] {nil, fire, 10, SPtransfire2, stt+GStransfire3, 0},
	[GStransfire3] {nil, fire, 10, SPtransfire3, stt+GStransfire4, 0},
	[GStransfire4] {nil, fire, 10, SPtransfire2, stt+GStransfire5, 0},
	[GStransfire5] {nil, fire, 10, SPtransfire3, stt+GStransfire6, 0},
	[GStransfire6] {nil, fire, 10, SPtransfire2, stt+GStransfire7, 0},
	[GStransfire7] {nil, fire, 10, SPtransfire3, stt+GStransfire8, 0},
	[GStransfire8] {nil, nil, 10, SPtransfire1, stt+GStranschase1, 0},
	[GStransdie1] {nil, yelp, 1, SPtranswalk1, stt+GStransdie2, 0},
	[GStransdie2] {nil, nil, 1, SPtranswalk1, stt+GStransdie3, 0},
	[GStransdie3] {nil, nil, 15, SPtransdie1, stt+GStransdie4, 0},
	[GStransdie4] {nil, nil, 15, SPtransdie2, stt+GStransdie5, 0},
	[GStransdie5] {nil, nil, 15, SPtransdie3, stt+GStransdie6, 0},
	[GStransdie6] {nil, nil, 0, SPtransdead, stt+GStransdie6, 0},
	[GSwilh] {uwait, nil, 0, SPwilhwalk1, stt+GSwilh, 0},
	[GSwilhchase1] {uboss, nil, 10, SPwilhwalk1, stt+GSwilhchase2, 0},
	[GSwilhchase2] {nil, nil, 3, SPwilhwalk1, stt+GSwilhchase3, 0},
	[GSwilhchase3] {uboss, nil, 8, SPwilhwalk2, stt+GSwilhchase4, 0},
	[GSwilhchase4] {uboss, nil, 10, SPwilhwalk3, stt+GSwilhchase5, 0},
	[GSwilhchase5] {nil, nil, 3, SPwilhwalk3, stt+GSwilhchase6, 0},
	[GSwilhchase6] {uboss, nil, 8, SPwilhwalk4, stt+GSwilhchase1, 0},
	[GSwilhfire1] {nil, nil, 30, SPwilhfire1, stt+GSwilhfire2, 0},
	[GSwilhfire2] {nil, launch, 10, SPwilhfire2, stt+GSwilhfire3, 0},
	[GSwilhfire3] {nil, fire, 10, SPwilhfire3, stt+GSwilhfire4, 0},
	[GSwilhfire4] {nil, fire, 10, SPwilhfire4, stt+GSwilhfire5, 0},
	[GSwilhfire5] {nil, fire, 10, SPwilhfire3, stt+GSwilhfire6, 0},
	[GSwilhfire6] {nil, fire, 10, SPwilhfire4, stt+GSwilhchase1, 0},
	[GSwilhdie1] {nil, yelp, 1, SPwilhwalk1, stt+GSwilhdie2, 0},
	[GSwilhdie2] {nil, nil, 10, SPwilhwalk1, stt+GSwilhdie3, 0},
	[GSwilhdie3] {nil, nil, 10, SPwilhdie1, stt+GSwilhdie4, 0},
	[GSwilhdie4] {nil, nil, 10, SPwilhdie2, stt+GSwilhdie5, 0},
	[GSwilhdie5] {nil, nil, 10, SPwilhdie3, stt+GSwilhdie6, 0},
	[GSwilhdie6] {nil, nil, 20, SPwilhdead, stt+GSwilhdie6, 0},
	[GSuber] {uwait, nil, 0, SPuberwalk1, stt+GSuber, 0},
	[GSuberchase1] {uchase, nil, 10, SPuberwalk1, stt+GSuberchase2, 0},
	[GSuberchase2] {nil, nil, 3, SPuberwalk1, stt+GSuberchase3, 0},
	[GSuberchase3] {uchase, nil, 8, SPuberwalk2, stt+GSuberchase4, 0},
	[GSuberchase4] {uchase, nil, 10, SPuberwalk3, stt+GSuberchase5, 0},
	[GSuberchase5] {nil, nil, 3, SPuberwalk3, stt+GSuberchase6, 0},
	[GSuberchase6] {uchase, nil, 8, SPuberwalk4, stt+GSuberchase1, 0},
	[GSuberfire1] {nil, nil, 30, SPuberfire1, stt+GSuberfire2, 0},
	[GSuberfire2] {nil, uberfire, 12, SPuberfire2, stt+GSuberfire3, 0},
	[GSuberfire3] {nil, uberfire, 12, SPuberfire3, stt+GSuberfire4, 0},
	[GSuberfire4] {nil, uberfire, 12, SPuberfire4, stt+GSuberfire5, 0},
	[GSuberfire5] {nil, uberfire, 12, SPuberfire3, stt+GSuberfire6, 0},
	[GSuberfire6] {nil, uberfire, 12, SPuberfire2, stt+GSuberfire7, 0},
	[GSuberfire7] {nil, nil, 12, SPuberfire1, stt+GSuberchase1, 0},
	[GSuberdie1] {nil, yelp, 1, SPuberwalk1, stt+GSuberdie2, 0},
	[GSuberdie2] {nil, nil, 1, SPuberwalk1, stt+GSuberdie3, 0},
	[GSuberdie3] {nil, nil, 15, SPuberdie1, stt+GSuberdie4, 0},
	[GSuberdie4] {nil, nil, 15, SPuberdie2, stt+GSuberdie5, 0},
	[GSuberdie5] {nil, nil, 15, SPuberdie3, stt+GSuberdie6, 0},
	[GSuberdie6] {nil, nil, 15, SPuberdie4, stt+GSuberdie7, 0},
	[GSuberdie7] {nil, nil, 0, SPuberdead, stt+GSuberdie7, 0},
	[GSknight] {uwait, nil, 0, SPknightwalk1, stt+GSknight, 0},
	[GSknightchase1] {uboss, nil, 10, SPknightwalk1, stt+GSknightchase2, 0},
	[GSknightchase2] {nil, nil, 3, SPknightwalk1, stt+GSknightchase3, 0},
	[GSknightchase3] {uboss, nil, 8, SPknightwalk2, stt+GSknightchase4, 0},
	[GSknightchase4] {uboss, nil, 10, SPknightwalk3, stt+GSknightchase5, 0},
	[GSknightchase5] {nil, nil, 3, SPknightwalk3, stt+GSknightchase6, 0},
	[GSknightchase6] {uboss, nil, 8, SPknightwalk4, stt+GSknightchase1, 0},
	[GSknightfire1] {nil, nil, 30, SPknightfire1, stt+GSknightfire2, 0},
	[GSknightfire2] {nil, launch, 10, SPknightfire2, stt+GSknightfire3, 0},
	[GSknightfire3] {nil, fire, 10, SPknightfire4, stt+GSknightfire4, 0},
	[GSknightfire4] {nil, launch, 10, SPknightfire3, stt+GSknightfire5, 0},
	[GSknightfire5] {nil, fire, 10, SPknightfire4, stt+GSknightchase1, 0},
	[GSknightdie1] {nil, yelp, 1, SPknightwalk1, stt+GSknightdie2, 0},
	[GSknightdie2] {nil, nil, 10, SPknightwalk1, stt+GSknightdie3, 0},
	[GSknightdie3] {nil, nil, 10, SPknightdie1, stt+GSknightdie4, 0},
	[GSknightdie4] {nil, nil, 10, SPknightdie2, stt+GSknightdie5, 0},
	[GSknightdie5] {nil, nil, 10, SPknightdie3, stt+GSknightdie6, 0},
	[GSknightdie6] {nil, nil, 10, SPknightdie4, stt+GSknightdie7, 0},
	[GSknightdie7] {nil, nil, 10, SPknightdie5, stt+GSknightdie8, 0},
	[GSknightdie8] {nil, nil, 10, SPknightdie6, stt+GSknightdie9, 0},
	[GSknightdie9] {nil, nil, 0, SPknightdead, stt+GSknightdie9, 0},
	[GSspectrewait1] {uwait, nil, 10, SPspectrewalk1, stt+GSspectrewait2, 0},
	[GSspectrewait2] {uwait, nil, 10, SPspectrewalk2, stt+GSspectrewait3, 0},
	[GSspectrewait3] {uwait, nil, 10, SPspectrewalk3, stt+GSspectrewait4, 0},
	[GSspectrewait4] {uwait, nil, 10, SPspectrewalk4, stt+GSspectrewait1, 0},
	[GSspectrewake] {nil, wake, 10, SPspectreF4, stt+GSspectrewake, 0},
	[GSspectrechase1] {ughost, nil, 10, SPspectrewalk1, stt+GSspectrechase2, 0},
	[GSspectrechase2] {ughost, nil, 10, SPspectrewalk2, stt+GSspectrechase3, 0},
	[GSspectrechase3] {ughost, nil, 10, SPspectrewalk3, stt+GSspectrechase4, 0},
	[GSspectrechase4] {ughost, nil, 10, SPspectrewalk4, stt+GSspectrechase1, 0},
	[GSspectredie1] {nil, nil, 10, SPspectreF1, stt+GSspectredie2, 0},
	[GSspectredie2] {nil, nil, 10, SPspectreF2, stt+GSspectredie3, 0},
	[GSspectredie3] {nil, nil, 10, SPspectreF3, stt+GSspectredie4, 0},
	[GSspectredie4] {nil, nil, 300, SPspectreF4, stt+GSspectrewake, 0},
	[GSangel] {uwait, nil, 0, SPangelwalk1, stt+GSangel, 0},
	[GSangelchase1] {uboss, nil, 10, SPangelwalk1, stt+GSangelchase2, 0},
	[GSangelchase2] {nil, nil, 3, SPangelwalk1, stt+GSangelchase3, 0},
	[GSangelchase3] {uboss, nil, 8, SPangelwalk2, stt+GSangelchase4, 0},
	[GSangelchase4] {uboss, nil, 10, SPangelwalk3, stt+GSangelchase5, 0},
	[GSangelchase5] {nil, nil, 3, SPangelwalk3, stt+GSangelchase6, 0},
	[GSangelchase6] {uboss, nil, 8, SPangelwalk4, stt+GSangelchase1, 0},
	[GSangelfire1] {nil, prelaunch, 10, SPangelfire1, stt+GSangelfire2, 0},
	[GSangelfire2] {nil, launch, 20, SPangelfire2, stt+GSangelfire3, 0},
	[GSangelfire3] {nil, relaunch, 10, SPangelfire1, stt+GSangelfire2, 0},
	[GSangeldie1] {nil, yelp, 1, SPangelwalk1, stt+GSangeldie2, 0},
	[GSangeldie2] {nil, nil, 1, SPangelwalk1, stt+GSangeldie3, 0},
	[GSangeldie3] {nil, slurp, 10, SPangeldie1, stt+GSangeldie4, 0},
	[GSangeldie4] {nil, nil, 10, SPangeldie2, stt+GSangeldie5, 0},
	[GSangeldie5] {nil, nil, 10, SPangeldie3, stt+GSangeldie6, 0},
	[GSangeldie6] {nil, nil, 10, SPangeldie4, stt+GSangeldie7, 0},
	[GSangeldie7] {nil, nil, 10, SPangeldie5, stt+GSangeldie8, 0},
	[GSangeldie8] {nil, nil, 10, SPangeldie6, stt+GSangeldie9, 0},
	[GSangeldie9] {nil, nil, 10, SPangeldie7, stt+GSangeldie10, 0},
	[GSangeldie10] {nil, victory, 130, SPangeldead, stt+GSangeldie10, 0},
	[GSangeltired1] {nil, tiredsfx, 40, SPangeltired1, stt+GSangeltired2, 0},
	[GSangeltired2] {nil, nil, 40, SPangeltired2, stt+GSangeltired3, 0},
	[GSangeltired3] {nil, tiredsfx, 40, SPangeltired1, stt+GSangeltired4, 0},
	[GSangeltired4] {nil, nil, 40, SPangeltired2, stt+GSangeltired5, 0},
	[GSangeltired5] {nil, tiredsfx, 40, SPangeltired1, stt+GSangeltired6, 0},
	[GSangeltired6] {nil, nil, 40, SPangeltired2, stt+GSangeltired7, 0},
	[GSangeltired7] {nil, tiredsfx, 40, SPangeltired1, stt+GSangelchase1, 0},
	[GSmissile] {uprj, smoke, 3, SPmissile1, stt+GSmissile, 1},
	[GSmsmoke1] {nil, nil, 3, SPmsmoke1, stt+GSmsmoke2, 0},
	[GSmsmoke2] {nil, nil, 3, SPmsmoke2, stt+GSmsmoke3, 0},
	[GSmsmoke3] {nil, nil, 3, SPmsmoke3, stt+GSmsmoke4, 0},
	[GSmsmoke4] {nil, nil, 3, SPmsmoke4, nil, 0},
	[GSmboom1] {nil, nil, 6, SPmboom1, stt+GSmboom2, 0},
	[GSmboom2] {nil, nil, 6, SPmboom2, stt+GSmboom3, 0},
	[GSmboom3] {nil, nil, 6, SPmboom3, nil, 0},
	[GSrocket] {uprj, smoke, 3, SProcket1, stt+GSrocket, 1},
	[GSrsmoke1] {nil, nil, 3, SPrsmoke1, stt+GSrsmoke2, 0},
	[GSrsmoke2] {nil, nil, 3, SPrsmoke2, stt+GSrsmoke3, 0},
	[GSrsmoke3] {nil, nil, 3, SPrsmoke3, stt+GSrsmoke4, 0},
	[GSrsmoke4] {nil, nil, 3, SPrsmoke4, nil, 0},
	[GSrboom1] {nil, nil, 6, SPrboom1, stt+GSrboom2, 0},
	[GSrboom2] {nil, nil, 6, SPrboom2, stt+GSrboom3, 0},
	[GSrboom3] {nil, nil, 6, SPrboom3, nil, 0},
	[GSflame1] {nil, uprj, 6, SPflame1, stt+GSflame2, 0},
	[GSflame2] {nil, uprj, 6, SPflame2, stt+GSflame1, 0},
	[GSneedle1] {uprj, nil, 6, SPneedle1, stt+GSneedle2, 0},
	[GSneedle2] {uprj, nil, 6, SPneedle2, stt+GSneedle3, 0},
	[GSneedle3] {uprj, nil, 6, SPneedle3, stt+GSneedle4, 0},
	[GSneedle4] {uprj, nil, 6, SPneedle4, stt+GSneedle1, 0},
	[GSspark1] {uprj, nil, 6, SPspark1, stt+GSspark2, 0},
	[GSspark2] {uprj, nil, 6, SPspark2, stt+GSspark3, 0},
	[GSspark3] {uprj, nil, 6, SPspark3, stt+GSspark4, 0},
	[GSspark4] {uprj, nil, 6, SPspark4, stt+GSspark1, 0}
};

static void
upal(void)
{
	int step;

	if(dmgtc > 0){
		step = dmgtc / 10;
		if(step > Cwht - Cred - 1)
			step = Cwht - Cred - 1;
		dmgtc -= Δtc;
		if(dmgtc < 0)
			dmgtc = 0;
		if(step > 0)
			pal = pals[Cred + step];
	}else if(bonustc > 0){
		step = bonustc / 6;
		if(step > Cfad - Cwht - 1)
			step = Cfad - Cwht - 1;
		bonustc -= Δtc;
		if(bonustc < 0)
			bonustc = 0;
		if(step > 0)
			pal = pals[Cwht + step];
	}else
		pal = pals[C0];
}

static void
crmchop(void)
{
	gm.tc = 99 * 60 * Tb;
	gm.pt = 0;
	hudp();
	bonustc = 6 * (Cfad-Cwht);
	dirty = 1;
}
static void
crmrcv(void)
{
	allrecv++;
	sfx(Snobonus);
}
static void
crmmed(void)
{
	giveh(100);
	sfx(Shealth2);
	crmchop();
}
static void
crmamo(void)
{
	givea(99);
	sfx(Sgetammo);
	crmchop();
}
static void
crmkey(void)
{
	givek(0);
	givek(1);
	sfx(Sgetkey);
	crmchop();
}
static void
crmwep(void)
{
	givew(WPgatling);
	sfx(Sgetgatling);
	crmchop();
}
static void
crmmli(void)
{
	giveh(100);
	givea(99);
	givek(0);
	givek(1);
	givew(WPgatling);
	sfx(Sgetgatling);
	crmchop();
}
static void
crmgod(void)
{
	god ^= 1;
	if(god){
		sfx(Sendb2);
		crmchop();
	}else
		sfx(Snobonus);
}
static void
crmclp(void)
{
	noclip ^= 1;
	if(noclip){
		sfx(Sbonus4);
		crmchop();
	}else
		sfx(Snobonus);
}
static void
crmslo(void)
{
	slomo ^= 1;
	if(slomo){
		sfx(Sbonus4);
		crmchop();
	}else
		sfx(Snobonus);
}
static void
crmskp(void)
{
	gm.end = EDup;
	if(ver < SDM && gm.map % 10 == 8 || ver >= SDM && gm.map == 20)
		gm.end = EDwon;
	sfx(Slvlend);
	crmchop();
}
static void
crmmus(void)
{
	mus(nrand(ver < SDM ? 27 : 24));
}

void
dieturn(void)
{
	int Δθ;

	Δθ = Δtc * 2;
	if(dieΔθ - Δθ < 0)
		Δθ = dieΔθ;
	dieΔθ -= Δθ;
	oplr->θ = (oplr->θ + Δθ * diedir + 360) % 360;
	render();
	out();
	if(dieΔθ != 0)
		qtc = 0;
	else
		pal = pals[C0];
}

void
die(void)
{
	int θ, lrot, rrot;
	double fθ;

	gm.w = WPnone;
	gm.lives--;
	stopmus();
	sfx(Sdie);
	fizzop(0x4, 0);
	fθ = atan2(oplr->y - killy, killx - oplr->x);
	if(fθ < 0)
		fθ += Fpi * 2;
	θ = fθ / (Fpi * 2) * 360;
	lrot = θ - oplr->θ;
	rrot = oplr->θ - θ;
	if(oplr->θ > θ)
		lrot += 360;
	else
		rrot += 360;
	if(lrot < rrot){
		diedir = 1;
		dieΔθ = abs(lrot % 360);
	}else{
		diedir = -1;
		dieΔθ = abs(rrot % 360);
	}
}

void
camwarp(void)
{
	int Δr;
	double θ;

	oplr->x = killx;
	oplr->y = killy;
	θ = atan2(oplr->y - camobj->y, camobj->x - oplr->x);
	if(θ < 0)
		θ = Fpi * 2 + θ;
	oplr->θ = θ / (Fpi * 2) * 360;
	Δr = 0x14000;
	do{
		oplr->x = camobj->x - ffs(Δr, cost[oplr->θ]);
		oplr->y = camobj->y + ffs(Δr, sint[oplr->θ]);
		Δr += 0x1000;
	}while(!trymove(oplr, Dplr, 1, 0));
	oplr->tx = oplr->x >> Dtlshift;
	oplr->ty = oplr->y >> Dtlshift;
	oplr->tl = tiles + oplr->ty * Mapdxy + oplr->tx;
	ostate(oplr, stt+GSplrcam);
	switch(camobj->type){
	case Oschb: ostate(camobj, stt+GSschbcam); break;
	case Ootto: ostate(camobj, stt+GSottocam); break;
	case Ofett: ostate(camobj, stt+GSfettcam); break;
	case Ohitler: ostate(camobj, stt+GShitlercam); break;
	}
}

void
givew(int n)
{
	givea(6);
	if(gm.bestw < n)
		gm.w = gm.lastw = gm.bestw = n;
	hudw();
	if(n == WPgatling){
		pic(136, 164, pict[Pgat]);
		facetc = 0;
	}
}

void
givep(int n)
{
	if(dirty)
		return;
	gm.pt += n;
	while(gm.pt >= gm.to1up){
		gm.to1up += 40000;
		givel();
	}
	hudp();
}

void
bonus(Static *s)
{
	if(gm.hp == 0)
		s->item = Rnil;
	switch(s->item){
	case Rstim:
		if(gm.hp == 100)
			return;
		sfx(Shealth2);
		giveh(25);
		break;
	case Rkey1:
	case Rkey2:
	case Rkey3:
	case Rkey4:
		sfx(Sgetkey);
		givek(s->item - Rkey1);
		break;
	case Rcross:
		sfx(Sbonus1);
		givep(100);
		gm.tp++;
		break;
	case Rchalice:
		sfx(Sbonus2);
		givep(500);
		gm.tp++;
		break;
	case Rbible:
		sfx(Sbonus3);
		givep(1000);
		gm.tp++;
		break;
	case Rcrown:
		sfx(Sbonus4);
		givep(5000);
		gm.tp++;
		break;
	case Rclip1:
		if(gm.ammo == 99)
			return;
		sfx(Sgetammo);
		givea(8);
		break;
	case Rclip2:
		if(gm.ammo == 99)
			return;
		sfx(Sgetammo);
		givea(4);
		break;
	case Rammobox:
		if(gm.ammo == 99)
			return;
		sfx(Sammobox);
		givea(25);
		break;
	case Rmg:
		sfx(Sgetmg);
		givew(WPmg);
		break;
	case Rchaingun:
		sfx(Sgetgatling);
		givew(WPgatling);
		break;
	case R1up:
		sfx(S1up);
		giveh(99);
		givea(25);
		givel();
		gm.tp++;
		break;
	case Rfood:
		if(gm.hp == 100)
			return;
		sfx(Shealth1);
		giveh(10);
		break;
	case Ralpo:
		if(gm.hp == 100)
			return;
		sfx(Shealth1);
		giveh(4);
		break;
	case Rgibs:
		if(gm.hp > 10)
			return;
		sfx(Sslurp);
		giveh(1);
		break;
	case Rspear:
		spearx = oplr->x;
		speary = oplr->y;
		spearθ = oplr->θ;
		gm.end = gm.demo || gm.record ? EDdem : EDspear;
	}
	bonustc = 6 * (Cfad-Cwht);
	s->tl = nil;
}

void
crm114(int n)
{
	static Crm crms[] = {
		{"fgd135", crmrcv},
		{"opeaid", crmmed},
		{"opepak", crmamo},
		{"opeopn", crmkey},
		{"opephz", crmwep},
		{"opemli", crmmli},
		{"opedqd", crmgod},
		{"opeclp", crmclp},
		{"opeslo", crmslo},
		{"opeskp", crmskp},
		{"opemus", crmmus}
	};
	Crm *p, *e;

	p = crms;
	e = crms + 1;
	if(allrecv){
		p++;
		e = crms + nelem(crms);
	}
	while(p < e){
		if(strncmp(crs, p->s, n) == 0){
			if(n == Ncrm-1){
				p->f();
				memset(crs, 0, sizeof crs);
			}
			return;
		}
		p++;
	}
	memset(crs, 0, sizeof crs);
}

void
eatcs(void)
{
	int i, d;
	char *p, rc[UTFmax];
	Rune r;

	if(gm.demo){
		if(nbrecv(csc, nil) > 0)
			gm.end = EDkey;
		return;
	}
	i = 0;
	p = crs + strlen(crs);
	while(nbrecv(csc, &r) > 0 && i++ < Ncrm-1){
		if(quickkey(r))
			return;
		if(gm.record)
			continue;
		d = runetochar(rc, &r);
		if(p + d < crs + sizeof crs){
			memcpy(p, rc, d);
			p += d;
			crm114(p - crs);
		}
	}
}

int
rnd(void)
{
	rndi = rndi+1 & 0xff;
	return rndt[rndi];
}

void
gstep(void)
{
	if(gm.demo || gm.record || slomo){
		if(demfrm-- != 0)
			return;
		demfrm = 3;
		Δtc = slomo ? 1 : 4;
	}
	input();
	noise = 0;
	uworld();
	upal();
	render();
	qtc += Δtc;
	gm.tc += Δtc;
	if(dofizz){
		dofizz = 0;
		if(!gm.end)
			gm.end = EDfizz;
		gend();
		return;
	}
	if(ver >= SDM)
		idleface();
	out();
	eatcs();
	if(gm.end)
		gend();
}

void
nextmap(void)
{
	static int setece[] = {1, 11, 27, 33, 45, 53};
	int n, e, m;

	m = gm.map;
	n = m + 1;
	if(ver < SDM){
		e = m / 10;
		if(gm.com == GMret)
			n = setece[e];
		else if(gm.com == GMsetec)
			n = e * 10 + 9;
	}else{
		if(gm.com == GMret)
			n = m == 18 ? 4 : m == 19 ? 12 : m + 1;
		else if(gm.com == GMsetec)
			n = m == 3 ? 18 : m == 11 ? 19 : m + 1;
	}
	gm.map = n;
}

void
game(void)
{
	if(!loaded){
		initmap();
		killx = oplr->x;
		killy = oplr->y;
		mapmus();
	}
	pal = pals[C0];
	dofizz++;
	step = gstep;
}

void
spshunt(void)
{
	gm.oldpt = gm.pt;
	gm.map = 20;
	dmgtc = bonustc = 0;
	initmap();
	sfx(Sspear);
	oplr->x = spearx;
	oplr->y = speary;
	oplr->tx = spearx >> Dtlshift;
	oplr->ty = speary >> Dtlshift;
	oplr->θ = spearθ;
	oplr->areaid = oplr->tl->p0 - MTfloor;
}

void
wrgm(void)
{
	disking();
	pack("wwdddwwwwwwwwwwwwwwwwwdddwwwdbb", gm.difc, gm.map, gm.oldpt,
		gm.pt, gm.to1up, gm.lives, gm.hp, gm.ammo, gm.keys, gm.bestw,
		gm.w, gm.lastw, gm.facefrm, atkfrm, atktc, gm.wfrm, gm.sp,
		gm.tp, gm.kp, gm.stot, gm.ttot, gm.ktot, gm.tc, killx, killy,
		gm.epk, gm.eps, gm.ept, gm.eptm, dirty, firing);
}

void
ldgm(void)
{
	disking();
	unpack("wwdddwwwwwwwwwswwwwwwwdddwwwdbb", &gm.difc, &gm.map, &gm.oldpt,
		&gm.pt, &gm.to1up, &gm.lives, &gm.hp, &gm.ammo, &gm.keys,
		&gm.bestw, &gm.w, &gm.lastw, &gm.facefrm, &atkfrm, &atktc,
		&gm.wfrm, &gm.sp, &gm.tp, &gm.kp, &gm.stot, &gm.ttot, &gm.ktot,
		&gm.tc, &killx, &killy, &gm.epk, &gm.eps, &gm.ept, &gm.eptm,
		&dirty, &firing);
}

void
greset(void)
{
	if(gm.w == WPnone){
		gm.hp = 100;
		gm.ammo = 8;
		gm.w = gm.lastw = gm.bestw = WPpistol;
	}
	gm.pt = gm.oldpt;
	gm.tc = 0;
	gm.kp = gm.sp = gm.tp = 0;
	gm.ktot = gm.stot = gm.ttot = 0;
	gm.wfrm = gm.facefrm = 0;
	gm.keys = 0;
	gm.won = gm.mut = 0;
	dmgtc = bonustc = facetc = funtc = 0;
	firing = 0;
	kb = 0;
	mb = 0;
	kon = kold = 0;
	mΔx = mΔy = 0;
	kΔθ = 0;
	allrecv = 0;
	dirty = 0;
	slomo = noclip = god = 0;
	if(ver == SOD && gm.map == 20)
		givek(0);
	loaded = 0;
}

void
ginit(uchar *p, int m, int d)
{
	int r;

	r = 1;
	memset(&gm, 0, sizeof gm);
	if(p != nil){
		gm.demo++;
		gm.difc = GDhard;
		gm.map = p[0];
		deme = (char*)(p + (p[2]<<8 | p[1]));
		dem = (char*)p + 4;
		if((deme-dem) % 3 != 0)
			sysfatal("initd: invalid demo lump\n");
		demfrm = 0;
		r = 0;
		greset();
	}else if(m != -1){
		gm.map = m;
		gm.difc = d;
	}
	rndi = r ? time(nil) & 0xff : 0;
	gm.hp = 100;
	gm.ammo = 8;
	gm.lives = 3;
	gm.w = gm.lastw = gm.bestw = WPpistol;
	gm.to1up = GPextra;
}