
#include "game.h"
#include "bomber.h"
#include "draw.h"
#include "gfx.h"
#include "list.h"
#include "menu.h"
#include "network.h"
#include "sound.h"
#include "utils.h"

static int gameframe;

static listhead activebombs;
static listhead activedecays;
static listhead activebonus;
static listhead activegeneric;
static listhead detonatebombs;
static listhead activeflames;
static listhead activeplayers;
static listhead allplayers;

figure walking[MAXSETS][NUMWALKFRAMES];
solid background, backgroundoriginal;

/* The playfield array, contains FIELD_* equates */
unsigned char field[32][32];
void* info[32][32];

volatile char exitflag = 0;
static int framecount = 0;

char playername[16];

static int mycount;

static int bonustotal;
static const int bonuschances[] = {TILE_BOMB, 20, TILE_FLAME, 20, TILE_TRIGGER, 2, TILE_GOLDFLAME, 2, TILE_SKATES, 20, TILE_TURTLE, 5, TILE_NONE, 160};

static GameOptions gameoptions;

static const unsigned char playerpositions[MAXNETNODES * 3] = {
	/* color, x, y */
	2, 0, 0, 3, 14, 10, 4, 14, 0, 5, 0, 10, 1, 6, 0, 6, 8, 10, 7, 0, 6, 8, 14, 4,
};

static void resetplayer(player* pl, int color, int x, int y) {
	pl->speed = SPEEDSTART;
	pl->flags = 0;
	pl->flamelength = gameoptions.flames + 1;
	pl->bombsavailable = gameoptions.bombs + 1;

	pl->color = color;

	pl->xpos = arraytoscreenx(x);
	pl->ypos = arraytoscreeny(y);

	field[y][x] = FIELD_EMPTY;
	if (x) field[y][x - 1] = FIELD_EMPTY;
	if (y) field[y - 1][x] = FIELD_EMPTY;
	if (x < arraynumx - 1) field[y][x + 1] = FIELD_EMPTY;
	if (y < arraynumy - 1) field[y + 1][x] = FIELD_EMPTY;

	addtail(&activeplayers, pl);
}

static void initplayer(int color, int x, int y, int controller) {
	player* pl;

	pl = allocentry(player);
	if (!pl) nomem("Couldn't get player structure (allocentry())");

	list_add_tail(&allplayers, &pl->list_all_players);

	pl->controller = controller;
	pl->fixx = -4;
	pl->fixy = -40;
	pl->kills = pl->deaths = 0;

	resetplayer(pl, color, x, y);
}

static void initplayers(void) {
	int i;
	const unsigned char* p;
	int c, x, y;

	if (NETWORK_NONE == network) {
		initplayer(2, 0, 0, -1);
		return;
	}
	p = playerpositions;
	for (i = 0; i < MAXNETNODES; ++i) {
		if (!netnodes[i].used) continue;
		c = *p++;
		x = *p++;
		y = *p++;
		initplayer(c, x, y, i);
	}
}

static void resetplayers(void) {
	const unsigned char* p;
	int c, x, y;
	player* pl;

	p = playerpositions;
	list_for_each_entry(pl, &allplayers, list_all_players) {
		c = *p++;
		x = *p++;
		y = *p++;
		resetplayer(pl, c, x, y);
	}
}

static void firstzero(void) {
	alloc_things();
	list_init_head(&activebombs);
	list_init_head(&detonatebombs);
	list_init_head(&activeflames);
	list_init_head(&activedecays);
	list_init_head(&activebonus);
	list_init_head(&activeplayers);
	list_init_head(&activegeneric);
	list_init_head(&allplayers);

	mycount = mydatacount = 0;
	memset(latestactions, 0, sizeof(latestactions));
	memset(latestcounts, 0, sizeof(latestcounts));
	memset(actionblock, 0, sizeof(actionblock));
	actioncount = 0;
}

