/*
 *  Hunt
 *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
 *  San Francisco, California
 *
 *  Copyright (c) 1985 Regents of the University of California.
 *  All rights reserved.  The Berkeley software License Agreement
 *  specifies the terms and conditions for redistribution.
 */

# include	"hunt.h"

# undef CTRL
# define	CTRL(x)	('x' & 037)

# ifdef MONITOR
/*
 * mon_execute:
 *	Execute a single monitor command
 */
mon_execute(pp)
register PLAYER	*pp;
{
	register char	ch;

	ch = pp->p_cbuf[pp->p_ncount++];
	switch (ch) {
	  case CTRL(L):
		sendcom(pp, REDRAW);
		break;
	  case 'q':
		(void) strcpy(pp->p_death, "| Quit |");
		break;
	}
}
# endif MONITOR

/*
 * execute:
 *	Execute a single command
 */
execute(pp)
register PLAYER	*pp;
{
	register char	ch;

	ch = pp->p_cbuf[pp->p_ncount++];

# ifdef	FLY
	if (pp->p_flying >= 0) {
		switch (ch) {
		  case CTRL(L):
			sendcom(pp, REDRAW);
			break;
		  case 'q':
			(void) strcpy(pp->p_death, "| Quit |");
			break;
		}
		return;
	}
# endif	FLY

	switch (ch) {
	  case CTRL(L):
		sendcom(pp, REDRAW);
		break;
	  case 'h':
		move(pp, LEFTS);
		break;
	  case 'H':
		face(pp, LEFTS);
		break;
	  case 'j':
		move(pp, BELOW);
		break;
	  case 'J':
		face(pp, BELOW);
		break;
	  case 'k':
		move(pp, ABOVE);
		break;
	  case 'K':
		face(pp, ABOVE);
		break;
	  case 'l':
		move(pp, RIGHT);
		break;
	  case 'L':
		face(pp, RIGHT);
		break;
	  case 'f':
		fire(pp, SHOT);
		break;
	  case 'g':
		fire(pp, GRENADE);
		break;
	  case 'F':
		fire(pp, SATCHEL);
		break;
	  case 'G':
		fire(pp, BOMB);
		break;
# ifdef	OOZE
	  case 'o':
		fire_slime(pp, SLIMEREQ);
		break;
	  case 'O':
		fire_slime(pp, SSLIMEREQ);
		break;
# endif	OOZE
	  case 's':
		scan(pp);
		break;
	  case 'c':
		cloak(pp);
		break;
	  case 'q':
		(void) strcpy(pp->p_death, "| Quit |");
		break;
	}
}

/*
 * move:
 *	Execute a move in the given direction
 */
move(pp, dir)
register PLAYER	*pp;
int		dir;
{
	register PLAYER	*newp;
	register int	x, y;
	register FLAG	moved;
	register BULLET	*bp;

	y = pp->p_y;
	x = pp->p_x;

	switch (dir) {
	  case LEFTS:
		x--;
		break;
	  case RIGHT:
		x++;
		break;
	  case ABOVE:
		y--;
		break;
	  case BELOW:
		y++;
		break;
	}

	moved = FALSE;
	switch (Maze[y][x]) {
	  case SPACE:
# ifdef RANDOM
	  case DOOR:
# endif RANDOM
		moved = TRUE;
		break;
	  case WALL1:
	  case WALL2:
	  case WALL3:
# ifdef REFLECT
	  case WALL4:
	  case WALL5:
# endif REFLECT
		break;
	  case MINE:
	  case GMINE:
		if (dir == pp->p_face)
			pickup(pp, y, x, 5, Maze[y][x]);
		else if (opposite(dir, pp->p_face))
			pickup(pp, y, x, 95, Maze[y][x]);
		else
			pickup(pp, y, x, 50, Maze[y][x]);
		Maze[y][x] = SPACE;
		moved = TRUE;
		break;
	  case SHOT:
	  case GRENADE:
	  case SATCHEL:
	  case BOMB:
		bp = is_bullet(y, x);
		if (bp != NULL)
			bp->b_expl = TRUE;
		Maze[y][x] = SPACE;
		moved = TRUE;
		break;
	  case LEFTS:
	  case RIGHT:
	  case ABOVE:
	  case BELOW:
# ifdef FLY
	  case FLYER:
# endif FLY
		if (dir != pp->p_face)
			sendcom(pp, BELL);
		else {
			newp = play_at(y, x);
			checkdam(newp, pp, pp->p_ident, STABDAM, KNIFE);
		}
		break;
	}
	if (moved) {
		if (pp->p_ncshot > 0)
			if (--pp->p_ncshot == MAXNCSHOT) {
				cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
				outstr(pp, " ok", 3);
			}
		if (pp->p_undershot) {
			fixshots(pp->p_y, pp->p_x, pp->p_over);
			pp->p_undershot = FALSE;
		}
		drawplayer(pp, FALSE);
		pp->p_over = Maze[y][x];
		pp->p_y = y;
		pp->p_x = x;
		drawplayer(pp, TRUE);
	}
}

