/*	$NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $	*/
/*
 *  Hunt
 *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
 *  San Francisco, California
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $");
#endif /* not lint */

# include	<ctype.h>
# include	<errno.h>
# include	<fcntl.h>
# include	<stdlib.h>
# include	<unistd.h>
# include	<sys/time.h>
# include	"hunt.h"

# define	SCOREDECAY	15

static char	Ttyname[NAMELEN];

int
answer()
{
	PLAYER			*pp;
	int			newsock;
	static u_long		mode;
	static char		name[NAMELEN];
	static char		team;
	static int		enter_status;
	static int		socklen;
	static u_long		machine;
	static u_long		uid;
	static SOCKET		sockstruct;
	char			*cp1, *cp2;
	int			flags;
	long			version;

# ifdef INTERNET
	socklen = sizeof sockstruct;
# else
	socklen = sizeof sockstruct - 1;
# endif
	errno = 0;
	newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
	if (newsock < 0)
	{
		if (errno == EINTR)
			return FALSE;
# ifdef LOG
		syslog(LOG_ERR, "accept: %m");
# else
		perror("accept");
# endif
		cleanup(1);
	}

# ifdef INTERNET
	machine = ntohl(((struct sockaddr_in *) &sockstruct)->sin_addr.s_addr);
# else
	if (machine == 0)
		machine = gethostid();
# endif
	version = htonl((u_int32_t) HUNT_VERSION);
	(void) write(newsock, (char *) &version, LONGLEN);
	(void) read(newsock, (char *) &uid, LONGLEN);
	uid = ntohl((unsigned long) uid);
	(void) read(newsock, name, NAMELEN);
	(void) read(newsock, &team, 1);
	(void) read(newsock, (char *) &enter_status, LONGLEN);
	enter_status = ntohl((unsigned long) enter_status);
	(void) read(newsock, Ttyname, NAMELEN);
	(void) read(newsock, (char *) &mode, sizeof mode);
	mode = ntohl(mode);

	/*
	 * Turn off blocking I/O, so a slow or dead terminal won't stop
	 * the game.  All subsequent reads check how many bytes they read.
	 */
	flags = fcntl(newsock, F_GETFL, 0);
	flags |= O_NDELAY;
	(void) fcntl(newsock, F_SETFL, flags);

	/*
	 * Make sure the name contains only printable characters
	 * since we use control characters for cursor control
	 * between driver and player processes
	 */
	for (cp1 = cp2 = name; *cp1 != '\0'; cp1++)
		if (isprint(*cp1) || *cp1 == ' ')
			*cp2++ = *cp1;
	*cp2 = '\0';

# ifdef INTERNET
	if (mode == C_MESSAGE) {
		char	buf[BUFSIZ + 1];
		int	n;

		if (team == ' ')
			(void) sprintf(buf, "%s: ", name);
		else
			(void) sprintf(buf, "%s[%c]: ", name, team);
		n = strlen(buf);
		for (pp = Player; pp < End_player; pp++) {
			cgoto(pp, HEIGHT, 0);
			outstr(pp, buf, n);
		}
		while ((n = read(newsock, buf, BUFSIZ)) > 0)
			for (pp = Player; pp < End_player; pp++)
				outstr(pp, buf, n);
		for (pp = Player; pp < End_player; pp++) {
			ce(pp);
			sendcom(pp, REFRESH);
			sendcom(pp, READY, 0);
			(void) fflush(pp->p_output);
		}
		(void) close(newsock);
		return FALSE;
	}
	else
# endif
# ifdef MONITOR
	if (mode == C_MONITOR)
		if (End_monitor < &Monitor[MAXMON])
			pp = End_monitor++;
		else {
			socklen = 0;
			(void) write(newsock, (char *) &socklen,
				sizeof socklen);
			(void) close(newsock);
			return FALSE;
		}
	else
# endif
		if (End_player < &Player[MAXPL])
			pp = End_player++;
		else {
			socklen = 0;
			(void) write(newsock, (char *) &socklen,
				sizeof socklen);
			(void) close(newsock);
			return FALSE;
		}

#ifdef MONITOR
	if (mode == C_MONITOR && team == ' ')
		team = '*';
#endif
	pp->p_ident = get_ident(machine, uid, name, team);
	pp->p_output = fdopen(newsock, "w");
	pp->p_death[0] = '\0';
	pp->p_fd = newsock;
	FD_SET(pp->p_fd, &Fds_mask);
	if (pp->p_fd >= Num_fds)
		Num_fds = pp->p_fd + 1;

	pp->p_y = 0;
	pp->p_x = 0;

# ifdef MONITOR
	if (mode == C_MONITOR)
		stmonitor(pp);
	else
# endif
		stplayer(pp, enter_status);
	return TRUE;
}