static void initgame() {
	int i, j;
	int x, y;
	int bl;
	const int* p;
	int comp;

	if (network != NETWORK_SLAVE) set_game_options(&configopts);
	gameframe = 0;

	things_list_clear(&activebombs);
	things_list_clear(&detonatebombs);
	things_list_clear(&activeflames);
	things_list_clear(&activedecays);
	things_list_clear(&activebonus);
	list_init_head(&activeplayers);
	things_list_clear(&activegeneric);

	p = bonuschances;
	bonustotal = 0;
	for (;;) {
		i = *p++;
		if (i == TILE_NONE) break;
		bonustotal += *p++;
	}
	bonustotal += 64 * (3 - gameoptions.generosity);
	memset(field, 0, sizeof(field));
	comp = gameoptions.density;
	for (j = 0; j < arraynumy; ++j)
		for (i = 0; i < arraynumx; ++i) {
			/*			if((i&j)&1) {
							field[j][i]=FIELD_BORDER;
						} else*/
			field[j][i] = (myrand() & 3) >= comp ? FIELD_BRICK : FIELD_EMPTY;
		}

	solidcopyany(&backgroundoriginal, &background, 0, 0, IXSIZE, IYSIZE);

	resetplayers();

	for (j = 0; j < arraynumy; ++j) {
		y = arraystarty + j * arrayspacey;
		for (i = 0; i < arraynumx; ++i) {
			x = arraystartx + i * arrayspacex;
			bl = field[j][i];
			if (bl == FIELD_BORDER)
				bl = 2;
			else if (bl == FIELD_BRICK)
				bl = 1;
			else
				continue;
			drawfigureany(x, y, blocks + bl, &background);
		}
	}
	solidcopy(&background, 0, 0, IXSIZE, IYSIZE);
	copyup();
}

static void addflame(player* owner, int px, int py) {
	flame *fl, *fl2;

	fl = allocentry(flame);
	if (!fl) return;
	addtail(&activeflames, fl);
	field[py][px] = FIELD_FLAME;
	info[py][px] = fl;
	fl->px = px;
	fl->py = py;
	fl->xpos = arraytoscreenx(px);
	fl->ypos = arraytoscreeny(py);
	fl->owner = owner;
	if (px && field[py][px - 1] == FIELD_FLAME) {
		fl2 = info[py][px - 1];
		fl->lurd |= FL_LEFT;
		fl2->lurd |= FL_RIGHT;
	}
	if (py && field[py - 1][px] == FIELD_FLAME) {
		fl2 = info[py - 1][px];
		fl->lurd |= FL_UP;
		fl2->lurd |= FL_DOWN;
	}
	if (px < arraynumx - 1 && field[py][px + 1] == FIELD_FLAME) {
		fl2 = info[py][px + 1];
		fl->lurd |= FL_RIGHT;
		fl2->lurd |= FL_LEFT;
	}
	if (py < arraynumy - 1 && field[py + 1][px] == FIELD_FLAME) {
		fl2 = info[py + 1][px];
		fl->lurd |= FL_DOWN;
		fl2->lurd |= FL_UP;
	}
}

static void dropbomb(player* pl, int px, int py, int type) {
	bomb* bmb;

	if (field[py][px] != FIELD_EMPTY) return;
	bmb = allocentry(bomb);
	if (!bmb) return;
	playsound(3);
	addtail(&activebombs, bmb);

	--(pl->bombsavailable);
	field[py][px] = FIELD_BOMB;
	info[py][px] = bmb;
	bmb->type = type;
	bmb->px = px;
	bmb->py = py;
	bmb->xpos = arraytoscreenx(px);
	bmb->ypos = arraytoscreeny(py);
	bmb->power = pl->flamelength;
	bmb->owner = pl;
}

