#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/subs/shpsub.c,v 2.20 1995/10/25 20:07:27 empire Exp $";
#endif

/*
 * shpsub.c
 *
 * Ship subroutine stuff
 *
 * Ken Stevens 1995
 */

#include "misc.h"
#include "queue.h"
#include "player.h"
#include "var.h"
#include "sect.h"
#include "ship.h"
#include "plane.h"
#include "land.h"
#include "news.h"
#include "item.h"
#include "xy.h"
#include "nsc.h"
#include "file.h"
#include "nat.h"
#include "path.h"
#include "mission.h"
#include "optlist.h"
#include "damage.h"
#include "edb.h"

void
shp_sel(ni, list/*, wantflags, nowantflags */)
	struct	nstr_item *ni;
	struct	qelem *list;
/*	int	wantflags;
	int	nowantflags;
*/
{
	struct	shpstr ship;
	struct	mchrstr *mcp;
	struct	mlist *mlp;

	initque(list);
	while (nxtitem(ni, (s_char *)&ship)) {
		if (!player->owner)
			continue;
		mcp = &mchr[ship.shp_type];
/*		if (wantflags && (mcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && mcp->m_flags & nowantflags)
			continue;
*/
		ship.shp_mission = 0;
		ship.shp_rflags = 0;
		bzero(ship.shp_rpath,RET_LEN);
		putship(ship.shp_uid, &ship);
		mlp = (struct mlist *) malloc(sizeof(struct mlist));
		mlp->mcp = mcp;
		bcopy((s_char *)&ship, (s_char *)&mlp->ship,
			sizeof(struct shpstr));
		mlp->mobil = (double)ship.shp_mobil;
		insque(&mlp->queue, list);
	}
}

/* This function assumes that the list was created by shp_sel */
void
shp_nav(list, minmobp, maxmobp, togetherp, actor)
	struct	qelem *list;
	double	*minmobp;
	double	*maxmobp;
	int	*togetherp;
	natid	actor;
{
	extern int update_pending;
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	struct	sctstr sect;
	struct	shpstr ship;
	int	vec[I_MAX+1];
	coord   allx;
	coord   ally;
	int	first=1;

	*minmobp = 9876.0;
	*maxmobp = -9876.0;
	*togetherp = 1;
	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		getship(mlp->ship.shp_uid, &ship);
		if (ship.shp_own != actor) {
			mpr(actor, "%s was sunk at %s\n",
			    prship(&ship),
			    xyas(ship.shp_x, ship.shp_y, actor));
			remque((struct qelem *)mlp);
			free((s_char *)mlp);
			continue;
		}
#ifdef SAIL
		if (*ship.shp_path && !update_pending) {
			shp_mess("has a sail path", mlp);
			mpr(actor, "Use `sail <#> -' to reset\n");
			continue;
		}
#endif /* SAIL */
		/* check crew - uws don't count */
		getvec(VT_ITEM, vec, (s_char *)&ship, EF_SHIP);
		if (vec[I_MILIT] == 0 && vec[I_CIVIL] == 0) {
			shp_mess("is crewless", mlp);
			continue;
		}
		if (!getsect(ship.shp_x, ship.shp_y, &sect)) {
			shp_mess("was sucked into the sky by a strange looking spaceship", mlp); /* heh -KHS */
			continue;
		}
		switch (shp_check_nav(&sect)) {
		case CN_CONSTRUCTION:
			shp_mess("is caught in a construction zone", mlp);
			continue;
		case CN_LANDLOCKED:
			shp_mess("is landlocked", mlp);
			break;
		case CN_NAVIGABLE:
			break;
		case CN_ERROR:
		default:
			shp_mess("was just swallowed by a big green worm", mlp); /* heh -KHS */
			continue;
		}
		if (first){
			allx = ship.shp_x;
			ally = ship.shp_y;
			first = 0;
		}
		if (ship.shp_x != allx ||
		    ship.shp_y != ally)
			*togetherp = 0;
		if (ship.shp_mobil + 1 < (int)mlp->mobil) {
			mlp->mobil = (double)ship.shp_mobil;
		}
		if (mlp->mobil < *minmobp)
			*minmobp = mlp->mobil;
		if (mlp->mobil > *maxmobp)
			*maxmobp = mlp->mobil;
		bcopy((s_char *)&ship, (s_char *)&mlp->ship,
			sizeof(struct shpstr));
	}
}

