#ifndef lint
static char *RCSid = "$Header: laun.c,v 1.9 89/12/01 20:46:19 mr-frog Exp $";
#endif /* not lint */

/*
 * laun.c
 *
 * Launch missiles from land or sea.
 *
 * Dave Pare, 1986
 */

#include "misc.h"
#include "xy.h"
#include "var.h"
#include "sect.h"
#include "item.h"
#include "plane.h"
#include "nuke.h"
#include "news.h"
#include "ship.h"
#include "nsc.h"
#include "nat.h"
#include "path.h"
#include "file.h"


/*
 * These are needed by the various launch routines below.
 */
static	struct	plnstr plane;	/* current missile we're lauinching */
static	struct	plchrstr *pcp;	/* characteristics of this missile */
static	int	shiplaunch;	/* launched from a ship? */
static	struct	sctstr sect;	/* sector from which we're launching */
static	struct	shpstr ship;	/* ship from which we're launching */

static	int launch_nuke();
static	int launch_sat();
static	int launch_conv();
extern  char *argp[];

/*
 * laun <PLANES>
 */
laun()
{
	extern	char *argp[];
	struct	nstr_item nstr;
	int	n;

	if (!snxtitem(&nstr, EF_PLANE, argp[1]))
		return RET_SYN;
	while (nxtitem(&nstr, (char *)&plane)) {
		if (plane.pln_own != cnum)
			continue;
		pcp = &plchr[plane.pln_type];
		if ((pcp->pl_flags & (P_M|P_O)) == 0) {
			pr(fmt("%s #%d isn't a missile!\n", pcp->pl_name,
				plane.pln_uid));
			continue;
		}
 		if (pcp->pl_flags & P_F) {
 			pr(fmt("%s #%d is a surface-to-air missile!\n",
 				pcp->pl_name, plane.pln_uid));
 			continue;
 		}
#ifdef	ABM
		if (pcp->pl_flags & P_N) {
			pr(fmt("%s #%d is an anti-ballistic-missile missile!\n",
				pcp->pl_name, plane.pln_uid));
			continue;
		}
#endif	ABM
		if ((plane.pln_flags & PLN_LAUNCHED) && (pcp->pl_flags & P_O)) {
			pr(fmt("%s #%d already in orbit!\n",
				pcp->pl_name, plane.pln_uid));
			continue;
		}
		if (plane.pln_ship >= 0) {
			getship(plane.pln_ship, &ship);
			shiplaunch = 1;
			if (ship.shp_own != cnum){
				continue;
			}
			plane.pln_x = ship.shp_x;
			plane.pln_y = ship.shp_y;
		} else {
			shiplaunch = 0;
			getsect(plane.pln_x, plane.pln_y, &sect);
			if (sect.sct_own && sect.sct_own != cnum) {
				pr(fmt("%s #%d: someone else owns %s!\n",
					pcp->pl_name, plane.pln_uid,
					xyas(plane.pln_x, plane.pln_y, cnum)));
				continue;
			}
		}
		if (plane.pln_effic < 60) {
			pr(fmt("%s #%d is damaged (%d%%)\n",
				pcp->pl_name, plane.pln_uid, plane.pln_effic));
			continue;
		}
#ifndef	CONVASAT
		if (plane.pln_nukeamt == 0 &&
		    (pcp->pl_flags & (P_M|P_O)) == (P_M|P_O)) {
			pr(fmt("%s #%d isn't armed with a nuclear device!\n",
				pcp->pl_name, plane.pln_uid));
			continue;
		}
#endif	CONVASAT
		pr(fmt("%s #%d at %s; range %d, eff %d%%", pcp->pl_name,
			plane.pln_uid,
			xyas(plane.pln_x, plane.pln_y, cnum),
			plane.pln_range,
			plane.pln_effic));
		n = plane.pln_nukeamt;
		if (!(pcp->pl_flags & P_O) && (n == 0)) { /* conventional */
			if (launch_conv() < 0)
				continue;
#ifdef	CONVASAT
		} else if ((pcp->pl_flags & (P_M|P_O)) == (P_M|P_O) && (n==0)) {
			if (launch_conv_as() < 0) /* conventional anti-sat */
				continue;
#endif	CONVASAT
		} else if (n == 0) {			/* satellites */
			if (launch_sat() < 0)
				continue;
		} else {				/* nukes */
			if (launch_nuke(n) < 0)
				continue;
		}
		putplane(plane.pln_uid, &plane);
		if (shiplaunch) {
			ship.shp_nplane -= 1;
			if (ship.shp_nplane < 0)	/* shouldn't happen */
				ship.shp_nplane = 0;
			putship(plane.pln_ship, &ship);
		}
	}
	return RET_OK;
}