static void adddecay(int px, int py) {
	brickdecay* bd;
	int xpos, ypos;

	bd = allocentry(brickdecay);
	if (!bd) return;
	field[py][px] = FIELD_EXPLODING;
	bd->xpos = arraytoscreenx(px);
	bd->ypos = arraytoscreeny(py);
	bd->px = px;
	bd->py = py;
	addtail(&activedecays, bd);
	xpos = tovideox(bd->xpos);
	ypos = tovideoy(bd->ypos);
	solidcopyany(&backgroundoriginal, &background, xpos, ypos, arrayspacex, arrayspacey);
	solidcopy(&background, xpos, ypos, arrayspacex, arrayspacey);
}

static void addbonus(int px, int py, int type) {
	bonustile* bonus;

	bonus = allocentry(bonustile);
	if (!bonus) return;
	addtail(&activebonus, bonus);
	bonus->px = px;
	bonus->py = py;
	bonus->xpos = arraytoscreenx(px);
	bonus->ypos = arraytoscreeny(py);
	bonus->type = type;
	field[py][px] = FIELD_BONUS;
	info[py][px] = bonus;
}

static void trybonus(int px, int py) {
	int i = 0, r;
	const int* p;

	if (field[py][px] != FIELD_EMPTY) return;
	p = bonuschances;
	r = myrand() % bonustotal;
	while (r >= 0) {
		i = *p++;
		r -= *p++;
	}
	if (i == TILE_NONE) return;
	addbonus(px, py, i);
}

static void deletebonus(bonustile* bonus) {
	int px, py;

	px = bonus->px;
	py = bonus->py;
	field[py][px] = 0;
	info[py][px] = 0;
	list_del(&bonus->list);
	freeentry(bonus);
}

static void adddetonate(bomb* bmb) {
	if (bmb->type == BOMB_OFF) return;

	bmb->type = BOMB_OFF;
	field[bmb->py][bmb->px] = FIELD_EXPLODING;
	info[bmb->py][bmb->px] = 0;

	removeitem(bmb);
	addtail(&detonatebombs, bmb);
}

static void processbombs() {
	bomb *bmb, *next;

	list_for_each_entry_safe(bmb, next, &activebombs, list) {
		++(bmb->figcount);
		++bmb->timer;
		switch (bmb->type) {
		case BOMB_NORMAL:
			if (bmb->timer == BOMBLIFE) adddetonate(bmb);
			break;
		case BOMB_CONTROLLED:
			if (bmb->timer == BOMB_CONTROLLED_LIFE) {
				bmb->timer = 0;
				bmb->type = BOMB_NORMAL;
			}
			break;
		}
	}
}

static void flameshaft(player* owner, int px, int py, int dx, int dy, int power) {
	int there;
	bomb* bmb;

	while (power--) {
		px += dx;
		py += dy;
		if (px < 0 || py < 0 || px >= arraynumx || py >= arraynumy) break;
		there = field[py][px];
		switch (there) {
		case FIELD_BOMB:
			bmb = info[py][px];
			adddetonate(bmb);
			break;
		case FIELD_EMPTY:
			addflame(owner, px, py);
			break;
		case FIELD_BRICK:
			adddecay(px, py);
			power = 0;
			break;
		case FIELD_BONUS:
			deletebonus(info[py][px]);
			power = 0;
			break;
		case FIELD_BORDER:
		case FIELD_EXPLODING:
		default:
			power = 0;
		case FIELD_FLAME:
			break;
		}
	}
}

static void detonatebomb(bomb* bmb) {
	int px, py;
	int power;
	player* owner;

	++(bmb->owner->bombsavailable);
	px = bmb->px;
	py = bmb->py;
	power = bmb->power;
	owner = bmb->owner;
	list_del(&bmb->list);
	freeentry(bmb);
	addflame(owner, px, py);
	flameshaft(owner, px, py, -1, 0, power);
	flameshaft(owner, px, py, 0, -1, power);
	flameshaft(owner, px, py, 1, 0, power);
	flameshaft(owner, px, py, 0, 1, power);
}

