#ifndef lint
static char *RCSid = "$Header: /u/dhay/stan/kent/update/RCS/sail.c,v 1.1 91/03/30 01:46:07 dhay Exp Locker: dhay $";
#endif

/*
 * sail.c
 *
 * Sail the ships.
 *
 * Doug Hay.
 * Cleaned up code originally written by Robert Forsman.
 */

#include "misc.h"
/* One massive case to disable all of this. */
#ifdef SAIL
#include "var.h"
#include "sect.h"
#include "path.h"
#include "ship.h"
#include "var.h"
#include "news.h"
#include "file.h"

/* stole these from player/commands/navi.c */
#define CN_LANDLOCKED	(1)
#define CN_CONSTRUCTION	(2)
#define CN_ERROR	(-1)
#define CN_NAVIGABLE	(0)

static
cost_ship(sp, ep, fp)
	struct shpstr	*sp;
	struct fltelemstr *ep;
	struct fltheadstr *fp;
{
	double	mobcost,ceil();
	int	howfar;

	mobcost = 0.0;
	if (sp->shp_effic > 0) {
		mobcost = sp->shp_effic * mchr[sp->shp_type].m_speed * 0.01;
		mobcost = 480.0 / ( mobcost*(1 + (50+sp->shp_tech)/
					(double)(200+sp->shp_tech)));
	}
	if (sp->shp_mobquota<0)
		sp->shp_mobquota=0;

	howfar = 0;
	if (mobcost > 0) {
		howfar = (int)sp->shp_mobil - (int)sp->shp_mobquota;
		howfar = ceil((howfar / mobcost));
	}
	if (howfar<0)
		howfar=0;
#ifdef SAILDEBUG
	wu(0,fp->own,fmt(
		"Ship #%d can move %d spaces on mobility %d (cost/sect %f)\n",
		sp->shp_uid, howfar, sp->shp_mobil, mobcost));
#endif
	if (howfar < fp->maxmoves)
		fp->maxmoves = howfar;

	ep->mobil = sp->shp_mobil;
	ep->mobcost = mobcost;
}

static
sail_find_fleet(head, sp)
	struct fltheadstr **head;
	struct shpstr	*sp;
{
	struct fltheadstr *fltp;
	struct shpstr	*ap;
	struct fltelemstr *this;
	int  len=0;
	int  follow = -1;
	int  n, stop;
	char *cp;

	if (sp->shp_own==0)
		return(0);
	/* If this ship is following, find the head of the follow list. */
	for (ap=sp; ap; len++, ap=getshipp(follow)) {
		follow = ap->shp_follow;
		/* Not same owner */
		if (ap->shp_own != sp->shp_own) {
			wu(0, sp->shp_own, fmt(
				"Ship #%d, following #%d, which you don't own.\n",
				sp->shp_uid, ap->shp_uid));
			return(0);
		}
		/* Not a follower. */
		if (ap->shp_path[0] != 'f')
			break;
		/* Following itself */
		if (follow==ap->shp_uid)
			break;
	}
	if (!ap) {
		wu(0, sp->shp_own, fmt(
			"Ship #%d, following #%d, which you don't own.\n",
			sp->shp_uid, follow));
		return(0);
	}

	/* This should prevent infinite loops. */
	if (len>=10) {
		wu(0,sp->shp_own,
		fmt("Ship #%d, too many follows (circular follow?).\n",
			sp->shp_uid));
		return(0);
	}

	for (stop=0,cp=ap->shp_path; (!stop) && (*cp); cp++) {
		switch (*cp) {
			case 'y':
			case 'u':
			case 'g':
			case 'j':
			case 'b':
			case 'n':
			case 'h':
			case 't':
				break;
			default:
				stop=1;
		}
	}

	/* we found a non-valid char in the path. */
	if (*cp) {
		wu(0,ap->shp_own,fmt(
			"invalid char '\\%03o' in path of ship %d\n",
			(unsigned char)*cp, ap->shp_uid));
		*cp=0;
	}

	/* if this ship is not sailing anywhere then ignore it. */
	if (!*ap->shp_path)
		return(0);

	/* Find the fleet structure we belong to. */
	for (fltp=(*head); (fltp && fltp->leader != follow); fltp = fltp->next)
		;