# ifdef MONITOR
void
stmonitor(pp)
	PLAYER	*pp;
{
	int	line;
	PLAYER	*npp;

	memcpy(pp->p_maze, Maze, sizeof Maze);

	drawmaze(pp);

	(void) sprintf(Buf, "%5.5s%c%-10.10s %c", " ", stat_char(pp),
		pp->p_ident->i_name, pp->p_ident->i_team);
	line = STAT_MON_ROW + 1 + (pp - Monitor);
	for (npp = Player; npp < End_player; npp++) {
		cgoto(npp, line, STAT_NAME_COL);
		outstr(npp, Buf, STAT_NAME_LEN);
	}
	for (npp = Monitor; npp < End_monitor; npp++) {
		cgoto(npp, line, STAT_NAME_COL);
		outstr(npp, Buf, STAT_NAME_LEN);
	}

	sendcom(pp, REFRESH);
	sendcom(pp, READY, 0);
	(void) fflush(pp->p_output);
}
# endif

void
stplayer(newpp, enter_status)
	PLAYER	*newpp;
	int	enter_status;
{
	int	x, y;
	PLAYER	*pp;

	Nplayer++;

	for (y = 0; y < UBOUND; y++)
		for (x = 0; x < WIDTH; x++)
			newpp->p_maze[y][x] = Maze[y][x];
	for (     ; y < DBOUND; y++) {
		for (x = 0; x < LBOUND; x++)
			newpp->p_maze[y][x] = Maze[y][x];
		for (     ; x < RBOUND; x++)
			newpp->p_maze[y][x] = SPACE;
		for (     ; x < WIDTH;  x++)
			newpp->p_maze[y][x] = Maze[y][x];
	}
	for (     ; y < HEIGHT; y++)
		for (x = 0; x < WIDTH; x++)
			newpp->p_maze[y][x] = Maze[y][x];

	do {
		x = rand_num(WIDTH - 1) + 1;
		y = rand_num(HEIGHT - 1) + 1;
	} while (Maze[y][x] != SPACE);
	newpp->p_over = SPACE;
	newpp->p_x = x;
	newpp->p_y = y;
	newpp->p_undershot = FALSE;

# ifdef FLY
	if (enter_status == Q_FLY) {
		newpp->p_flying = rand_num(20);
		newpp->p_flyx = 2 * rand_num(6) - 5;
		newpp->p_flyy = 2 * rand_num(6) - 5;
		newpp->p_face = FLYER;
	}
	else
# endif
	{
		newpp->p_flying = -1;
		newpp->p_face = rand_dir();
	}
	newpp->p_damage = 0;
	newpp->p_damcap = MAXDAM;
	newpp->p_nchar = 0;
	newpp->p_ncount = 0;
	newpp->p_nexec = 0;
	newpp->p_ammo = ISHOTS;
# ifdef BOOTS
	newpp->p_nboots = 0;
# endif
	if (enter_status == Q_SCAN) {
		newpp->p_scan = SCANLEN;
		newpp->p_cloak = 0;
	}
	else {
		newpp->p_scan = 0;
		newpp->p_cloak = CLOAKLEN;
	}
	newpp->p_ncshot = 0;

	do {
		x = rand_num(WIDTH - 1) + 1;
		y = rand_num(HEIGHT - 1) + 1;
	} while (Maze[y][x] != SPACE);
	Maze[y][x] = GMINE;
# ifdef MONITOR
	for (pp = Monitor; pp < End_monitor; pp++)
		check(pp, y, x);
# endif

	do {
		x = rand_num(WIDTH - 1) + 1;
		y = rand_num(HEIGHT - 1) + 1;
	} while (Maze[y][x] != SPACE);
	Maze[y][x] = MINE;
# ifdef MONITOR
	for (pp = Monitor; pp < End_monitor; pp++)
		check(pp, y, x);
# endif

	(void) sprintf(Buf, "%5.2f%c%-10.10s %c", newpp->p_ident->i_score,
		stat_char(newpp), newpp->p_ident->i_name,
		newpp->p_ident->i_team);
	y = STAT_PLAY_ROW + 1 + (newpp - Player);
	for (pp = Player; pp < End_player; pp++) {
		if (pp != newpp) {
			char	smallbuf[10];

			pp->p_ammo += NSHOTS;
			newpp->p_ammo += NSHOTS;
			cgoto(pp, y, STAT_NAME_COL);
			outstr(pp, Buf, STAT_NAME_LEN);
			(void) sprintf(smallbuf, "%3d", pp->p_ammo);
			cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
			outstr(pp, smallbuf, 3);
		}
	}
# ifdef MONITOR
	for (pp = Monitor; pp < End_monitor; pp++) {
		cgoto(pp, y, STAT_NAME_COL);
		outstr(pp, Buf, STAT_NAME_LEN);
	}
# endif

	drawmaze(newpp);
	drawplayer(newpp, TRUE);
	look(newpp);
# ifdef	FLY
	if (enter_status == Q_FLY)
		/* Make sure that the position you enter in will be erased */
		showexpl(newpp->p_y, newpp->p_x, FLYER);
# endif
	sendcom(newpp, REFRESH);
	sendcom(newpp, READY, 0);
	(void) fflush(newpp->p_output);
}