static void dodetonations(void) {
	int i = 0;
	bomb* bmb;

	while (!list_empty(&detonatebombs)) {
		bmb = (bomb*) detonatebombs.next;
		++i;
		detonatebomb(bmb);
	}
	if (i) playsound((myrand() & 1) ? 0 : 4);
}

static void processflames(void) {
	flame *fl, *next;

	list_for_each_entry_safe(fl, next, &activeflames, list) {
		++(fl->timer);

		if (fl->timer == FLAMELIFE) {
			field[fl->py][fl->px] = FIELD_EMPTY;
			info[fl->py][fl->px] = 0;
			list_del(&fl->list);
			freeentry(fl);
		}
	}
}

static void processdecays() {
	brickdecay *bd, *next;

	list_for_each_entry_safe(bd, next, &activedecays, list) {
		++(bd->timer);
		if (bd->timer == DECAYLIFE) {
			field[bd->py][bd->px] = FIELD_EMPTY;
			trybonus(bd->px, bd->py);
			list_del(&bd->list);
			freeentry(bd);
		}
	}
}

static void drawbombs(void) {
	int j;
	bomb* bmb;
	struct figure* figtab;
	int color;
	int xpos, ypos;

	list_for_each_entry(bmb, &activebombs, list) {
		color = bmb->owner->color;
		figtab = (bmb->type == BOMB_NORMAL) ? bombs1[color] : bombs2[color];
		j = bmb->figcount % (NUMBOMBFRAMES << 1);
		if (j >= NUMBOMBFRAMES) j = (NUMBOMBFRAMES << 1) - j - 1;
		xpos = tovideox(bmb->xpos);
		ypos = tovideoy(bmb->ypos) - 3;

		addsprite(xpos, ypos, figtab + j);
	}
}

static void drawflames(void) {
	flame* fl;
	int xpos, ypos;
	/* no player specific flame sprites yet */
	/* int color; */
	int fig;

	list_for_each_entry(fl, &activeflames, list) {
		/* color=fl->owner->color; */
		xpos = tovideox(fl->xpos);
		ypos = tovideoy(fl->ypos);
		fig = (fl->timer * 10) / FLAMELIFE;
		if (fig >= 5) fig = 9 - fig;
		fig += 5 * fl->lurd;
		addsprite(xpos, ypos, flamefigs[0 /* color */] + fig);
	}
}

static void drawdecays() {
	brickdecay* bd;

	list_for_each_entry(bd, &activedecays, list) {
		addsprite(tovideox(bd->xpos), tovideoy(bd->ypos), blocksx + (bd->timer * 9) / DECAYLIFE);
	}
}

static void drawbonus() {
	bonustile* bonus;

	list_for_each_entry(bonus, &activebonus, list) {
		addsprite(tovideox(bonus->xpos), tovideoy(bonus->ypos), tiles + bonus->type);
	}
}

static void drawplayers() {
	player* pl;
	int xpos, ypos;

	list_for_each_entry(pl, &activeplayers, list) {
		if (!(pl->flags & FLG_DEAD)) {
			if (!pl->figure) pl->figure = walking[pl->color] + 30;
			xpos = tovideox(pl->xpos) + pl->fixx;
			ypos = tovideoy(pl->ypos) + pl->fixy;
			addsprite(xpos, ypos, pl->figure);
		}
	}
}

static void detonatecontrolled(player* pl) {
	bomb *bmb, *next;

	list_for_each_entry_safe(bmb, next, &activebombs, list) {
		if (bmb->owner == pl && bmb->type == BOMB_CONTROLLED) adddetonate(bmb);
	}
}

static void playonce(generic* gen) {
	if (gen->timer == gen->data1) {
		list_del(&gen->list);
		freeentry(gen);
	}
}

static void drawgeneric(generic* gen) {
	addsprite(gen->xpos, gen->ypos, ((figure*) (gen->ptr1)) + gen->timer);
}