	if (!fltp) {
		fltp = (struct fltheadstr *) malloc(sizeof(*fltp));
		bzero(fltp, sizeof(*fltp));

		/* Fix the links. */
		fltp->next = (*head);
		*head = fltp;

		/* Set the leader. */
		fltp->leader = ap->shp_uid;
		fltp->real_q = LEADER_REAL;
		fltp->x = ap->shp_x;
		fltp->y = ap->shp_y;
		fltp->own = ap->shp_own;
		fltp->maxmoves = 500;
	}

	/* If the fleet is not in the same sector as us, no go. */
	if ( ( fltp->x!=sp->shp_x ) || ( fltp->y!=sp->shp_y ) ) {
		wu(0,sp->shp_own,fmt(
			"Ship %d not in same sector as its sailing fleet\n",
			sp->shp_uid));
		fltp->real_q = LEADER_WRONGSECT;
		return(0);
	}

	this = (struct fltelemstr *) malloc(sizeof(*this));
	bzero(this, sizeof(*this));
	this->num = sp->shp_uid;
	this->own = sp->shp_own;
	this->next = fltp->head;
	fltp->head = this;
	cost_ship(sp, this, fltp);
}

static
sail_indir(fltp, dirch)
	struct fltheadstr *fltp;
	int dirch;
{
	struct fltelemstr *fe;
	struct shpstr	*sp;
	struct	sctstr *sectp;
	int	mines;
	int	error=0;
	coord	newx,newy;
	int	dir;

	dir = nav_chkdir(dirch, DIR_STOP, DIR_LAST);
	if ((dir == -1) || (dir == DIR_STOP)) {
		wu(0,fltp->own,fmt(
			"Your fleet lead by ship #%d has reached %s.\n",
			fltp->leader,xyas(fltp->x,fltp->y,fltp->own)));
		if ('t' == dirch)
			return(0);
		return(1);
	}
	newx = xnorm(fltp->x + diroff[dir][0]);
	newy = ynorm(fltp->y + diroff[dir][1]);
#ifdef SAILDEBUG
	wu (0,fltp->own,fmt(
		"travel from %s to %s\n",
		xyas(fltp->x,fltp->y,fltp->own), xyas(newx,newy,fltp->own)));
#endif
	sectp = getsectp(newx, newy);
	if ((check_nav(sectp)!=CN_NAVIGABLE) ||
			(sectp->sct_own && sectp->sct_own!=fltp->own)) {
		wu(0,fltp->own,fmt(
			"Your fleet lead by %d cannot go to %s.\n",
			fltp->leader,xyas(newx,newy,fltp->own)));
		return(1);
	}
	mines = getvar(V_MINE,sectp,EF_SECTOR);
	fltp->maxmoves--;
	for (fe=fltp->head; fe!=0;fe = fe->next) {
		struct mchrstr	*mcp;

		sp = getshipp(fe->num);
		nav_shiprad(sp, newx, newy);
		mcp = mchr + sp->shp_type;
		fe->mobil -= fe->mobcost;
#ifdef SAILDEBUG
		wu(0,fltp->own,fmt("Ship #%d has %d mobility now.\n",
				fe->num,(int)fe->mobil));
#endif
		if ( mines>0 && (mchr[sp->shp_type].m_flags & M_SWEEP) &&
				sectp->sct_own!=fltp->own) {
			int max, tries, shells, swept;
			max = vl_find(V_SHELL,mcp->m_vtype,mcp->m_vamt,
					mcp->m_nv);
			shells = getvar(V_SHELL,sp,EF_SHIP);
			for (tries=0,swept=0; mines>0 && tries<5; tries++)
				if (chance(0.66)) {
					mines--;
					swept++;
					shells = min(max,shells+1);
				}
			if (swept) {
				wu(0,fltp->own,fmt("Ship #%d swept %d mines.\n",
					sp->shp_uid,swept));
				putvar(V_SHELL,shells,sp,EF_SHIP);
				error = 2;
			}
		}
		if (mines>0 && sectp->sct_own!=fltp->own &&
				chance(0.05*mines)) {
			int	m;
			wu(0,fltp->own,fmt(
				"Kawhomp! Ship #%d hits mine at %s!:",
				sp->shp_uid,xyas(newx,newy,fltp->own)));
			nreport(sp->shp_own, N_HIT_MINE, 0, 1);
			if (mcp->m_glim)
				m = 15 + 1350 / mcp->m_armor;
			else
				m = 2100 / (mcp->m_vrnge*mcp->m_visib);
			m += random() % 21;
			shipdamage(sp,m);
			mines--;
			putvar(V_MINE, mines, (char *)sectp, EF_SECTOR);
			wu(0,fltp->own,fmt("%d%% damage sustained\n", m));
			error = 2;	/* edit ship location first */
		}
	}
	putvar(V_MINE, mines, (char *)sectp, EF_SECTOR);
	fltp->x = newx;
	fltp->y = newy;
#ifdef SAILDEBUG
	wu (0,fltp->own,fmt("        travel to %s\n",
			xyas(fltp->x,fltp->y,fltp->own)));
#endif
	return(error);
}