shp_put(list, actor)
	struct	qelem *list;
	natid	actor;
{
	register struct qelem *qp;
	register struct qelem *newqp;
	struct	mlist *mlp;

	qp = list->q_back;
	while (qp != list) { 
		mlp = (struct mlist *) qp;
		mpr(actor, "%s stopped at %s\n", prship(&mlp->ship),
		    xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
		mlp->ship.shp_mobil = (int)mlp->mobil;
		putship(mlp->ship.shp_uid, &mlp->ship);
		newqp = qp->q_back;
		remque(qp);
		free((s_char *)qp);
		qp = newqp;
	}
}

void
shp_sweep(ship_list, verbose, actor)
	struct  qelem *ship_list;
	int 	verbose;
	natid	actor;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  mlist   *mlp;
	struct  sctstr  sect;
	int	mines, m, max, shells;
	int	changed = 0;
	double	mobcost;

        for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
                next = qp->q_back;
                mlp = (struct mlist *) qp;
		if (!(mlp->mcp->m_flags & M_SWEEP)) {
			if (verbose)
				mpr(actor, "%s doesn't have minesweeping capability!\n", prship(&mlp->ship));
			continue;
		}
		if (mlp->mobil < 0.0) {
			if (verbose)
				mpr(actor, "%s is out of mobility!\n", prship(&mlp->ship));
			continue;
		}
		getsect(mlp->ship.shp_x,mlp->ship.shp_y,&sect);
		if (sect.sct_type != SCT_WATER) {
			if (verbose)
				mpr(actor, "%s is not at sea.  No mines there!\n", prship(&mlp->ship));
			continue;
		}		
		mobcost = mlp->ship.shp_effic * 0.01 * mlp->mcp->m_speed;
		mobcost = 480.0 / (mobcost +
				   techfact(mlp->ship.shp_tech,mobcost));
		mlp->mobil -= mobcost;
		mlp->ship.shp_mobil = (int)mlp->mobil;
		putship(mlp->ship.shp_uid, &mlp->ship);
		if (!(mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR)))
			continue;
		max = vl_find(V_SHELL, mlp->mcp->m_vtype,
			      mlp->mcp->m_vamt, (int)mlp->mcp->m_nv);
		shells = getvar(V_SHELL, (s_char *)&mlp->ship, EF_SHIP);
		for (m=0; mines > 0 && m < 5; m++) {
			if (chance(0.66)) {
				mpr(actor, "Sweep...\n");
				mines--;
				shells = min(max, shells + 1);
				changed |= map_set(actor,
						   sect.sct_x,
						   sect.sct_y,
						   'X', 0);
			}
		}
		putvar(V_MINE, mines, (s_char *)&sect, EF_SECTOR);
		putvar(V_SHELL, shells, (s_char *)&mlp->ship, EF_SHIP);
		putship(mlp->ship.shp_uid, &mlp->ship);
		putsect(&sect);
	}
	if (changed)
		writemap(actor);
}

int
shp_check_mines(ship_list)
	struct  qelem *ship_list;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  mlist   *mlp;
	struct  sctstr  sect;
	int	mines;
	int	changed = 0;
	int	stopping = 0;
	natid	actor = -1;

        for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
                next = qp->q_back;
                mlp = (struct mlist *) qp;
		getsect(mlp->ship.shp_x,mlp->ship.shp_y,&sect);
		if (sect.sct_type != SCT_WATER)
			return 0;
		if (!(mines=getvar(V_MINE,(s_char *)&sect,EF_SECTOR)))
			return 0;
		if (chance(DMINE_HITCHANCE(mines))) {
			shp_hit_mine(&mlp->ship, mlp->mcp);
			mines--;
			changed |= map_set(actor = mlp->ship.shp_own,
					   sect.sct_x,
					   sect.sct_y,
					   'X', 0);
			putvar(V_MINE,mines,(s_char *)&sect,EF_SECTOR);
			putsect(&sect);
			putship(mlp->ship.shp_uid, (s_char *)&mlp->ship);
			if (!mlp->ship.shp_own) {
				stopping = 1;
				remque(qp);
				free((s_char *)qp);
			}
		}
	}
	if (changed)
		writemap(actor);
	return stopping;
}

int
shp_list(ship_list)
	struct  qelem *ship_list;
{
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  mlist   *mlp;
	struct	shpstr	*shp;
	int	vec[I_MAX+1];

