/*********************************************/
/* you just keep on pushing my luck over the */
/*           BOULDER        DASH             */
/*                                           */
/*     Jeroen Houttuin, ETH Zurich, 1990     */
/*                                           */
/*                                           */
/*	  PC-VGA version from :				*/
/*                                           */
/*		Herve SOULARD, Paris, 1990		*/
/*                                           */
/*********************************************/

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/timeb.h>
#include "xbd.h"

void blink(i, j)
int	i, j;
{
	field[i][j].changed = TRUE;
}


void move_cell(i, j, ii, jj)
int	i, j, ii, jj;
{
	field[ii][jj] = field[i][j];
	field[ii][jj].speed = 1;
	field[ii][jj].changed = TRUE;
	field[ii][jj].dir = ((ii - i) == 1) ? S : ((i - ii) == 1) ? 
		                                 N : ((jj - j) == 1) ? E : W;
	set_cell(i, j, SPACE);
}


void	explode(a, b, stage)
int	a, b, stage;
{
  if (field[a][b].content != STEEL) {
	  set_cell(a, b, EXPLOSION);
	  field[a][b].stage = stage;/* dirty fix, not what stage was meant for */
  }
}


Bool move_nucbal(i, j, t)
int	i, j, t;
{
	ii = (!t) ? (i + y - 1) % y : i;
	if (!t)
		jj = j;
	else
		switch (field[i][j].dir) {
			case E:
				jj = (t == 1) ? (j + x - 1) % x : (j + 1) % x;
				break;
			case W:
			default:
				jj = (t == 2) ? (j + x - 1) % x : (j + 1) % x;
				break;
		}
	switch (field[ii][jj].content) {
		case SPACE:
			move_cell(i, j, ii, jj);
			return (TRUE);
		case NUCBAL:
			explode(i, j, DIAEXPLO);
			explode(ii, jj, DIAEXPLO);
			return (TRUE);
		default:
			if (field[i][j].dir == N)
				field[i][j].dir = E;
			else
				field[i][j].dir = ((field[i][j].dir + 2) % 4);
			return (FALSE);
	}
}


Bool move_monster(content, direction)
char	content, direction;
{
	switch (direction) {
		case N:	
			ii = (i + y - 1) % y;
			jj	= j;
			break;
		case E:
			jj = (j + 1) % x;
			ii = i;
			break;
		case S:
			ii = (i + 1) % y;
			jj = j;
			break;
		default:
			jj = (j + x - 1) % x;
			ii = i;
			break;			/* default must be SOMEthing, West */
	}
	switch (field[ii][jj].content) {
		case SPACE:
			if (!field[ii][jj].changed) {
				move_cell(i, j, ii, jj);
				field[ii][jj].dir = direction;
				return (TRUE);
			} 
			else
				return (FALSE);
			break;
		case BOULDER:
		case DIAMOND:
			if ((direction == N) && (field[ii][jj].speed != 0))
				if (content == RMONSTER) {
					explode(i, j, DIAEXPLO);
					return (TRUE);
				} 
				else 
					if (content == LMONSTER) {
						explode(i, j, SPACEEXPLO);
						return (TRUE);
					} 
					else
						return (FALSE);
			else
				return (FALSE);
			break;
		default:
			return (FALSE);
			break;
	}
	return (FALSE);
}


Bool search_destroy(content)
char	content;
{
	ii = (i + y - 1) % y;
	jj = (j + x - 1) % x;
	if (field[i][jj].content == PLAYER || field[i][jj].content == BLOB) {
		set_cell(i, j, SPACE);
		if (field[i][jj].content == PLAYER) {
			lives--;
			scoreobs = TRUE;
		}
		if (content == RMONSTER)
			explode(i, jj, DIAEXPLO);
		else
			explode(i, jj, SPACEEXPLO);
		return (TRUE);
	} 
	else 
		if (field[ii][j].content == PLAYER || field[ii][j].content == BLOB) {
			set_cell(i, j, SPACE);
			if (field[ii][j].content == PLAYER) {
				lives--;
				scoreobs = TRUE;
			}
			if (content == RMONSTER)
				explode(ii, j, DIAEXPLO);
			else
				explode(ii, j, SPACEEXPLO);
			return (TRUE);
		}
		else {
			ii = (i + 1) % y;
			jj = (j + 1) % x;
			if (field[i][jj].content == PLAYER || field[i][jj].content == BLOB) {
				set_cell(i, j, SPACE);
				if (field[i][jj].content == PLAYER) {
					lives--;
					scoreobs = TRUE;
				}
				if (content == RMONSTER)
					explode(i, jj, DIAEXPLO);
				else
					explode(i, jj, SPACEEXPLO);
				return (TRUE);
			} 
			else 
				if (field[ii][j].content == PLAYER || field[ii][j].content == BLOB) {
					set_cell(i, j, SPACE);
					if (field[ii][j].content == PLAYER) {
						lives--;
						scoreobs = TRUE;
					}
					if (content == RMONSTER)
						explode(ii, j, DIAEXPLO);
					else
						explode(ii, j, SPACEEXPLO);
					return (TRUE);
				} 
				else
					return (FALSE);
			}
	}