static void queuesequence(int xpos, int ypos, figure* fig, int count) {
	generic* gen;

	gen = allocentry(generic);
	if (!gen) return;
	gen->xpos = xpos;
	gen->ypos = ypos;
	gen->data1 = count;
	gen->process = playonce;
	gen->draw = drawgeneric;
	gen->ptr1 = fig;
	addtail(&activegeneric, gen);
}

static void adddeath(player* pl) {
	int xpos, ypos;

	xpos = tovideox(pl->xpos) + pl->fixx - 10;
	ypos = tovideoy(pl->ypos) + pl->fixy;
	queuesequence(xpos, ypos, death, NUMDEATHFRAMES);
}

static void killplayer(player* pl) {
	pl->deaths++;
	pl->flags |= FLG_DEAD;
	playsound(2);
	adddeath(pl);
	detonatecontrolled(pl);
}

static void processgenerics(void) {
	generic *gen, *next;

	list_for_each_entry_safe(gen, next, &activegeneric, list) {
		++(gen->timer);
		gen->process(gen);
	}
}

static void drawgenerics(void) {
	generic* gen;

	list_for_each_entry(gen, &activegeneric, list) {
		gen->draw(gen);
	}
}

static void drawstats(void) {
	player* pl;
	int p = 0;
	const char* n;
	char buf[17];

	solidcopy(&background, 0, 0, IXSIZE, arraystarty);
	list_for_each_entry(pl, &allplayers, list_all_players) {
		if (pl->controller >= 0) {
			n = netnodes[pl->controller].name;
		} else {
			n = playername;
		}
		snprintf(buf, sizeof(buf), "%-8.8s %2i/%2i", n, pl->kills % 100, pl->deaths % 100);
		drawstring(11 + (p / 2) * (15 * fontxsize + 7), 11 + (p % 2) * (fontysize + 2), buf);
		p++;
	}
}

static int centerxchange(player* pl) {
	int speed;
	int val;
	int line;
	int max;

	max = arrayspacex << FRACTION;
	speed = pl->speed;
	val = pl->xpos + (max << 2);
	val %= max;
	line = max >> 1;
	if (val < line) {
		if (val - speed < 0)
			return -val;
		else
			return -speed;
	} else if (val >= line) {
		if (val + speed > max)
			return max - val;
		else
			return speed;
	}
	return 0;
}

static void centerx(player* pl) {
	pl->xpos += centerxchange(pl);
}

static int centerychange(player* pl) {
	int speed;
	int val;
	int line;
	int max;

	max = arrayspacey << FRACTION;
	speed = pl->speed;
	val = pl->ypos + (max << 2);
	val %= max;
	line = max >> 1;
	if (val < line) {
		if (val - speed < 0)
			return -val;
		else
			return -speed;
	} else if (val >= line) {
		if (val + speed > max)
			return max - val;
		else
			return speed;
	}
	return 0;
}

static void centery(player* pl) {
	pl->ypos += centerychange(pl);
}

#define SGN(x) ((x) == 0 ? 0 : ((x) < 0 ? -1 : 1))

static void trymove(player* pl, int dx, int dy) {
	int wx, wy;
	int sx, sy;
	int there;
	int px, py;
	static int depth = 0;
	int tx, ty;

	++depth;
	sx = (dx * (arrayspacex + 1)) << (FRACTION - 1);
	sy = (dy * (arrayspacey + 1)) << (FRACTION - 1);

	wx = screentoarrayx(pl->xpos + sx);
	wy = screentoarrayy(pl->ypos + sy);
	px = screentoarrayx(pl->xpos);
	py = screentoarrayy(pl->ypos);

	if (wx < 0 || wx >= arraynumx || wy < 0 || wy >= arraynumy) {
		--depth;
		return;
	}
	there = field[wy][wx];
	if ((px != wx || py != wy) && (there == FIELD_BRICK || there == FIELD_BOMB || there == FIELD_BORDER)) {
		if (dx && !dy) {
			ty = centerychange(pl);
			if (ty && depth == 1) trymove(pl, 0, -SGN(ty));

		} else if (dy && !dx) {
			tx = centerxchange(pl);
			if (tx && depth == 1) trymove(pl, -SGN(tx), 0);
		}

	} else {
		pl->xpos += dx * pl->speed;
		pl->ypos += dy * pl->speed;
		if (dx && !dy) centery(pl);
		if (dy && !dx) centerx(pl);
	}
	--depth;
}


