#ifndef lint
static char *RCSid = "$Header: navi.c,v 1.1 89/12/14 08:20:15 jay Exp $";
#endif

/*
 * navi.c
 *
 * navigate ships and such.
 *
 * from PSL Empire, 1985
 */

#include <ctype.h>
#include "misc.h"
#include "var.h"
#include "ship.h"
#include "sect.h"
#include "news.h"
#include "xy.h"
#include "nsc.h"
#include "nat.h"
#include "path.h"
#include "deity.h"
#include "file.h"

static void ship_mess();
static int check_nav();
 /* return codes from check_nav */
#define CN_LANDLOCKED	(1)
#define CN_CONSTRUCTION	(2)
#define CN_ERROR	(-1)
#define CN_NAVIGABLE	(0)

navi()
{
	extern double techfact();
	extern char *argp[];
	struct sctstr sect;
	register n;
	register m;
	char   *cp;
	int	max;
	int     dir;
	int     nships;
	coord     newx;
	coord     newy;
	coord     dx;
	coord     dy;
	int     stopping;
	int     viewing;
	int     snums[NS_LSIZE];
	int     mines;
	int     shells;
	double  mobcost;
	double  mobils[NS_LSIZE];
	double  minmob;
	struct nstr_item nb;
	struct shpstr ships[NS_LSIZE];
	struct shpstr *shpq;
	struct shpstr *shpp;
	struct mchrstr *mcp;
	int	vec[I_MAX+1];
	int	time_to_stop;

	if (!snxtitem(&nb, EF_SHIP, argp[1]))
		return RET_SYN;
	minmob = 9999.0;
	n = 0;
	while (nxtitem(&nb, (char *)&ships[n])) {
		if (!owner)
			continue;
#ifdef	SAIL
		if (*ships[n].shp_path) /* can't nav a ship that's sailin, bad things happend */
		  {
		    ship_mess("has a sail path, `sail <#> -' to reset",ships[n].shp_uid,ships+n);
		    continue;
		  }
#endif	SAIL
		snums[n] = ships[n].shp_uid;
		shpp = &ships[n];
		mobils[n] = (double) shpp->shp_mobil;
		if (mobils[n] < minmob)
			minmob = mobils[n];
		/* check crew - uws don't count */
		getvec(VT_ITEM, vec, (char *)&ships[n], EF_SHIP);
		if (vec[I_MILIT] == 0 && vec[I_CIVIL] == 0) {
			ship_mess("crewless", snums[n], shpp);
			continue;
		}
		if (!getsect(shpp->shp_x, shpp->shp_y, &sect))
			return RET_SYN;
		switch (check_nav(&sect)) {
		case CN_CONSTRUCTION:
			ship_mess("caught in a construction zone",
				snums[n], shpp);
			continue;
		case CN_LANDLOCKED:
			ship_mess("landlocked", snums[n], shpp);
			continue;
			/*NOTREACHED*/
		case CN_NAVIGABLE:
#ifdef RETREAT
			shpp->shp_rflags = 0;
			bzero(shpp->shp_rpath,RET_LEN);
#endif /* RETREAT */
			break;
		case CN_ERROR:
		default:
			ship_mess("subject to an empire error", snums[n],
					  shpp);
			continue;
			/*NOTREACHED*/
		}
		n++;			    /* good ship - keep it */
	}
	if (n == 0) {
		pr("No ships\n");
		return RET_SYN;
	}
	nships = n;
	shpq = ships;
	stopping = 0;
	time_to_stop = 0;
	cp = argp[2];
	while (!stopping) {
		char    *bp,dp[80];
		dx = dy = viewing = 0;
		if (cp == 0 || *cp == 0)
			cp = getstring(fmt("<%.1f:%.1f: %s> ", mobils[0],
				minmob, xyas(shpq->shp_x, shpq->shp_y, cnum)));
		if (cp == 0 || *cp == 0)
			cp = &dirch[DIR_STOP];
		if (*cp == 'r' || *cp == 'l' || *cp == 's') {
			argp[1] = ++cp;
			argp[1] = 0;
			for (n = 0; n < nships; n++)
				if (snums[n] != -1)
					putship(snums[n], &ships[n]);
			/* Make it default to the current ship, but */
			/* Malso allow them to specify the ship on the */
			/* same line */
			bp=cp;
			while((*bp != ' ') && (*bp)) bp++;
			while((*bp == ' ') && (*bp)) bp++;
			if ((bp != (char *)0) && (*bp))
				argp[1] = bp;
			else{
				sprintf(dp,"%d",snums[0]);
				argp[1] = dp;
			}
			if (cp[-1] == 'r') {
				rada();
				NAT_DELTA(nat_btu, cnum, -1);
			} else if (cp[-1] == 'l') {
				look();
				NAT_DELTA(nat_btu, cnum, -1);
			} else {
				sona();
				NAT_DELTA(nat_btu, cnum, -1);
			}
			cp = 0;
			continue;
		} else {
			dir = chkdir(*cp++, DIR_STOP, DIR_VIEW);
			if (dir == -1) {
				pr(fmt("\"%c\" is not legal...", cp[-1]));
				direrr("`%c' to stop", " & `%c' to view\n", 0);
				cp = "";
				continue;
			}
			if (dir == DIR_VIEW)
				viewing++;
			else if (dir == DIR_STOP)
				stopping++;
			else {
				dx = diroff[dir][0];
				dy = diroff[dir][1];
			}
		}
		minmob = 9999.0;
		for (n = 0; n < nships; n++) {
			if (snums[n] == -1)
				continue;
			shpp = &ships[n];
			mcp = &mchr[shpp->shp_type];
			newx = xnorm(shpp->shp_x + dx);
			newy = ynorm(shpp->shp_y + dy);
			if (viewing || stopping || god)
				mobcost = 0.0;
			else {
				if (mobils[n] <= 0.0) {
					ship_mess("out of mobility", snums[n], shpp);
					continue;
				}
				mobcost = shpp->shp_effic * 0.01 * mcp->m_speed;
				mobcost = 480.0 /
				    (mobcost + techfact(shpp->shp_tech, mobcost));
			}
			getsect(newx, newy, &sect);
			if (check_nav(&sect) != CN_NAVIGABLE ||
			    (sect.sct_own && !owner &&
			     getrel(getnatp(sect.sct_own), cnum) != ALLIED)) {
#ifdef	SHIPNAMES
				pr(fmt("%s %s(#%d) can't go to %s\n",
				       mcp->m_name, shpp->shp_name, snums[n], 
#else
				pr(fmt("%s #%d can't go to %s\n",
				       mcp->m_name, snums[n], 
#endif	SHIPNAMES
				       xyas(newx, newy, cnum)));
				cp = 0;
				if (mobils[n] < minmob)
					minmob = mobils[n];
				continue;
			}
			if (viewing) {
				if (mcp->m_flags & M_FOOD)
					pr(fmt("[fert:%d] ", sect.sct_fertil));
				if (mcp->m_flags & M_OIL)
					pr(fmt("[oil:%d] ", sect.sct_oil));
#ifdef	SHIPNAMES
				pr(fmt("%s %s(#%d) @ %s %d%% %s\n", mcp->m_name,
				       shpp->shp_name, snums[n],
				       xyas(newx, newy, cnum),
#else
				pr(fmt("%s #%d @ %s %d%% %s\n", mcp->m_name,
				       snums[n], xyas(newx, newy, cnum),
#endif	SHIPNAMES
				       sect.sct_effic,
				       dchr[sect.sct_type].d_name));
			}
#ifdef	AUTONAV
			if (n == 0) {
				radmapnopr(newx, newy, (int)shpp->shp_effic, 
				  (int)techfact(shpp->shp_tech,
				  (double) mchr[shpp->shp_type].m_vrnge),
				  (double)((mchr[shpp->shp_type].m_flags 
				    & M_SONAR)
				      ? techfact(shpp->shp_tech, 1.0) : 0.0));
			}
#endif	AUTONAV
			shpp->shp_x = newx;
			shpp->shp_y = newy;
			mobils[n] -= mobcost;
			if (mobils[n] <= 0.0 || stopping) {
#ifdef	SHIPNAMES
				pr(fmt("%s %s(#%d) stopped at %s\n",
				mcp->m_name, shpp->shp_name,
#else
				pr(fmt("%s #%d stopped at %s\n", mcp->m_name,
#endif	SHIPNAMES
				       snums[n],
				       xyas(shpp->shp_x, shpp->shp_y, cnum)));
				shpp->shp_mobil = (int) mobils[n];
				putship(snums[n], shpp);
				snums[n] = -1;
				time_to_stop = 1;
				cp = &dirch[DIR_STOP];
			}
			if (mobils[n] < minmob)
				minmob = mobils[n];
			if (viewing || stopping)
				continue;
			mines = getvar(V_MINE, (char *)&sect, EF_SECTOR);
			if ((mcp->m_flags & M_SWEEP) && mines > 0 && !owner) {
				max = vl_find(V_SHELL, mcp->m_vtype,
					mcp->m_vamt, (int) mcp->m_nv);
				shells = getvar(V_SHELL, (char *)shpp, EF_SHIP);
				for (m=0; mines > 0 && m < 5; m++) {
					if (chance(0.66)) {
						pr("Sweep...\n");
						mines--;
						shells = min(max, shells + 1);
					}
				}
				putvar(V_MINE, mines, (char *)&sect, EF_SECTOR);
				putvar(V_SHELL, shells, (char *)shpp, EF_SHIP);
				putsect(&sect);
			}
			if (mines > 0 && !owner && chance(0.05 * mines)) {
#ifdef	SHIPNAMES
				pr(fmt("\007Kawhomp! Mine detected by %s %s(#%d) in %s!\n",
				       mcp->m_name, shpp->shp_name,
				       shpp->shp_uid,
#else
				pr(fmt("\007Kawhomp! Mine detected by %s #%d in %s!\n",
				       mcp->m_name, shpp->shp_uid,
#endif	SHIPNAMES
				       xyas(newx, newy, cnum)));
				nreport(cnum, 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;
				if (mcp->m_flags & M_SWEEP)
					m /= 2;
				shipdamage(shpp, m);
				mines--;
				putvar(V_MINE, mines, (char *)&sect, EF_SECTOR);
				putsect(&sect);
				cp = 0;
				if (shpp->shp_effic < 20) {
					time_to_stop = 1;
					snums[n] = -1;
				}
				putship(shpp->shp_uid, (char *)shpp);
				pr(fmt("%d%% damage sustained\n", m));
			}
		}
		if (time_to_stop)
			stopping = 1;
	}
	for (n = 0; n < nships; n++) {
		if (snums[n] == -1)
			continue;
		shpp = &ships[n];
		if (shpp->shp_own == 0)
			continue;
		mcp = &mchr[shpp->shp_type];
#ifdef	SHIPNAMES
		pr(fmt("%s %s(#%d) stopped at %s\n", mcp->m_name,
			shpp->shp_name, snums[n],
#else
		pr(fmt("%s #%d stopped at %s\n", mcp->m_name, snums[n],
#endif	SHIPNAMES
		       xyas(shpp->shp_x, shpp->shp_y, cnum)));
		if (mobils[n] < 0)
			mobils[n] = 0;
		shpp->shp_mobil = mobils[n];
		putship(snums[n], shpp);
	}
	return RET_OK;
}

static void 
ship_mess(str, n, shp)
	char   *str;
	int     n;
	struct shpstr *shp;
{
	extern struct mchrstr mchr[];

#ifdef	SHIPNAMES
	pr(fmt("%s %s(#%d) is %s & stays in %s\n",
	       mchr[shp->shp_type].m_name, shp->shp_name, n,
#else
	pr(fmt("%s #%d is %s & stays in %s\n",
	       mchr[shp->shp_type].m_name, n,
#endif	SHIPNAMES
	       str, xyas(shp->shp_x, shp->shp_y, shp->shp_own)));
}

static int 
check_nav(sect)
	struct	sctstr *sect;
{
	extern struct dchrstr dchr[];

	switch (dchr[sect->sct_type].d_flg & 03) {
	case NAVOK:
		break;

	case NAV_02:
		if (sect->sct_effic < 2)
			return CN_CONSTRUCTION;
		break;
	case NAV_60:
		if (sect->sct_effic < 60)
			return CN_CONSTRUCTION;
		break;
	default:
		return CN_LANDLOCKED;
	}
	return CN_NAVIGABLE;
}