	pr("shp#     ship type       x,y   fl  eff mil  sh gun pn he xl ln mob tech\n");

        for (qp=ship_list->q_back;qp!=ship_list;qp=next) {
                next = qp->q_back;
                mlp = (struct mlist *) qp;
		shp = &mlp->ship;
		pr("%4d ", shp->shp_uid);
		pr("%-16.16s ", mlp->mcp->m_name);
		prxy("%4d,%-4d ", shp->shp_x, shp->shp_y, mlp->ship.shp_own);
		pr("%1c", shp->shp_fleet);
		pr("%4d%%", shp->shp_effic);
		getvec(VT_ITEM, vec, (s_char *)shp, EF_SHIP);
		pr("%4d", vec[I_MILIT]);
		pr("%4d", vec[I_SHELL]);
		pr("%4d", vec[I_GUN]);
		count_planes(shp);
		pr("%3d", shp->shp_nplane);
		pr("%3d",shp->shp_nchoppers);
		pr("%3d",shp->shp_nxlight);
		count_units(shp);
		pr("%3d", shp->shp_nland);
		pr("%4d", shp->shp_mobil);
		pr("%4d\n", shp->shp_tech);
	}
}

void 
shp_mess(str, mlp)
	s_char  *str;
	struct	mlist *mlp;
{
	mpr(mlp->ship.shp_own, "%s %s & stays in %s\n",
	    prship(&mlp->ship),
	    str, xyas(mlp->ship.shp_x, mlp->ship.shp_y, mlp->ship.shp_own));
	mlp->ship.shp_mobil = (int)mlp->mobil;
	putship(mlp->ship.shp_uid, &mlp->ship);
	remque((struct qelem *)mlp);
	free((s_char *)mlp);
}

int
shp_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;
}

static int
shp_count(list, wantflags, nowantflags)
	struct	qelem	*list;
	int	wantflags;
	int	nowantflags;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	int	count = 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && mlp->mcp->m_flags & nowantflags)
			continue;
		++count;
	}
	return count;
}

static int
shp_damage_one(mlp, dam)
	struct	mlist *mlp;
	int dam;
{
	shipdamage(&mlp->ship, dam);
	putship(mlp->ship.shp_uid, &mlp->ship);
	if (!mlp->ship.shp_own) {
		remque((struct qelem *)mlp);
		free((s_char *)mlp);
	}
}

static int
shp_damage(list, totdam, wantflags, nowantflags)
	struct	qelem	*list;
	int	totdam;
	int	wantflags;
	int	nowantflags;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	int	dam;
	int	count;

	if (!totdam || !(count = shp_count(list, wantflags, nowantflags)))
		return 0;
	dam = ldround(((double)totdam/(double)count),1);
	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && mlp->mcp->m_flags & nowantflags)
			continue;
		shp_damage_one(mlp, dam);
	}
	return dam;
}

static int
shp_contains(list, wantflags, nowantflags)
	struct qelem *list;
	int	wantflags;
	int	nowantflags;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && mlp->mcp->m_flags & nowantflags)
			continue;
		return 1;
	}
	return 0;
}

static struct mlist *
most_valuable_ship(list)
	struct qelem *list;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	struct	mlist *mvs = 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (mlp->mcp->m_flags & M_SUB)
			continue;
		if (!mlp->mcp->m_nxlight &&
		    !mlp->mcp->m_nchoppers &&
		    mlp->mcp->m_cost < 1000 &&
		    !mlp->mcp->m_nplanes &&
		    !mlp->mcp->m_nland)
			continue;
		if (!mvs) {
			mvs = mlp;
			continue;
		}
		if (mlp->mcp->m_cost * mlp->ship.shp_effic >
		    mvs->mcp->m_cost * mvs->ship.shp_effic)
			mvs = mlp;
	}
	return mvs;
}

static int shp_easiest_target(list, wantflags, nowantflags)
	struct	qelem	*list;
	int	wantflags, nowantflags;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	int	hard;
	int	easiest = 9876; /* things start great for victim */
	int	count = 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (wantflags && (mlp->mcp->m_flags & wantflags) != wantflags)
			continue;
		if (nowantflags && mlp->mcp->m_flags & nowantflags)
			continue;
		hard = shp_hardtarget(&mlp->ship);
		if (hard < easiest)
			easiest = hard; /* things get worse for victim */
		++count;
	}
	return easiest - count;
}