static void applybonus(player* pl, bonustile* bonus) {
	int type;
	int maxflame;

	maxflame = arraynumx > arraynumy ? arraynumx : arraynumy;
	type = bonus->type;
	deletebonus(bonus);
	switch (type) {
	case TILE_BOMB:
		if (pl->bombsavailable < 9) ++(pl->bombsavailable);
		break;
	case TILE_FLAME:
		if (pl->flamelength < maxflame) ++(pl->flamelength);
		break;
	case TILE_GOLDFLAME:
		pl->flamelength = maxflame;
		break;
	case TILE_TRIGGER:
		pl->flags |= FLG_CONTROL;
		break;
	case TILE_SKATES:
		if (pl->speed < SPEEDSTART) {
			pl->speed = SPEEDSTART;
		} else {
			pl->speed += SPEEDDELTA;
		}
		if (pl->speed > SPEEDMAX) pl->speed = SPEEDMAX;
		break;
	case TILE_TURTLE:
		pl->speed = SPEEDTURTLE;
		pl->speedturtle_timeout = SPEEDTURTLE_TIMEOUT;
		break;
	}
}

static void doplayer(player* pl) {
	/* int last; */
	int color;
	/* int speed; */
	int px, py;
	int there;
	int flags;
	int what;

	if (pl->controller == -1)
		what = myaction;
	else
		what = actions[pl->controller];

	flags = pl->flags;
	if (flags & FLG_DEAD) return;
	color = pl->color;
	/* last=pl->doing; */
	pl->doing = what;
	/* speed=pl->speed; */
	px = screentoarrayx(pl->xpos);
	py = screentoarrayy(pl->ypos);
	there = field[py][px];

	if (what == ACT_QUIT) {
		killplayer(pl);
		list_del(&pl->list_all_players);
		return;
	}

	if (there == FIELD_BONUS) {
		playsound((myrand() & 1) ? 1 : 5);
		applybonus(pl, info[py][px]);
	} else if (there == FIELD_FLAME) {
		flame* fl = info[py][px];
		if (fl->owner == pl) {
			pl->kills--;
		} else {
			fl->owner->kills++;
		}
		killplayer(pl);
		return;
	}

	//	if(what&ACT_TURBO) speed<<=2;
	if (what & ACT_PRIMARY) {
		if (there == FIELD_EMPTY && pl->bombsavailable) dropbomb(pl, px, py, (flags & FLG_CONTROL) ? BOMB_CONTROLLED : BOMB_NORMAL);
	}
	if (what & ACT_SECONDARY && (flags & FLG_CONTROL)) detonatecontrolled(pl);

	switch (what & ACT_MASK) {
	case ACT_UP:
		trymove(pl, 0, -1);
		pl->figcount = (pl->figcount + 1) % 15;
		pl->figure = walking[color] + pl->figcount + 15;
		break;
	case ACT_DOWN:
		trymove(pl, 0, 1);
		pl->figcount = (pl->figcount + 1) % 15;
		pl->figure = walking[color] + pl->figcount + 30;
		break;
	case ACT_LEFT:
		trymove(pl, -1, 0);
		pl->figcount = (pl->figcount + 1) % 15;
		pl->figure = walking[color] + pl->figcount + 45;
		break;
	case ACT_RIGHT:
		trymove(pl, 1, 0);
		pl->figcount = (pl->figcount + 1) % 15;
		pl->figure = walking[color] + pl->figcount;
		break;
	case ACT_NONE:
		break;
	}

	if (pl->speedturtle_timeout > 0) {
		pl->speedturtle_timeout--;
		if (0 == pl->speedturtle_timeout) {
			if (pl->speed < SPEEDSTART) pl->speed = SPEEDSTART;
		}
	}
}