void propagate(i, j, dias)
int             i, j;
Bool            dias;
{
	int	t, it, jt;
	if (dias)
		set_cell(i, j, DIAMOND);
	else {
		field[i][j].checked = PROPAGATED;
		field[i][j].caught = TRUE;
	}
	for (t = 0; (t < 4); ++t) {
		it = (t == 1) ? (i + y - 1) % y : (t == 3) ? (i + 1) % y : i;
		jt = (t == 2) ? (j + x - 1) % x : (t == 0) ? (j + 1) % x : j;	
		switch (field[it][jt].content) {
			case BLOB:
				if (field[it][jt].checked != PROPAGATED)
					propagate(it, jt, dias);
				break;
			case SPACE:
			case GRASS:
				if (!(rand() % (blobbreak + 1))) {
					set_cell(it, jt, BLOB);
					field[it][jt].checked = PROPAGATED;
					field[it][jt].caught = TRUE;
				}
				break;
			default:
				break;
		}
	}	
}


Bool caught(i, j)
int	i, j;
{
	Bool	Free;
	int	t, it, jt;

	field[i][j].checked = 1;
	Free = FALSE;

	for (t = 0; ((t < 4) && !Free); ++t) {
		it = (t == 1) ? (i + y - 1) % y : (t == 3) ? (i + 1) % y : i;
		jt = (t == 2) ? (j + x - 1) % x : (t == 0) ? (j + 1) % x : j;
		
		switch (field[it][jt].content) {
			case SPACE:
			case GRASS:
			case RMONSTER:
			case LMONSTER:
			case EATER:
				Free = TRUE;
				break;
			case BLOB:
				Free = (Free || ((field[it][jt].checked == 1) ? 
					             !field[i][j].caught : !caught(it, jt)));
				break;
			default:
				break;
		}
	}
	field[i][j].caught != Free;
	return (!Free);
}


void diaboulderproc(i, j)
int	i, j;
{
	
	if (field[i][j].content == DIAMOND)
		blink(i, j);
	
	ii = (ii + 1) % y;
	field[i][j].dir = NODIR;
	switch (field[ii][j].content) {
		case SPACE:			/* directly underneath */
			move_cell(i, j, ii, j);
			field[ii][j].speed = 1;
			break;
		case PLAYER:
			if (field[i][j].speed) {
				set_cell(i, j, SPACE);
				explode(ii, j, SPACEEXPLO);
				lives--;
				scoreobs = TRUE;
			}
			break;
		case LMONSTER:
		case EATER:
			if (field[i][j].speed) {
				set_cell(i, j, SPACE);
				explode(ii, j, SPACEEXPLO);
			}
			break;
		case RMONSTER:
			if (field[i][j].speed) {
				set_cell(i, j, SPACE);
				explode(ii, j, DIAEXPLO);
			}
			break;
		case TINKLE:
			if (field[i][j].speed) {
				tinkact = TRUE;
				field[ii][j].stage = field[i][j].content;
				field[ii][j].speed = 1;
				set_cell(i, j, SPACE);
				break;
			}
			break;
		case WALL:
		case BOULDER:
		case DIAMOND:
		case EXPLOSION:
			jj = (j + 1) % x;
			if (field[i][jj].content == SPACE && field[ii][jj].content == SPACE) {
				move_cell(i, j, i, jj);
				field[i][jj].speed = 0;
			} 
			else {
				jj = (j - 1) % x;
				if (field[i][jj].content == SPACE && field[ii][jj].content == SPACE) {
					move_cell(i, j, i, jj);
					field[i][jj].speed = 0;
				}
				else
					field[i][j].speed = 0;
			}
			break;
		default:
			field[i][j].speed = 0;
			break;
	}
}


