#ifndef lint
static char *RCSid = "$Header: /users/empire/EMP/empmain/SUBS/RCS/ship.c,v 1.2 89/06/27 01:33:49 griffith Exp $";
#endif

/*
 * shipsub.c
 *
 * ship subroutines
 *
 * Dave Pare, 1989
 */

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

#ifdef RETREAT

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)

struct	ccode{
	char	code;
	char	*desc[2];
} conditions[] = {
	{ 'i', {
		"retreated with a damaged ship",
		"was damaged",
		}, },
	{ 't', {
		"retreated with a torpedoed ship",
		"was hit by a torpedo",
		}, },
	{ 's', {
		"retreated with a ship scared by sonar",
		"detected a sonar ping",
		}, },
	{ 'h', {
		"retreated with a helpless ship",
		"was fired upon with no one able to defend it",
		}, },
	{ 'b', {
		"retreated with a bombed ship",
		"was bombed",
		}, },
	{ 'd', {
		"retreated with a depth-charged ship",
		"was depth-charged",
		}, },
	{ 0, {"",""} },
};

int
check_retreat_and_do_shipdamage(sp, dam)
	struct shpstr *sp;
	int dam;
{
	if (dam <=0)
		return;

	shipdamage(sp,dam);
	if (sp->shp_rflags & RET_INJURED)
		retreat(sp, 'i');

}

int
retreat(sp, code)
	struct shpstr *sp;
	char	code;
{
	struct	nstr_item ni;
	struct	shpstr ship;
	char	buf[2];

	if (sp->shp_rflags & RET_FLEET){
		bzero(buf,2);
		buf[0] = sp->shp_fleet;
		snxtitem(&ni, EF_SHIP, buf);
		while(nxtitem(&ni,(char *)&ship))
			if ((ship.shp_fleet == buf[0]) &&
				(ship.shp_own == sp->shp_own))
				if (ship.shp_uid == sp->shp_uid){
					retreat1(sp,code,1);
					if (sp->shp_rpath[0] == 0)
						sp->shp_rflags = 0;
				}else{
					retreat1(&ship,code,0);
					getship(ship.shp_uid,&ship);
					if (ship.shp_rpath[0] == 0){
						ship.shp_rflags = 0;
						putship(ship.shp_uid,&ship);
					}
				}
	}else{
		retreat1(sp,code,1);
		if (sp->shp_rpath[0] == 0)
			sp->shp_rflags = 0;
	}
}

int
retreat1(sp, code, orig)
	struct shpstr *sp;
	char	code;
	int	orig;	/* Is this the originally scared ship, or a follower */
{
	extern double techfact();
	extern char *argp[];
	struct sctstr sect;
	register n;
	register m;
	int	max;
	int     dir;
	int     nships;
	coord     newx;
	coord     newy;
	coord     dx;
	coord     dy;
	int     stopping;
	int     viewing;
	int     mines;
	int     shells;
	double  mobcost;
	double  minmob;
	struct nstr_item nb;
	struct mchrstr *mcp;
	int	vec[I_MAX+1];
	int	time_to_stop;
	char	buf[RET_LEN-1];

	if (sp->shp_own == 0)
		return 0;

	if (isupper(code))
		code = tolower(code);

	n = 0;
	if (sp->shp_effic < 20){
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut it died in the attack, and so couldn't retreat!\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s,\nbut it died in the attack, and so couldn't retreat!\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid, conditions[findcondition(code)].desc[orig]));
		if (!orig) putship(sp->shp_uid,sp);
		return 0;
	}

#ifdef	SAIL
	/* can't retreat a ship that's sailin, bad things happend */
	if (*sp->shp_path){
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut had sailing orders, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s,\nbut had sailing orders, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid, conditions[findcondition(code)].desc[orig]));
		if (!orig) putship(sp->shp_uid,sp);
		return 0;
	}