/*
 * rand_dir:
 *	Return a random direction
 */
int
rand_dir()
{
	switch (rand_num(4)) {
	  case 0:
		return LEFTS;
	  case 1:
		return RIGHT;
	  case 2:
		return BELOW;
	  case 3:
		return ABOVE;
	}
	/* NOTREACHED */
	return(-1);
}

/*
 * get_ident:
 *	Get the score structure of a player
 */
IDENT *
get_ident(machine, uid, name, team)
	u_long	machine;
	u_long	uid;
	const char	*name;
	char	team;
{
	IDENT		*ip;
	static IDENT	punt;

	for (ip = Scores; ip != NULL; ip = ip->i_next)
		if ((unsigned long)ip->i_machine == machine
		&&  (unsigned long)ip->i_uid == uid
		&&  ip->i_team == team
		&&  strncmp(ip->i_name, name, NAMELEN) == 0)
			break;

	if (ip != NULL) {
		if (ip->i_entries < SCOREDECAY)
			ip->i_entries++;
		else
			ip->i_kills = (ip->i_kills * (SCOREDECAY - 1))
				/ SCOREDECAY;
		ip->i_score = ip->i_kills / (double) ip->i_entries;
	}
	else {
		ip = (IDENT *) malloc(sizeof (IDENT));
		if (ip == NULL) {
			/* Fourth down, time to punt */
			ip = &punt;
		}
		ip->i_machine = machine;
		ip->i_team = team;
		ip->i_uid = uid;
		strncpy(ip->i_name, name, NAMELEN);
		ip->i_kills = 0;
		ip->i_entries = 1;
		ip->i_score = 0;
		ip->i_absorbed = 0;
		ip->i_faced = 0;
		ip->i_shot = 0;
		ip->i_robbed = 0;
		ip->i_slime = 0;
		ip->i_missed = 0;
		ip->i_ducked = 0;
		ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
		ip->i_stillb = ip->i_saved = 0;
		ip->i_next = Scores;
		Scores = ip;
	}

	return ip;
}