static int
shp_missile_interdiction(list, newx, newy, victim)
	struct	qelem *list;
	coord	newx, newy;
	natid	victim;
{
        int     dam;
	int	twotries;
	int	stopping = 0;
	struct	qelem msl_list, *qp, *newqp;
	struct	mlist *mvs;
	s_char	what[512];

	msl_sel(&msl_list, newx, newy, victim, P_T|P_MAR, 0, MI_INTERDICT);

	twotries = 0;
	while (!QEMPTY(&msl_list) && (mvs = most_valuable_ship(list))) {
		sprintf(what, "%s", prship(&mvs->ship));
		dam = msl_launch_mindam(&msl_list, newx, newy,
					shp_hardtarget(&mvs->ship),
					EF_SHIP,
					1,
					what, victim, MI_INTERDICT);
		if (dam) {
			mpr(victim, "missile interdiction mission does %d damage to %s!\n", dam, what);
			shp_damage_one(mvs, dam);
			twotries = 0;
			stopping |= 1;
		} else if (++twotries >= 2) {
			break;
		}
	}
	qp = msl_list.q_forw;
	while(qp != msl_list.q_forw) {
		newqp=qp->q_forw;
		remque(qp);
		free(qp);
		qp = newqp;
	}

	return stopping;
}

static int
notify_coastguard(list, trange, sectp)
        struct  qelem *list;
	int	trange;
	struct	sctstr *sectp;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	mlist *mlp;
	struct	natstr *natp;
	int	vrange;

	natp = getnatp(sectp->sct_own);
	if (!(natp->nat_flags & NF_COASTWATCH))
		return;

	vrange = sectp->sct_type==SCT_RADAR?14:4;
	vrange *= tfact(sectp->sct_own, 1.0) * sectp->sct_effic / 100.0;

	if (vrange < 1)
		vrange = 1;

	if (vrange < trange)
		return 0;

	for (qp = list->q_back; qp != list; qp = next) {
		next = qp->q_back;
		mlp = (struct mlist *) qp;
		if (mlp->mcp->m_flags & M_SUB)
			continue;
		prsync(sectp->sct_own, &mlp->ship, EDV_COAS);
		wu(0, sectp->sct_own,
		   "%s %s sighted at %s\n",
		   cname(mlp->ship.shp_own),
		   prship(&mlp->ship),
		   xyas(mlp->ship.shp_x, mlp->ship.shp_y, sectp->sct_own));
#ifdef HIDDEN
		setcont(sectp->sct_own, mlp->ship.shp_own, FOUND_LOOK);
#endif /* HIDDEN */
	}

	return 1;
}