#endif	SAIL
	minmob = (double) sp->shp_mobil;
	/* check crew - uws don't count */
	getvec(VT_ITEM, vec, (char *)sp, EF_SHIP);
	if (vec[I_MILIT] == 0 && vec[I_CIVIL] == 0){
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut had no crew, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s,\nbut had no crew, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid, conditions[findcondition(code)].desc[orig]));
		if (!orig) putship(sp->shp_uid,sp);
		return 0;
	}

	getsect(sp->shp_x,sp->shp_y,&sect);
	switch (check_nav(&sect)) {
		case CN_CONSTRUCTION:
#ifdef SHIPNAMES
			wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut was caught in a construction zone, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name, sp->shp_name,
#else
			wu (0, sp->shp_own, fmt("%s #%d %s,\nbut was caught in a construction zone, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
				sp->shp_uid,
				conditions[findcondition(code)].desc[orig]));
			if (!orig) putship(sp->shp_uid,sp);
			return 0;
		case CN_LANDLOCKED:
#ifdef SHIPNAMES
			wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut was landlocked, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name, sp->shp_name,
#else
			wu (0, sp->shp_own, fmt("%s #%d %s,\nbut was landlocked, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
				sp->shp_uid,
				conditions[findcondition(code)].desc[orig]));
			if (!orig) putship(sp->shp_uid,sp);
			return 0;
			/*NOTREACHED*/
		case CN_NAVIGABLE:
			break;
		case CN_ERROR:
		default:
#ifdef SHIPNAMES
			wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut was subject to an empire error, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name, sp->shp_name,
#else
			wu (0, sp->shp_own, fmt("%s #%d %s,\nbut was subject to an empire error, and couldn't retreat!\n",
				mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
				sp->shp_uid,
				conditions[findcondition(code)].desc[orig]));
			if (!orig) putship(sp->shp_uid,sp);
			return 0;
			/*NOTREACHED*/
	}

	if (sp->shp_mobil <= 0.0) {
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut had no mobility, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s,\nbut had no mobility, and couldn't retreat!\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid,
			conditions[findcondition(code)].desc[orig]));
		if (!orig) putship(sp->shp_uid,sp);
		return 0;
	}

	n=(-1*MAX_RETREAT);
	stopping = 0;
	time_to_stop = 0;
	while ((!stopping) && n){
		dx = dy = viewing = 0;
		if (sp->shp_rpath[0] == 0 || sp->shp_rpath[0] == 0){
			stopping=1;
			continue;
		}
		if (sp->shp_mobil <= 0.0) {
#ifdef SHIPNAMES
			wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
				mchr[sp->shp_type].m_name, sp->shp_name,
#else
			wu (0, sp->shp_own, fmt("%s #%d %s,\nbut ran out of mobility, and couldn't retreat fully!\n",
				mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
				sp->shp_uid,
				conditions[findcondition(code)].desc[orig]));
			if (!orig) putship(sp->shp_uid,sp);
			return 0;
		}
		dir = chkdir(sp->shp_rpath[0], DIR_STOP, DIR_VIEW);
		bzero(buf,RET_LEN-1);
		bcopy(&sp->shp_rpath[1],buf,RET_LEN-1);
		bzero(sp->shp_rpath,RET_LEN);
		bcopy(buf,sp->shp_rpath,RET_LEN-1);
		if (dir == -1)
			continue;
		if (dir == DIR_STOP)
			stopping++;
		else {
			dx = diroff[dir][0];
			dy = diroff[dir][1];
		}
		n++;

		mcp = &mchr[sp->shp_type];
		newx = xnorm(sp->shp_x + dx);
		newy = ynorm(sp->shp_y + dy);
		mobcost = sp->shp_effic * 0.01 * mcp->m_speed;
		mobcost = 480.0 / (mobcost + techfact(sp->shp_tech, mobcost));

		getsect(newx, newy, &sect);
		if (check_nav(&sect) != CN_NAVIGABLE ||
			    (sect.sct_own && !owner &&
			     getrel(getnatp(sect.sct_own), sp->shp_own) != ALLIED)) {
#ifdef SHIPNAMES
				wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nbut could not retreat to %s!\n",
					mchr[sp->shp_type].m_name,
					sp->shp_name,
#else
				wu (0, sp->shp_own, fmt("%s #%d %s,\nbut could not retreat to %s!\n",
					mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
					sp->shp_uid,
					conditions[findcondition(code)].desc[orig],
					xyas(newx, newy, sp->shp_own)));
				if (!orig) putship(sp->shp_uid,sp);
				return 0;
		}
		sp->shp_x = newx;
		sp->shp_y = newy;
		sp->shp_mobil -= mobcost;
		if (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 *)sp, EF_SHIP);
			for (m=0; mines > 0 && m < 5; m++) {
				if (chance(0.66)) {
					mines--;
					shells = min(max, shells + 1);
				}
			}
			putvar(V_MINE, mines, (char *)&sect, EF_SECTOR);
			putvar(V_SHELL, shells, (char *)sp, EF_SHIP);
			putsect(&sect);
		}
		if (mines > 0 && !owner && chance(0.05 * mines)) {
#ifdef SHIPNAMES
			wu (0, sp->shp_own, fmt("%s %s (#%d) %s,\nand hit a mine in %s while retreating!\n",
				mchr[sp->shp_type].m_name, sp->shp_name,
#else
			wu (0, sp->shp_own, fmt("%s #%d %s,\nand hit a mine while retreating!\n",
				mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
				sp->shp_uid,
				conditions[findcondition(code)].desc[orig],
				xyas(newx, newy, sp->shp_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 *)&sect, EF_SECTOR);
			putsect(&sect);
			if (sp->shp_effic < 20)
				time_to_stop = 1;
			if (!orig) putship(sp->shp_uid,sp);
			return 0;
		}
		if (time_to_stop)
			stopping = 1;
	}

	if (orig){
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s, and retreated to %s\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s, and retreated to %s\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid,
			conditions[findcondition(code)].desc[orig],
			xyas(sp->shp_x, sp->shp_y, sp->shp_own)));
	}else{
#ifdef SHIPNAMES
		wu (0, sp->shp_own, fmt("%s %s (#%d) %s, and ended up at %s\n",
			mchr[sp->shp_type].m_name, sp->shp_name,
#else
		wu (0, sp->shp_own, fmt("%s #%d %s, and ended up at %s\n",
			mchr[sp->shp_type].m_name,
#endif /* SHIPNAMES */
			sp->shp_uid,
			conditions[findcondition(code)].desc[orig],
			xyas(sp->shp_x, sp->shp_y, sp->shp_own)));
	}
	if (!orig) putship(sp->shp_uid,sp);
	return 1;
}

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

int
findcondition(code)
	char	code;
{
	int	x;

	x=0;
	while (conditions[x].code){
		if (conditions[x].code == code)
			return(x);
		x++;
	}

	return(x);
}
#endif /* RETREAT */