static void processplayers(void) {
	player *pl, *next;

	list_for_each_entry_safe(pl, next, &activeplayers, list) {
		doplayer(pl);
	}
}

static void processquits(void) {
	int i;

	if (network != NETWORK_SLAVE) return;
	for (i = 0; i < MAXNETNODES; ++i) {
		if (netnodes[i].used && actions[i] == ACT_QUIT) netnodes[i].used = 0;
	}
}

static int getaction(void) {
	int what;

	what = ACT_NONE;
	if (checkpressed(MYLEFT))
		what = ACT_LEFT;
	else if (checkpressed(MYRIGHT))
		what = ACT_RIGHT;
	else if (checkpressed(MYDOWN))
		what = ACT_DOWN;
	else if (checkpressed(MYUP))
		what = ACT_UP;
	else if (checkdown(13))
		what = ACT_ENTER;
	else if (checkdown(0x1b))
		what = ACT_QUIT;

	if (checkdown(' ')) what |= ACT_PRIMARY;
	if (checkdown('b')) what |= ACT_SECONDARY;

	return what;
}

static int iterate(void) {
	int i;
	int gountil; /* destination tick */
	static int deathcount = 0;

	mypause();
	scaninput();

	erasesprites();
	clearspritelist();
	gfxunlock();

	myaction = getaction();
	if (NETWORK_NONE == network && myaction == ACT_QUIT) return CODE_QUIT;
	if (NETWORK_NONE == network) {
		gountil = mycount + 1; /* single step in singly player mode */
	} else {
		gountil = networktraffic(); /* as master single step, as slave as many as we can do */
	}

	while (mycount < gountil) { /* simulate ticks up to gountil */
		++mycount;
		if (NETWORK_NONE != network) {
			i = gountil - mycount;
			if (i >= ACTIONHIST) // too far behind
				goto leave_game;
			memcpy(actions, actionblock + i * MAXNETNODES, MAXNETNODES);
			if (actions[myslot] == ACT_QUIT) return CODE_QUIT;
		}
		processbombs();
		dodetonations();
		processdecays();
		processflames();
		processgenerics();
		processquits();
		processplayers();
	}

	/*
		if(!(rand()&127))
		{
			i=gtime();
			while(gtime()-i<100);
		}
	*/

	drawbombs();
	drawbonus();
	drawgenerics();
	drawdecays();
	drawflames();
	drawplayers();
	drawstats();
	plotsprites();
	copyup();
	if (list_empty(&activegeneric)) {
		player* pl;

		int deadplayers = 0;
		i = 0;

		list_for_each_entry(pl, &activeplayers, list) {
			if (!(pl->flags & FLG_DEAD))
				++i;
			else
				deadplayers++;
		}
		if (deadplayers > 0 && (!i || (NETWORK_NONE != network && i == 1))) {
			++deathcount;
			if (deathcount == 25) return CODE_ALLDEAD;
		} else
			deathcount = 0;
	}
	return CODE_CONT;

leave_game: /* client disconnect/timeout: send ACT_QUIT to master */
	myaction = ACT_QUIT;
	networktraffic();
	return CODE_QUIT;
}

void set_game_options(GameOptions* options) {
	gameoptions = *options;
}

void run_single_player(void) {
	int code;
	network = NETWORK_NONE;

	firstzero();
	initplayers();
	do {
		initgame();
		while (!(code = iterate()) && !exitflag) ++framecount;
	} while (code != CODE_QUIT && !exitflag);
}

void run_network_game(void) {
	int code;

	firstzero();
	initplayers();
	do {
		initgame();
		while (!(code = iterate()) && !exitflag) ++framecount;
	} while (code != CODE_QUIT && !exitflag);
}