static int
shp_fort_interdiction(list, newx, newy, victim)
	struct	qelem *list;
	coord	newx, newy;
	natid	victim;
{
	extern	int fort_max_interdiction_range;
	struct	nstr_sect ns;
	struct	sctstr fsect;
	int	trange;
	double	range, range2, guneff;
	int	shell, gun;
	int	dam;
	int	totdam = 0;
	s_char	notified[MAXNOC];
	int	i;

	/* Inform neutral and worse */
	for (i = 0; i < MAXNOC; ++i) {
		if (getrel(getnatp(i),victim) <= NEUTRAL)
			notified[i] = 0;
		else
			notified[i] = 1;
	}

	snxtsct_dist(&ns,newx,newy,fort_max_interdiction_range);
	while (nxtsct(&ns,&fsect)){
		if (!fsect.sct_own)
			continue;
		if (fsect.sct_own == victim)
			continue;
		if (notified[fsect.sct_own])
			continue;
		trange = mapdist(newx, newy,
				 fsect.sct_x, fsect.sct_y);
		if (notify_coastguard(list, trange, &fsect))
			notified[fsect.sct_own] = 1;
	}
	/* Only fire at Hostile ships */
	for (i = 0; i < MAXNOC; ++i) {
		if (getrel(getnatp(i),victim) >= NEUTRAL)
			notified[i] = 0;
	}
	snxtsct_dist(&ns,newx,newy,fort_max_interdiction_range);
	while (nxtsct(&ns,&fsect)){
		if (!notified[fsect.sct_own])
			continue;
		if (fsect.sct_type != SCT_FORTR)
			continue;
		gun=getvar(V_GUN,(s_char *)&fsect,EF_SECTOR);
		if (gun < 1)
			continue;
		range = tfact(fsect.sct_own,(double)min(gun,7));
		if (fsect.sct_effic > 59)
			range++;
		range2 = roundrange(range);
		trange = mapdist(newx, newy,
				 fsect.sct_x, fsect.sct_y);
		if (trange > range2)
			continue;
		if (getvar(V_MILIT,(s_char *)&fsect,EF_SECTOR)<5)
			continue;
		shell = getvar(V_SHELL,(s_char *)&fsect,EF_SECTOR);
		if (shell < 1)
			shell += supply_commod(fsect.sct_own,
					       fsect.sct_x,fsect.sct_y,I_SHELL,
					       1);
		if (shell < 1)
			continue;
		shell--;
		putvar(V_SHELL,shell,(s_char *)&fsect,EF_SECTOR);
		putsect(&fsect);
		if (gun > 7)
			gun = 7;
		guneff = landgun((int)fsect.sct_effic, gun);
		dam = (int)guneff;
		totdam += dam;
		mpr(victim, "%s fires at you for %d!\n",
		   xyas(fsect.sct_x,fsect.sct_y,victim),
		   dam);
		wu(0,fsect.sct_own,
		   "%s fires at %s ships in %s for %d!\n",
		   xyas(fsect.sct_x,fsect.sct_y,
			fsect.sct_own),
		   cname(victim),
		   xyas(newx,newy,fsect.sct_own), dam);
		nreport(fsect.sct_own, N_SHP_SHELL, victim, 1);
	}
	if (totdam > 0)
		return shp_damage(list, totdam, 0, M_SUB);
	return 0;
}

int
shp_interdict(list, newx, newy, victim)
	struct	qelem *list;
	coord	newx, newy;
	natid	victim;
{
	int stopping = 0;

	if (shp_contains(list, 0, M_SUB)) {
		stopping |= shp_fort_interdiction(list, newx, newy, victim);
		if (shp_contains(list, 0, M_SUB)) {
			stopping |= shp_damage(list, unit_interdict(newx,newy,victim,"ships", shp_easiest_target(list, 0, M_SUB), MI_INTERDICT), 0, M_SUB);
			if (most_valuable_ship(list)) {
				stopping |= shp_missile_interdiction(list,newx,newy,victim);
			}
		}
	}
	if (shp_contains(list, M_SUB, 0)) {
		stopping |= shp_damage(list, unit_interdict(newx, newy, victim, "subs", shp_easiest_target(list, M_SUB, 0), MI_SINTERDICT), M_SUB, 0);
	}
	return stopping;
}

/* high value of hardtarget is harder to hit */
int shp_hardtarget(sp)
	struct shpstr *sp;
{
	struct	sctstr	sect;
	int	vis, onsea;
	struct	mchrstr *mcp = mchr + sp->shp_type;

	vis = mcp->m_visib;
	getsect(sp->shp_x,sp->shp_y, &sect);
	onsea = (sect.sct_type == SCT_WATER);
	if (mcp->m_flags & M_SUB)
		vis *= 4;
	return (int)(((double)sp->shp_effic/100.0) *
		(20 + (double)mcp->m_speed * onsea/2.0 - vis));
}

shp_hit_mine(sp, mcp)
	struct shpstr *sp;
	struct	mchrstr *mcp;
{
	double	m;

	mpr(sp->shp_own, "Kawhomp! Mine detected in %s!\n",
		xyas(sp->shp_x, sp->shp_y, sp->shp_own));

	nreport(sp->shp_own, N_HIT_MINE, 0, 1);

	m = 22.0 + (double)(random() % 21);
	if (mcp->m_flags & M_SWEEP)
		m /= 2.0;

	shipdamage(sp, ldround(m,1));

	return (int)m;
}

shp_view(list)
	struct	qelem *list;
{
	struct	sctstr	sect;
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  mlist   *mlp;