#ifdef	CONVASAT
/*
 * Launch a conventional anti-sat weapon.
 * Return -1 on failure, 0 on success (even if missile explodes).
 */

static int
launch_conv_as()
{
	coord	sx, sy;
	int	dam;
	int	shell;
	int	bombs;
	int	needed;
	int	i;
	char	*cp,*qp;
	struct	nstr_item ni;
	float	tech, eff, acc;
#ifdef SLOW_WAR
	struct	nstr_item ni2;
	int	rel, goodtarget;
	struct natstr	*natp;
	struct	plnstr plane2;
#endif /* SLOW_WAR */

	pr(", carrying conventional warhead\n");
	if (shiplaunch) {
		shell = getvar(V_SHELL, (char *)&ship, EF_SHIP);
	} else {
		shell = getvar(V_SHELL, (char *)&sect, EF_SECTOR);
	}
	needed = pcp->pl_load - 1;
	if (shell < needed) {
		pr(fmt("(#%d) %s not enough shells!\n",
			plane.pln_uid, pcp->pl_name));
		return -1;
	}
	bombs = pcp->pl_load;
	if (shiplaunch) {
		putvar(V_SHELL, shell - needed, (char *)&ship, EF_SHIP);
	} else {
		putvar(V_SHELL, shell - needed, (char *)&sect, EF_SECTOR);
	}
	putsect(&sect);
	qp = argp[2];
	for (;;) {
		cp = getstarg(qp, "Target sector? ");
		if (argp[2])
			*argp[2] = 0;
		if (cp == 0)
			return (-1);
		if (!sarg_xy(cp, &sx, &sy)) {
			pr("Bad sector designation; try again!\n");
			qp=(char *)0;
			continue;
		}
		if (mapdist(plane.pln_x, plane.pln_y, sx, sy) >
		    plane.pln_range) {
			pr("Range too great; try again!\n");
			qp=(char *)0;
			continue;
		}
		break;
	}
#ifdef SLOW_WAR
	goodtarget=0;
	snxtitem_dist(&ni2, EF_PLANE, sx, sy, 0);
	while (nxtitem(&ni2, (caddr_t)&plane2)) {
		if (plane2.pln_own == 0)
			continue;
		if ((plane2.pln_flags&PLN_LAUNCHED) == 0)
			continue;
		natp = getnatp(cnum);
		rel = getrel(natp,plane2.pln_own);
		if ((rel != AT_WAR) && (plane2.pln_own))
			continue;
		goodtarget = 1;
		
	}
	if (goodtarget == 0){
		pr("No enemy satellites there!\n");
		return -1;
	}
#endif /* SLOW_WAR */

	pr("Launching!\n");
	if (chance(0.07 + (100 - plane.pln_effic)/100.0)) {
		pr("KABOOOOM!  Anti-Sat explodes on launch pad!\n");
		plane.pln_own = 0;
		return 0;
	}
	pr("Anti-Sat approaching hostile satellite....");
	tech = plane.pln_tech;
	eff = plane.pln_effic;
	acc = pcp->pl_acc;
	plane.pln_own = 0;
	putplane(plane.pln_uid, &plane);
	if (chance(1/(((1.5 * tech + eff)/(acc + (.44 * acc))) * ((10 * (acc - 30) / (tech - 40)))))) {
		pr("HIT!\n");
		snxtitem_dist(&ni, EF_PLANE, sx, sy, 0);
       		while (nxtitem(&ni, (caddr_t)&plane)) {
			if (plane.pln_own == 0)
				continue;
			if ((plane.pln_flags&PLN_LAUNCHED) == 0)
				continue;
#ifdef SLOW_WAR
			natp = getnatp(cnum);
			rel = getrel(natp,plane.pln_own);
			if ((rel != AT_WAR) && (plane.pln_own))
				continue;
#endif /* SLOW_WAR */
			nreport(cnum, N_SAT_KILL, plane.pln_own, 1);
			dam = 100;
			planedamage(&plane, dam);
			if (cnum == plane.pln_own) {
				 pr(fmt("%s #%d over %s reports %d%% damage\n",
					plchr[plane.pln_type].pl_name, ni.cur,
					xyas(plane.pln_x, plane.pln_y, plane.pln_own),
					dam));
			} else {
#ifdef MERC
				if(plane.pln_own != 0)
#endif
				wu(0, plane.pln_own,
				fmt("%s anti-sat did %d%% damage to %s #%d over %s",
					cname(cnum), dam,
					plchr[plane.pln_type].pl_name, ni.cur,
				xyas(plane.pln_x, plane.pln_y, plane.pln_own)));
                	}
			putplane(ni.cur, &plane);
		}
		return(0);
	}
	else {
		pr("Missed\n");
		return(0);
	}
}
#endif	CONVASAT