static
sail_nav_fleet(fltp)
	struct fltheadstr *fltp;
{
	struct fltelemstr *fe;
	struct shpstr	*sp;
	int	vec[I_MAX+1];
	struct sctstr *sectp;
	int	error=0;
	char	*s, *p;

#ifdef SAILDEBUG
	switch (fltp->real_q) {
	case LEADER_VIRTUAL:
		s = "leaderless";
		break;
	case LEADER_REAL:
		s = "real";
		break;
	case LEADER_WRONGSECT:
		s = "scattered";
		break;
	default:
		s = "inconsistent";
	}
	wu(0,fltp->own,fmt(
		"Fleet lead by %d is %s, can go %d spaces\n  contains ships:",
		fltp->leader, s, fltp->maxmoves));
	for (fe=fltp->head; fe; fe = fe->next)
		wu(0, fltp->own, fmt(" %d", fe->num));
	wu(0, fltp->own, "\n");
#endif

	sectp = getsectp(fltp->x, fltp->y);
	
	switch (check_nav(sectp)) {
	case CN_NAVIGABLE:
		break;
	case CN_CONSTRUCTION:
	case CN_LANDLOCKED:
	default:
		wu(0,fltp->own,fmt(
			"Your fleet lead by %d is trapped by land.\n",
			fltp->leader));
		return(0);
	}

	for (fe=fltp->head; fe; fe = fe->next) {
		sp = getshipp(fe->num);
		getvec(VT_ITEM, vec, (char *)sp, EF_SHIP);
		if (vec[I_MILIT]==0 && vec[I_CIVIL]==0) {
			wu(0,fltp->own,fmt(
			"   ship #%d (%s) is crewless and can't go on\n",
					fe->num, cname(fe->own)));
			error = 1;
		}
	}
	if (error)
		return(0);
	sp = getshipp(fltp->leader);
	openbigmap(sp->shp_own);
	for (s=sp->shp_path; (*s) && (fltp->maxmoves>0); s++) {
		error = sail_indir(fltp, *s);

		if (error==2) s++; /* if we hit a mine, eat the move. */
		if (error) break;
	}
	writebigmap();
	closebigmap();
	for (p=sp->shp_path; *s; p++,s++)
		*p = *s;
	*p = 0;
#ifdef SAILDEBUG
	if (sp->shp_path[0]) {
		wu(0,fltp->own,fmt(
			"Fleet lead by #%d nav'd to %s, path left = %s\n",
			fltp->leader, xyas(fltp->x,fltp->y,fltp->own),
			&sp->shp_path));
	} else
		wu(0,fltp->own,fmt(
			"Fleet lead by #%d nav'd to %s, finished.\n",
			fltp->leader, xyas(fltp->x,fltp->y,fltp->own)));
#endif
	for (fe=fltp->head; fe!=0; fe = fe->next) {
		sp = getshipp(fe->num);
		sp->shp_x = fltp->x;
		sp->shp_y = fltp->y;
		sp->shp_mobil = fe->mobil;
#ifdef SAILDEBUG
		wu(0,sp->shp_own,fmt(
			"Ship #%d has %d mobility now.\n",
			fe->num, (int)fe->mobil));
#endif
	}

}

sail_ship()
{
	struct shpstr	*sp;
	struct fltheadstr *head=0;
	struct fltheadstr *fltp;
	int	n;

	for (n=0; sp = getshipp(n); n++)
		sail_find_fleet(&head, sp);

	/* see what the fleets fall out into */
	for (fltp=head; fltp; fltp = fltp->next)
		sail_nav_fleet(fltp);

	/* Free up the memory, 'cause I want to. */
	for (fltp=head; fltp!=0; ) {
		struct fltelemstr *fe;
		struct fltheadstr	*saveh;
		saveh = fltp->next;
		for (fe=fltp->head; fe!=0; ) {
			struct fltelemstr *saveel;
			saveel = fe->next;
			free(fe);
			fe = saveel;
		}
		free (fltp);
		fltp = saveh;
	}
}
#endif SAIL