	for (qp=list->q_back;qp!=list;qp=next) {
		next = qp->q_back;
		mlp = (struct mlist *)qp;
		getsect(mlp->ship.shp_x,mlp->ship.shp_y,&sect);
		if (mlp->mcp->m_flags & M_FOOD)
			mpr(mlp->ship.shp_own, "[fert:%d] ", sect.sct_fertil);
		if (mlp->mcp->m_flags & M_OIL)
			mpr(mlp->ship.shp_own, "[oil:%d] ", sect.sct_oil);
		mpr(mlp->ship.shp_own, "%s @ %s %d%% %s\n",
		    prship(&mlp->ship),
		    xyas(mlp->ship.shp_x, mlp->ship.shp_y, player->cnum),
		    sect.sct_effic,
		    dchr[sect.sct_type].d_name);
	}
}

int
shp_nav_one_sector(list, dir, actor, together)
	struct	qelem *list;
	int	dir;
	natid	actor;
	int	together;
{
	struct	sctstr	sect;
	struct  qelem   *qp;
	struct  qelem   *next;
	struct  mlist   *mlp;
	coord	dx;
	coord	dy;
	coord   newx;
	coord   newy;
	int	stopping = 0;
	double	mobcost;
	s_char	dp[80];

	if (dir <= DIR_STOP || dir >= DIR_VIEW) {
		shp_put(list, actor);
		return 1;
	}
	dx = diroff[dir][0];
	dy = diroff[dir][1];
	for (qp=list->q_back;qp!=list;qp=next) {
		next = qp->q_back;
		mlp = (struct mlist *)qp;
		newx = xnorm(mlp->ship.shp_x + dx);
		newy = ynorm(mlp->ship.shp_y + dy);
		getsect(newx, newy, &sect);
		if (shp_check_nav(&sect) != CN_NAVIGABLE ||
		    (sect.sct_own && actor != sect.sct_own &&
		     getrel(getnatp(sect.sct_own), actor) < FRIENDLY)) {
			if (together) {
				mpr(actor,"can't go to %s\n",xyas(newx, newy, actor));
				return 1;
			} else {
				sprintf(dp, "can't go to %s",
					xyas(newx, newy, actor));
				shp_mess(dp, mlp);
				continue;
			}
		}
		if (opt_BIG_CITY && sect.sct_type == SCT_CAPIT) {
			if (mlp->mcp->m_lcm + 2 * mlp->mcp->m_hcm >= 60) {
				sprintf(dp, "is too large to fit into the canal system at %s", xyas(newx, newy, actor));
				shp_mess(dp, mlp);
				continue;
			}
		}
		if (mlp->mobil <= 0.0) {
			shp_mess("is out of mobility", mlp);
			continue;
		}
		mobcost = mlp->ship.shp_effic * 0.01 * mlp->mcp->m_speed;
		mobcost = 480.0 /
			(mobcost + techfact(mlp->ship.shp_tech, mobcost));
		mlp->ship.shp_x = newx;
		mlp->ship.shp_y = newy;
		mlp->mobil -= mobcost;
		mlp->ship.shp_mobil = (int)mlp->mobil;
		putship(mlp->ship.shp_uid, &mlp->ship);
	}
	if (QEMPTY(list))
		return stopping;
	shp_sweep(list, 0, actor);
	stopping |= shp_check_mines(list);
	if (QEMPTY(list))
		return stopping;
	stopping |= shp_interdict(list, newx, newy, actor);
	return stopping;
}

/*
 * shp_miss_defence 
 * Check for incoming missiles with a P_MAR flag. 
 * Return True=1 if the missile was shotdown.
 * Or False=0
 * 
 * Chad Zabel, July 95
 */