/*
 * Launch a conventional warhead missile.
 * Return -1 on failure, 0 on success (even if missile explodes).
 */
static int
launch_conv()
{
	coord	sx, sy;
	float	damage;
	int	dam;
	int	shell;
	int	bombs;
	int	needed;
	int	i;
	char	*cp,*qp;
#ifdef	PINPOINTMISSILE
	int	shipno;
	struct	mchrstr *mcp;
	int	onsea;
	int	acc;
	int	vis;
	int	n;
	struct	shpstr	target_ship;
#endif	PINPOINTMISSILE
#ifdef SLOW_WAR
	int	rel;
	struct natstr	*natp;
#endif /* SLOW_WAR */

	pr(", carrying conventional warhead\n");
	if (shiplaunch) {
		shell = getvar(V_SHELL, (char *)&ship, EF_SHIP);
	} else {
		shell = getvar(V_SHELL, (char *)&sect, EF_SECTOR);
	}
	needed = pcp->pl_load - 1;
	if (shell < needed) {
		pr(fmt("(#%d) %s not enough shells!\n",
			plane.pln_uid, pcp->pl_name));
		return -1;
	}
	bombs = pcp->pl_load;
	if (shiplaunch) {
		putvar(V_SHELL, shell - needed, (char *)&ship, EF_SHIP);
	} else {
		putvar(V_SHELL, shell - needed, (char *)&sect, EF_SECTOR);
	}
	qp=argp[2];
	for (;;) {
		cp = getstarg(qp, "Target sector? ");
		if (cp == 0)
			return (-1);
#ifdef	PINPOINTMISSILE
		if (sarg_type(cp) == NS_LIST) {
			if (!(pcp->pl_flags & P_T)) {
				qp=(char *)0;
				pr("Missile not designed to attack ships!\n");
				continue;
			}
			n = atoi(cp);
			if ((n < 0) || !getship(n,&target_ship) ||
			    !target_ship.shp_own) {
				pr("Bad ship number; try again!\n");
				qp=(char *)0;
				continue;
			}
			sx = target_ship.shp_x;
			sy = target_ship.shp_y;
			shipno = n;
		}
		else
#endif	PINPOINTMISSILE
		if (!sarg_xy(cp, &sx, &sy)) {
			pr("Bad sector designation; try again!\n");
			qp=(char *)0;
			continue;
		}
#ifdef	PINPOINTMISSILE
		else
		{
			if (pcp->pl_flags & P_T) {
				pr("Missile designed to attack ships!\n");
				qp=(char *)0;
				continue;
			}
		}
#endif	PINPOINTMISSILE
		if (mapdist(plane.pln_x, plane.pln_y, sx, sy) >
		    plane.pln_range) {
			pr("Range too great; try again!\n");
			qp=(char *)0;
			continue;
		}
		break;
	}

	pr("Launching!\n");
	if (chance(0.02 + (100 - plane.pln_effic)/100.0)) {
		pr("KABOOOOM!  Missile explodes on launch pad!\n");
		plane.pln_own = 0;
		return 0;
	}

	plane.pln_own = 0;
#ifdef	PINPOINTMISSILE
	if (!(pcp->pl_flags & P_T))
	{
#endif	PINPOINTMISSILE
#ifdef	ABM
		/*
		 * rv_intercept attempts to perform abm interception of
		 * incoming warhead.  Returns 0 if failed interception.
		 *
		 * Alex Shatsky (lex@ucscb.ucsc.edu)
		 */
#ifdef SLOW_WAR
		if (!(rv_intercept(sx, sy, cnum))){
			getsect(sx,sy,&sect);
			natp = getnatp(cnum);
			rel = getrel(natp,sect.sct_own);
			if ((rel != AT_WAR) && (sect.sct_own != cnum) && (sect.sct_own) && (sect.sct_oldown != cnum)){
				pr("You are not at war with the owner of the target sector!\n");
				pr("Kaboom!!!\07\n");
				pr("Missile monitoring officer destroys RV before detonation.\n");
				return(0);
			}
		}else{
			pr("Missile destroyed!\n");
			return RET_OK;
		}
#else
		if (!rv_intercept(sx, sy, cnum)){
		}else{
			pr("Missile destroyed!\n");
			return RET_OK;
		}
#endif /* SLOW_WAR */
#else
#ifdef SLOW_WAR
		getsect(sx,sy,&sect);
		natp = getnatp(cnum);
		rel = getrel(natp,sect.sct_own);
		if ((rel != AT_WAR) && (sect.sct_own != cnum) && (sect.sct_own) && (sect.sct_oldown != cnum)){
			pr("You are not at war with the owner of the target sector!\n");
			pr("Kaboom!!!\07\n");
			pr("Missile monitoring officer destroys RV before detonation.\n");
			return(0);
		}
#endif /* SLOW_WAR */
#endif	ABM
		dam = 100;
		i = roll(100);
		if (i <= 10) {
			damage = 0.92;	/* 8% */
			pr("BLAM");
		} else if (i < 100 - pcp->pl_acc) {	/* small pl_acc is better */
			damage = 0.95;	/* 5% */
			pr("Blam");
		} else {
			damage = 0.98;	/* 2% */
			pr("blam");
		}
		pr("\n");
		for (i = 0; i < bombs; i++)
			dam = dam * damage;
		getsect(sx, sy, &sect);
		sectdamage(&sect, 100 - dam);
		putsect(&sect);
		nreport(cnum, N_SCT_MISS, sect.sct_own, 1);
		pr(fmt("did %d%% damage in %s\n", 100 - dam, xyas(sx, sy, cnum)));
#ifdef MERC
		if(sect.sct_own != 0)
#endif
		wu(0, sect.sct_own,
			fmt("%s missile attack did %d%% damage in %s",
			cname(cnum), 100 - dam, xyas(sx, sy, sect.sct_own)));
#ifdef	PINPOINTMISSILE
	}
	else
	{
		getsect(sx,sy,&sect);
		onsea = (sect.sct_type == SCT_WATER) ? 1 : 0;
		mcp = &mchr[target_ship.shp_type];
		/*
		 * subs in port are sitting ducks, while subs
		 * at sea are impossible to spot.
		 */
		vis = mcp->m_visib;
		if (mcp->m_flags & M_SUB) {
			if (onsea)
				return -1;
			vis = 20;
		}
		/*
		 * ships on high seas are harder to hit because they're
		 * moving around so much!
		 */
		acc = pcp->pl_acc - vis + (mcp->m_speed * onsea) / 2;
		dam = 0;
		if (roll(100) > acc) {
			i = bombs;
			while (i--) {
				pr("BLAM");
				if (i)
					pr("-");
				dam += ((roll(8)+8) * 63) / mcp->m_armor;
			}
			pr("\n");
		} else {
			pr("splash\n");
		}
		plane.pln_own = 0;
		nreport(cnum, N_SHP_MISS, target_ship.shp_own, 1);
		if (dam > 100)
			dam = 100;
		check_retreat_and_do_shipdamage(&target_ship, dam);
#ifdef MERC
		if(target_ship.shp_own != 0)
#endif
#ifdef	SHIPNAMES
		wu(0, target_ship.shp_own, fmt("%s missile did %d%% damage to %s %s(#%d) at %s",
			cname(cnum), dam, mcp->m_name, target_ship.shp_name,
			target_ship.shp_uid,
#else
		wu(0, target_ship.shp_own, fmt("%s missile did %d%% damage to %s #%d at %s",
			cname(cnum), dam, mcp->m_name, target_ship.shp_uid,
#endif	SHIPNAMES
			xyas(target_ship.shp_x, target_ship.shp_y,
			    target_ship.shp_own)));
		pr("\n");
		putship(n, &target_ship);
	};
#endif	PINPOINTMISSILE
	return 0;
}

/*
 * Launch a satellite.
 * Return -1 on error, 0 on success (even if the satellite fails).
 */
static int
launch_sat()
{
	coord	sx, sy;
	int	i;
	int	dist;
	int	dir;
	char	*cp;
#ifdef	ORBIT
	char	*p;
#endif	ORBIT

	pr("\n");
	while (1) {
		cp = getstring("Target sector? ");
		if (cp == 0)
			return -1;
		if (!sarg_xy(cp, &sx, &sy)) {
			pr("Bad sector designation; try again!\n");
			continue;
		}
		if ((dist = mapdist(plane.pln_x, plane.pln_y, sx, sy)) >
		    plane.pln_range) {
			pr("Range too great; try again!\n");
			continue;
		}
		break;
	}
#ifdef	ORBIT
	p = getstring("Geosynchronous orbit? ");
	if (p == 0)
		return -1;
	plane.pln_theta = 0;
	plane.pln_flags |= PLN_SYNCHRONOUS;
	if (*p == 0 || *p == 'n')
		plane.pln_flags &= ~(PLN_SYNCHRONOUS);
#endif	ORBIT
	pr("3... 2... 1... Blastoff!!!\n");
	if (chance(0.07 + (100 - plane.pln_effic)/100.0)) {
		pr(fmt("KABOOOOM!  Range safety officer detonates booster!\n"));
		plane.pln_own = 0;
		return 0;
	}
	i = plane.pln_tech + plane.pln_effic;
	if (chance(1.0 - (i/(i+50.0)))) {
		dir = (random() % 6) + 1;
		sx += diroff[dir][0];
		sy += diroff[dir][1];
		pr("Your trajectory was a little off.\n");
	}
	nreport(cnum, N_LAUNCH, 0, 1);
	pr(fmt("%s #%d positioned over %s, ",
		pcp->pl_name, plane.pln_uid, xyas(sx, sy, cnum)));
	plane.pln_x = sx;
	plane.pln_y = sy;
	plane.pln_flags |= PLN_LAUNCHED;
	plane.pln_mobil = (plane.pln_mobil > dist) ? 
		(plane.pln_mobil - dist) : 0;
	pr(fmt("will be ready for use in %d time units\n",
		127 - plane.pln_mobil));
	return 0;
}

#define	MAXRV	30

struct target {
	int x, y;
	int airburst;
};

/*
 * Launch a nuke.
 * Return -1 on error, 0 on success (even if nuke fails).
 */
static int
launch_nuke(n)
	int	n;
{
	coord	sx, sy;
	struct	target xy[MAXRV];
	char	*p;
	int	i;
	int	dir;
	extern int miss_inacc_thresh;
	extern int miss_miss_chance;
#ifdef SLOW_WAR
	int	rel;
	struct natstr	*natp;
#endif /* SLOW_WAR */
#ifdef DAVYJONES
	struct sctstr sect;
#endif /* DAVYJONES */

	pr(fmt(", payload %dx%s\n", plane.pln_nukeamt,
			nchr[plane.pln_nuketype].n_name));
	if (n > MAXRV)
		n = MAXRV;
	for (i = 0; i < n;) {
		p = getstring(fmt("RV #%d, target sector? ", i+1));
		if (p == 0)
			return (-1);
		if (!sarg_xy(p, &sx, &sy)) {
			pr("Bad sector designation; try again!\n");
			continue;
		}
		if (mapdist(plane.pln_x, plane.pln_y, sx, sy) >
		    plane.pln_range) {
			pr("Range too great; try again!\n");
			continue;
		}
#ifndef	CONVASAT
		if (pcp->pl_flags & P_O) {
			dir = plane.pln_tech + plane.pln_effic;
			if (chance(1.0 - dir/(dir+50.0)))
				dir = (random() % 6) + 1;
			else
				dir = 0;
			xy[i].x = sx + diroff[dir][0];
			xy[i].y = sy + diroff[dir][1];
			xy[i].airburst = 2;

		} else {
#endif	CONVASAT
			p = getstring("Air-burst? ");
			if (p == 0)
				return -1;
			xy[i].x = sx;
			xy[i].y = sy;
			xy[i].airburst = 1;
			if (*p == 0 || *p == 'n')
				xy[i].airburst = 0;
#ifndef	CONVASAT
		}
#endif	CONVASAT
		i++;
	}

	plane.pln_own = 0;
	plane.pln_nukeamt = 0;
	pr("Launching!\n");
	if (chance(0.02 + (100 - plane.pln_effic)/100.0)) {
		pr(fmt("KABOOOOM!  Missile explodes on launch pad!\n"));
#ifdef	NUKEFAILDETONATE
		for (i = 0; i < n; i++) {
			if (chance(0.33)) {
				detonate(plane.pln_nuketype, plane.pln_x,
					plane.pln_y, 0, cnum);
			}
		}
#endif	NUKEFAILDETONATE
		return 0;
	}
	pr("Arming warheads...\n");
	pr("Releasing RV's...\n");
	for (i = 0; i < n; i++) {
#ifdef	MISSINGMISSILES
	    {
		int	dist;

		dist = mapdist(plane.pln_x, plane.pln_y, xy[i].x, xy[i].y);

#ifndef BABY_NUKES
		if (dist > miss_inacc_thresh)
		{
			if	(chance((((dist - miss_inacc_thresh) /
					(plane.pln_range - miss_inacc_thresh)) *
					miss_miss_chance) / 100.0))
			{
				dir = (random() % 6) + 1;
				xy[i].x += diroff[dir][0];
				xy[i].y += diroff[dir][1];
			};
		};
#endif /* BABY_NUKES */
	    }
#endif	MISSINGMISSILES
#ifdef DAVYJONES
		getsect(xy[i].x,xy[i].y,&sect);
		if (sect.sct_type == SCT_WASTE){
			pr(fmt("Nuclear device landed in a wasteland at %s, and failed to detonate.\n",xyas(xy[i].x, xy[i].y, cnum)));
			continue;
		}
		if (sect.sct_type == SCT_SANCT){
			pr(fmt("Nuclear device landed on a sanctuary at %s, and failed to detonate.\n",xyas(xy[i].x, xy[i].y, cnum)));
			continue;
		}
		/* Code in detonate will handle nukes hitting water ---ts */
#endif /* DAVYJONES */
#ifdef	ABM
		/*
		 * rv_intercept attempts to perform abm interception of
		 * incoming warhead.  Returns 0 if failed interception.
		 *
		 * Alex Shatsky (lex@ucscb.ucsc.edu)
		 */
#ifdef SLOW_WAR
		if (!(rv_intercept(xy[i].x, xy[i].y, cnum))){
			getsect(sx,sy,&sect);
			natp = getnatp(cnum);
			rel = getrel(natp,sect.sct_own);
			if ((rel != AT_WAR) && (sect.sct_own != cnum) && (sect.sct_own) && (sect.sct_oldown != cnum)){
				pr("You are not at war with the owner of the target sector!\n");
				pr("Kaboom!!!\07\n");
				pr("Missile monitoring officer destroys RV before detonation.\n");
				return(0);
			}
#else
		if (!(rv_intercept(xy[i].x, xy[i].y, cnum)))
#endif /* SLOW_WAR */
			detonate(plane.pln_nuketype, xy[i].x, xy[i].y,
				xy[i].airburst, cnum);
#ifdef SLOW_WAR
		}
#endif /* SLOW_WAR */
#else
#ifdef SLOW_WAR
		getsect(sx,sy,&sect);
		natp = getnatp(cnum);
		rel = getrel(natp,sect.sct_own);
		if ((rel != AT_WAR) && (sect.sct_own != cnum) && (sect.sct_own) && (sect.sct_oldown != cnum)){
			pr("You are not at war with the owner of the target sector!\n");
			pr("Kaboom!!!\07\n");
			pr("Missile monitoring officer destroys RV before detonation.\n");
			return(0);
		}
#endif /* SLOW_WAR */
		detonate(plane.pln_nuketype, xy[i].x, xy[i].y,
			xy[i].airburst, cnum);
#endif	ABM
	}
	return 0;
}