/*
 * face:
 *	Change the direction the player is facing
 */
face(pp, dir)
register PLAYER	*pp;
register int	dir;
{
	if (pp->p_face != dir) {
		pp->p_face = dir;
		drawplayer(pp, TRUE);
	}
}

/*
 * fire:
 *	Fire a shot of the given type in the given direction
 */
fire(pp, type)
register PLAYER	*pp;
register char	type;
{
	register int	req_index;
	static int	req[4] = { BULREQ, GRENREQ, SATREQ, BOMBREQ };
	static int	shot_type[4] = { SHOT, GRENADE, SATCHEL, BOMB };

	if (pp == NULL)
		return;
	if (pp->p_ammo == 0) {
		message(pp, "No more charges.");
		return;
	}
	if (pp->p_ncshot > MAXNCSHOT)
		return;
	if (pp->p_ncshot++ == MAXNCSHOT) {
		cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
		outstr(pp, "   ", 3);
	}
	switch (type) {
	  case SHOT:
		req_index = 0;
		break;
	  case GRENADE:
		req_index = 1;
		break;
	  case SATCHEL:
		req_index = 2;
		break;
	  case BOMB:
		req_index = 3;
		break;
# ifdef DEBUG
	  default:
		message(pp, "What you do!!!");
		return;
# endif DEBUG
	}
	while (pp->p_ammo < req[req_index])
		req_index--;
	pp->p_ammo -= req[req_index];
	(void) sprintf(Buf, "%3d", pp->p_ammo);
	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
	outstr(pp, Buf, 3);

	add_shot(shot_type[req_index], pp->p_y, pp->p_x, pp->p_face,
		req[req_index], pp, FALSE, pp->p_face);
	pp->p_undershot = TRUE;

	/*
	 * Show the object to everyone
	 */
	showexpl(pp->p_y, pp->p_x, shot_type[req_index]);
	for (pp = Player; pp < End_player; pp++)
		sendcom(pp, REFRESH);
# ifdef MONITOR
	for (pp = Monitor; pp < End_monitor; pp++)
		sendcom(pp, REFRESH);
# endif MONITOR
}

# ifdef	OOZE
/*
 * fire_slime:
 *	Fire a slime shot in the given direction
 */
fire_slime(pp, req)
register PLAYER	*pp;
register int	req;
{
	if (pp == NULL)
		return;
	if (pp->p_ammo < req) {
		message(pp, "Not enough charges.");
		return;
	}
	if (pp->p_ncshot > MAXNCSHOT)
		return;
	if (pp->p_ncshot++ == MAXNCSHOT) {
		cgoto(pp, STAT_GUN_ROW, STAT_VALUE_COL);
		outstr(pp, "   ", 3);
	}
	pp->p_ammo -= req;
	(void) sprintf(Buf, "%3d", pp->p_ammo);
	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
	outstr(pp, Buf, 3);

	add_shot(SLIME, pp->p_y, pp->p_x, pp->p_face, req, pp, FALSE,
		pp->p_face);

	/*
	 * Show the object to everyone
	 */
	showexpl(pp->p_y, pp->p_x, SLIME);
	for (pp = Player; pp < End_player; pp++)
		sendcom(pp, REFRESH);
# ifdef MONITOR
	for (pp = Monitor; pp < End_monitor; pp++)
		sendcom(pp, REFRESH);
# endif MONITOR
}
# endif	OOZE