void calculate_field()
{
	players = 0;

	/* Iterate through each horizontal line */
	if (!curtime)
		curorder = KILL;
	for (i = y - 1; i >= 0; --i) {
		for (j = 0; j < x; ++j) {
			if (!(field[i][j].changed)) {
				ii = i;
				jj = j;
				switch (field[i][j].content) {
					case PLAYER:
						players++;
						switch (curorder) {
							case STAND:
								break;
							case UP:
								ii = (i + y - 1) % y;
								break;
							case DOWN:
								ii = (i + 1) % y;
								break;
							case LEFT:
								jj = (j + x - 1) % x;
								break;
							case RIGHT:
								jj = (j + 1) % x;
								break;
						}
						if (!(curorder == STAND) && !(field[i][j].changed)) {
							if (curorder == KILL) {
								set_cell(i, j, EXPLOSION);
								lives--;
								scoreobs = TRUE;
								break;
							}
							switch (field[ii][jj].content) {
								case SPACE:
								case GRASS:
								case DIAMOND:
									if (field[ii][jj].content == DIAMOND) {
										if (curorder == UP && field[ii][jj].speed)
											break;
										score += diapoints;
										if (diareq)
											diareq--;
										scoreobs = TRUE;
									}
									if (steal)
										set_cell(ii, jj, SPACE);
									else
										move_cell(i, j, ii, jj);
									break;
								case BOULDER:
									jjj = (2 * jj - j + x) % x;
									if (field[i][jjj].content == SPACE && 
										((rand() % 2) == 0) &&	
										!(field[ii][jj].dir == E))	{
										move_cell(i, jj, i, jjj);
										field[i][jjj].speed = 0;
										if (!steal) {
											move_cell(i, j, i, jj);
										}
									}
									break;
								case EXIT:
									if (diareq < 1) {
										if (!steal)
											move_cell(i, j, ii, jj);
										else
											set_cell(ii, jj, SPACE);
										if (!levincreased) {
											levelnum++;
											lives++;
											levincreased = TRUE;
											for (jj = curtime / 10; jj > 0; --jj) {
												score += 10;
												draw_score();
											}
										}
										gamestop = TRUE;
										stoplevel = TRUE;
									}
									break;
							}
						}
						blink(i, j);
						break;
					case DIAMOND:
					case BOULDER:
						diaboulderproc(i, j);
						break;
					case EATER:
						if (!field[i][j].speed) {
							for (jjj = 0; ((!field[i][j].changed) && (jjj < 4)); ++jjj) {
								ii = (jjj == 2) ? (i + 1) % y : (jjj == 0) ? (i + y - 1) % y : i;
								jj = (jjj == 1) ? (j + 1) % x : (jjj == 3) ? (j + x - 1) % x : j;
								switch (field[ii][jj].content) {
									case PLAYER:
										lives--;
										scoreobs = TRUE;
									case DIAMOND:
										if (field[ii][jj].speed && (ii == i - 1) && (jj == j))
											break;
									case BLOB:
									case LMONSTER:
									case RMONSTER:
									case NUCBAL:
										move_cell(i, j, ii, jj);
										break;
									default:
										break;
								}
							}
						}
						else {
							jjj = field[i][j].dir;
							ii = (jjj == 2) ? (i + 1) % y : (jjj == 0) ? (i + y - 1) % y : i;
							jj = (jjj == 1) ? (j + 1) % x : (jjj == 3) ? (j + x - 1) % x : j;
							switch (field[ii][jj].content) {
								case LMONSTER:
								case BLOB:
								case SPACE:
								case GRASS:
								case DIAMOND:
								case RMONSTER:
								case PLAYER:
								case NUCBAL:
									if (field[ii][jj].content == PLAYER) {
										lives--;
										scoreobs = TRUE;
									}
									move_cell(i, j, ii, jj);
									break;
								case BOULDER:
									jjj = (2 * jj - j + x) % x;
									if (field[i][jjj].content == SPACE && 
										 ((rand() % 2) == 0) && 
										 !(field[ii][jj].dir == E)) {
										 move_cell(i, jj, i, jjj);
										 move_cell(i, j, i, jj);
									 }
									 else
										 field[i][j].speed = 0;
									 break;
								default:
									field[i][j].speed = 0;
									break;
							}
						}
						blink(i, j);
						break;
					case RMONSTER:
						blink(i, j);
						if (search_destroy(RMONSTER))
							break;
						jjj =	3;
						while (jjj >= 0 && !move_monster(RMONSTER, (field[i][j].dir + jjj + 2) % 4))
							jjj--;
						break;
					case LMONSTER:
						blink(i, j);
						if (search_destroy(LMONSTER))
							break;
						jjj = 0;
						while (jjj <= 4 && !move_monster(LMONSTER, (field[i][j].dir + jjj + 3) % 4))
							jjj++;
						break;
					case EXPLOSION:
						jjj = field[i][j].stage;
						if (!(jjj % 5)) {
							jjj++;		/* use jjj for setting new stage */
							ii = (i + 1) % y;
							jj = (j + 1) % x;
							explode(i, j, jjj);
							explode(i, jj, jjj);
							explode(ii, j, jjj);
							explode(ii, jj, jjj);
							ii = (i + y - 1) % y;
							explode(ii, j, jjj);
							explode(ii, jj, jjj);
							jj = (j + x - 1) % x;
							explode(ii, jj, jjj);
							explode(i, jj, jjj);
							ii = (i + 1) % y;
							explode(ii, jj, jjj);
						}
						else {
							if (jjj % 10 < 3)
								field[i][j].stage++;
							else 
								if (jjj > DIAEXPLO) {
									set_cell(i, j, DIAMOND);
								} 
								else 
									if (jjj > BOULDEXPLO) {
										set_cell(i, j, BOULDER);
									}
									else
										if (jjj > SPACEEXPLO) {
											set_cell(i, j, SPACE);
										}
										else
											field[i][j].stage++;
						}
						break;
					case EXIT:
						blink(i, j);
						break;
					case BLOB:
						blobcells++;
						if (blobcollapse)
							set_cell(i, j, BOULDER);
						else {
							if (blobcells > critical)
								blobcollapse = TRUE;
							else {
								if (!field[i][j].checked) {
									if (caught(i, j))
										propagate(i, j, DIAMOND);
									else
										propagate(i, j, FALSE);
								}
								field[i][j].checked = FALSE;
								field[i][j].caught = TRUE;
							}
						}
						blink(i, j);
						break;
					case NUCBAL:
						for (jjj = 0; ((jjj < 3) && !move_nucbal(i, j, jjj)); ++jjj);
						blink(i, j);
						break;
					case MAGICWALL:
						jj = (j + x - 1) % x;
						if (field[i][jj].content == SPACE)
							set_cell(i, jj, MAGICWALL);
						jj = (j + 1) % x;
						if (field[i][jj].content == SPACE)
							set_cell(i, jj, MAGICWALL);
						break;
					case TINKLE:
						if (tinkact) {
							blink(i, j);
							if (tinkdur > 0) {
								switch (field[i][j].stage) {
									case BOULDER:
										{
											field[i][j].content = DIAMOND;
											diaboulderproc(i, j);
											break;
										}
									case DIAMOND:
										{
											field[i][j].content = BOULDER;
											diaboulderproc(i, j);
											break;
										}
									default:
										break;
								}
							}
						}
						set_cell(i, j, TINKLE);	/* reset to empty tinkle wall */
						break;
					default:
						break;
				}
			}
		}
	}
	curorder = STAND;		/* reset orders */
	blobcells = 0;
	pgc = (pgc == pgc1) ? ((rand() % 22) == 0) ? pgc2 : pgc1 : pgc1;
	dgc = (dgc == dgc1) ? dgc2 : dgc1;
	lgc = (lgc == lgc1) ? lgc2 : lgc1;
	rgc = (rgc == rgc1) ? rgc2 : rgc1;
	egc = (egc == egc1) ? egc2 : egc1;
	Bgc = (Bgc == Bgc1) ? Bgc2 : Bgc1;
	if (tinkact)
		tgc = (tgc == tgc1) ? tgc2 : tgc1;
	if (!diareq) {
		Egc = (Egc == Egc1) ? Egc2 : Egc1;
	if (diareq == 0) {
		diapoints = extradiapoints;
		scoreobs = TRUE;
	}
}
}