int
shp_missile_defense(dx,dy,bombown,hardtarget)
	coord dx;
	coord dy;
	natid bombown;
	int   hardtarget;
{
	struct nstr_item ni;
	struct shpstr	ship;
	int		hitchance;
	int		mis_ship=0;
	int		vec[I_MAX+1];
	int		rel;
	double		gun,eff,teff;

	if (!snxtitem_dist(&ni,EF_SHIP,dx,dy,1))
		return 0;

	while (nxtitem(&ni,(caddr_t)&ship)) {
		if (!ship.shp_own)
			continue;

		if (!(mchr[ship.shp_type].m_flags & M_ANTIMISSILE))
			continue;

		if (getrel(getnatp(ship.shp_own), bombown) >= NEUTRAL)
			continue;

		if (ship.shp_effic < 60)
			continue;

		if (getvec(VT_ITEM,vec,(caddr_t)&ship,EF_SHIP) < 0)
			continue;
		if (vec[I_MILIT] < 1)   /* do we have mil? */
			continue;
		if (vec[I_SHELL] < 2) {     /* do we need shells */
			if (vec[I_SHELL] += supply_commod(ship.shp_own,
							  ship.shp_x,
							  ship.shp_y,
							  I_SHELL,2) < 2)
				continue;
		}
		if (vec[I_GUN] < 1)      /* we need at least 1 gun */
			continue;

		/* now calculate the odds */
		gun  =  ((double)min(vec[I_GUN],mchr[ship.shp_type].m_glim));
		eff  =  (double)ship.shp_effic / 100.0;
		teff =  (((double)ship.shp_tech)/(((double)ship.shp_tech)+200.0));
		/* raise 4.5 for better interception -KHS */
		hitchance = (int)(gun*eff*teff*4.5) - hardtarget;
		if (hitchance < 0)
			hitchance = 0;
		if (hitchance > 100)
			hitchance = 100;

		mpr(bombown,"%s anti-missile system activated...",cname(ship.shp_own));
		mpr(ship.shp_own,
		    "Ship #%i anti-missile system activated!\n",ship.shp_uid);
		mpr(ship.shp_own,
		    "%d%% hitchance...", hitchance);
		/* use ammo */
		putvar (V_SHELL, vec[I_SHELL]-2,(caddr_t)&ship, EF_SHIP);
		putship(ship.shp_uid,&ship);

		if (roll(100) <= hitchance) {
			mpr(bombown,"KABOOOM!! Missile destroyed\n\n");
			mpr(ship.shp_own,
			    "KABOOOM!!  Incoming missile destroyed!\n\n");
			return 1;
		} else {
			mpr(bombown, "SWOOSH!!  anti-missile system failed!!\n");
			mpr(ship.shp_own,
			    "SWOOSH!!  Missile evades anti-missile systems\n\n"); 
		}
	}
	return 0;   /* all attempts failed */
}

s_char *
shp_path(together, shp, buf)
	int	together;
	struct	shpstr	*shp;
	s_char	*buf;
{
	coord   destx;
	coord   desty;
	struct	sctstr d_sect;
	s_char	*cp;
	    
	if (!sarg_xy(buf, &destx, &desty))
		return 0;
	if (!together){
		mpr(shp->shp_own, "Cannot go to a destination sector if not all starting in the same sector\n");
		return 0;
	}
	if (!getsect(destx, desty, &d_sect)) {
		mpr(shp->shp_own, "%d,%d is not a sector\n", destx, desty);
		return 0;
	}
		
	cp = (s_char *)bestownedpath(buf, player->bmap, shp->shp_x, shp->shp_y, d_sect.sct_x, d_sect.sct_y, ".=h", player->cnum, 1);
	if (!cp) {
		mpr(shp->shp_own, "No all-sea path from %s to %s!\n",
		    xyas(shp->shp_x,shp->shp_y,player->cnum),
		    xyas(d_sect.sct_x,d_sect.sct_y,player->cnum));
		return 0;
	}
	mpr(shp->shp_own, "Using path '%s'\n",cp);
	return cp;
}

/* Fire missiles at a ship which has fired shells */
shp_missdef(sp, victim)
	struct	shpstr *sp;
	natid	victim;
{
	struct	qelem	list;
	struct	mlist	*mlp;
	int	eff;
	int	dam;
	s_char	buf[512];

	initque(&list);
	
	mlp = (struct mlist *) malloc(sizeof(struct mlist));
	mlp->mcp = &mchr[sp->shp_type];
	bcopy(sp, &mlp->ship, sizeof(struct shpstr));
	mlp->mobil = (double)sp->shp_mobil;
	insque(&mlp->queue, &list);
	sprintf(buf, "%s", prship(&mlp->ship));
		
	eff = sp->shp_effic;
	if (most_valuable_ship(&list)) {
		shp_missile_interdiction(&list, sp->shp_x, sp->shp_y, sp->shp_own);
		getship(sp->shp_uid, sp);

		if (!sp->shp_own) {
			wu(0, victim, "missiles launched in defense did 100%% damage to %s\n", buf);
			wu(0, victim, "%s sunk!\n", buf);
		} else if (eff > 0 && sp->shp_effic < eff) {
			wu(0, victim, "missiles launched in defense did %d%% damage to %s\n", 100*(eff - sp->shp_effic)/eff, buf);
		}
	}
	if (!QEMPTY(&list))
		free(mlp);
}