/*
 * create_shot:
 *	Create a shot with the given properties
 */
add_shot(type, y, x, face, charge, owner, expl, over)
int	type;
int	y, x;
char	face;
int	charge;
PLAYER	*owner;
int	expl;
char	over;
{
	register BULLET	*bp;

# ifdef CONSTANT_MOVE
	/*
	 * if there are no bullets in flight, set up the alarm
	 */

	if (Bullets == NULL)
		bul_alarm(1);
# endif CONSTANT_MOVE

	bp = create_shot(type, y, x, face, charge, owner,
		(owner == NULL) ? NULL : owner->p_ident, expl, over);
	bp->b_next = Bullets;
	Bullets = bp;
}

BULLET *
create_shot(type, y, x, face, charge, owner, score, expl, over)
int	type;
int	y, x;
char	face;
int	charge;
PLAYER	*owner;
IDENT	*score;
int	expl;
char	over;
{
	register BULLET	*bp;

	bp = (BULLET *) malloc(sizeof (BULLET));	/* NOSTRICT */
	if (bp == NULL) {
		if (owner != NULL)
			message(owner, "Out of memory");
		return NULL;
	}

	bp->b_face = face;
	bp->b_x = x;
	bp->b_y = y;
	bp->b_charge = charge;
	bp->b_owner = owner;
	bp->b_score = score;
	bp->b_type = type;
	bp->b_expl = expl;
	bp->b_over = over;
	bp->b_next = NULL;

	return bp;
}

/*
 * cloak:
 *	Turn on or increase length of a cloak
 */
cloak(pp)
register PLAYER	*pp;
{
	if (pp->p_ammo <= 0) {
		message(pp, "No more charges");
		return;
	}
	(void) sprintf(Buf, "%3d", --pp->p_ammo);
	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
	outstr(pp, Buf, 3);

	pp->p_cloak += CLOAKLEN;
	cgoto(pp, STAT_CLOAK_ROW, STAT_VALUE_COL);
	outstr(pp, " on", 3);

	if (pp->p_scan >= 0) {
		pp->p_scan = -1;
		cgoto(pp, STAT_SCAN_ROW, STAT_VALUE_COL);
		outstr(pp, "   ", 3);
	}

	showstat(pp);
}

/*
 * scan:
 *	Turn on or increase length of a scan
 */
scan(pp)
register PLAYER	*pp;
{
	if (pp->p_ammo <= 0) {
		message(pp, "No more charges");
		return;
	}
	(void) sprintf(Buf, "%3d", --pp->p_ammo);
	cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
	outstr(pp, Buf, 3);

	pp->p_scan += SCANLEN;
	cgoto(pp, STAT_SCAN_ROW, STAT_VALUE_COL);
	outstr(pp, " on", 3);

	if (pp->p_cloak >= 0) {
		pp->p_cloak = -1;
		cgoto(pp, STAT_CLOAK_ROW, STAT_VALUE_COL);
		outstr(pp, "   ", 3);
	}

	showstat(pp);
}

/*
 * pickup:
 *	check whether the object blew up or whether he picked it up
 */
pickup(pp, y, x, prob, obj)
register PLAYER	*pp;
register int	y, x;
int		prob;
int		obj;
{
	register int	req;

	switch (obj) {
	  case MINE:
		req = BULREQ;
		break;
	  case GMINE:
		req = GRENREQ;
		break;
	  default:
		abort();
	}
	if (rand_num(100) < prob)
		add_shot(obj, y, x, LEFTS, req, (PLAYER *) NULL,
			TRUE, pp->p_face);
	else {
		pp->p_ammo += req;
		(void) sprintf(Buf, "%3d", pp->p_ammo);
		cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
		outstr(pp, Buf, 3);
	}
}
