#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	aircombat.c
#	mission.c
# This archive created: Wed May 11 22:58:24 1994
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'aircombat.c'
then
	echo shar: "will not over-write existing file 'aircombat.c'"
else
cat << \SHAR_EOF > 'aircombat.c'
#ifndef lint
static char *RCSid = "$Header: aircombat.c,v 1.1 89/12/14 13:28:06 jay Exp $";
#endif

/*
 * aircombat.c
 *
 * Deal with air to air combat
 *
 * Dave Pare, 1986
 * --ts 1992
 */

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

static	getilist();
s_char *ac_planedamage();

#define	DOG_MAX		15
#define FLAK_MAX	15

		/*       -7    -6    -5    -4    -3    -2    -1    0 */
float flaktable[16] = {	0.20, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
			0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, };

ac_encounter(bomb_list, esc_list, x, y, path, mission_flags, no_air_defense)
	struct	qelem *bomb_list;
	struct	qelem *esc_list;
	coord	x;
	coord	y;
	s_char	*path;
	int	mission_flags;
	int	no_air_defense;
{
	extern	int ismission;
	int	val, non_missiles;
	int	rel;
	int	dir;
	int	nats[MAXNOC];
	int	lnats[MAXNOC];
	int	gotilist[MAXNOC];
	int	unfriendly[MAXNOC];
	int	overfly[MAXNOC];
	struct	qelem ilist[MAXNOC], *qp;
	s_char	mypath[1024];
	int	myp;
	int	civ, mil;
	natid	plane_owner;
	struct	sctstr sect;
	struct	shpstr ship;
	struct	lndstr land;
	struct	nstr_item ni;
	natid	cn;
	struct	natstr *over, *mynatp;
	struct	plist *plp;
	int	evaded;
#ifdef ASW_PLANES
	struct	shiplook head;
	struct	shiplook *s,*s2;

	bzero(&head,sizeof(struct shiplook));
	head.uid = -1;
#endif /* ASW_PLANES */

	plp = (struct plist *) bomb_list->q_forw;
	plane_owner = plp->plane.pln_own;

	bzero((s_char *)mypath, 1024);
	bcopy(path,mypath,strlen(path));
	myp = 0;

	bzero((s_char *)overfly, sizeof(overfly));
	bzero((s_char *)gotilist, sizeof(gotilist));
	bzero((s_char *)unfriendly, sizeof(unfriendly));
	for (cn=1; cn<MAXNOC; cn++) {
		if ((mynatp=getnatp(cn)) == 0)
			continue;
		rel = getrel(mynatp, plane_owner);
#ifdef SLOW_WAR
		if (rel != HOSTILE && rel != AT_WAR && rel != MOBILIZATION && rel != SITZKRIEG)
                        continue;
#else
		if (rel != HOSTILE && rel != AT_WAR)
			continue;
#endif /* SLOW_WAR */
		if (plane_owner == cn)
			continue;
		unfriendly[cn]++;
	}
	if (mission_flags & PM_R) {
		if (mission_flags & P_S) {
			PR(plane_owner,"\nSPY Plane report\n");
			PRdate(plane_owner);
			sathead();
#ifdef ASW_PLANES
		} else if (mission_flags & P_A){
			PR(plane_owner,"\nAnti-Sub Patrol report\n");
#endif /* ASW_PLANES */
		} else {
			PR(plane_owner,"\nReconnaissance report\n");
			PRdate(plane_owner);
		}
	}

	pln_removedupes(bomb_list, esc_list);
	while ((dir = mypath[myp++]) && !QEMPTY(bomb_list)) {
		if ((val = chkdir(dir, DIR_STOP, DIR_LAST)) == 0)
			break;
		/* XXX using xnorm is probably bad */
		x = xnorm(x + diroff[val][0]);
		y = ynorm(y + diroff[val][1]);
		getsect(x, y, &sect);
		over = getnatp(sect.sct_own);

		evaded = do_evade(bomb_list,esc_list,mission_flags);
		if ((unfriendly[sect.sct_own]) && (!evaded))
			ac_doflak(bomb_list, &sect);

		/* mission planes aborted due to flak -- don't send escorts */
		if (QEMPTY(bomb_list))
			break;

		if ((!no_air_defense) && (!evaded)) {
			air_defense(x,y,plane_owner,bomb_list, esc_list);
		}
#ifdef HIDDEN
		setfnd(sect.sct_own, plane_owner, FOUND_FLY);
#endif /* HIDDEN */

		if (mission_flags & PM_R) {
			if (sect.sct_type == SCT_WATER){
#ifdef SWEEP_PLANES
				if (mission_flags & PM_S)
					plane_sweep(bomb_list,x,y);
#endif /* SWEEP_PLANES */
#ifdef ASW_PLANES
				if (mission_flags & P_A){
					plane_sona(bomb_list,x,y,&head);
				}
#endif /* ASW_PLANES */
#if	defined(BMAP) || defined(AUTONAV)
				if (!ismission)
					setbigmap(sect.sct_x, sect.sct_y,
						dchr[sect.sct_type].d_mnem);
#endif
				continue;
			}
			if (mission_flags & P_S) {
				satdisp(&sect, (mission_flags&P_I)?10:50, 1);
			} else {
				/* This is borrowed from lookout */
				if (sect.sct_own == plane_owner)
					PR(plane_owner,"Your ");
				else {
					PR(plane_owner,
						fmt("%s (#%d) ",
						cname(sect.sct_own),
						sect.sct_own));
				}
				PR(plane_owner,dchr[sect.sct_type].d_name);
#if	defined(BMAP) || defined(AUTONAV)
				if (!ismission)
					setbigmap(sect.sct_x, sect.sct_y,
						dchr[sect.sct_type].d_mnem);
#endif
				PR(plane_owner,fmt(" %d%% efficient ",
					owner ? sect.sct_effic :
					round((int)sect.sct_effic,25)));
				civ = getvar(V_CIVIL, (s_char *)&sect, EF_SECTOR);
				mil = getvar(V_MILIT, (s_char *)&sect, EF_SECTOR);
				if (civ)
					PR(plane_owner,fmt("with %s%d civ ",
					    owner ? "" : "approx ",
					    owner? civ : round(civ, 25)));
				if (mil)
					PR(plane_owner,fmt("with %s%d mil ",
						owner ? "" : "approx ",
						owner? mil : round(mil, 25)));
				PR(plane_owner,
					fmt("@ %s\n", xyas(x, y, plane_owner)));
			}
		} else {
			PR(plane_owner,fmt("flying over %s at %s\n",
				dchr[sect.sct_type].d_name,
				xyas(x, y, plane_owner)));
#if	defined(BMAP) || defined(AUTONAV)
			if (!ismission)
				setbigmap(sect.sct_x, sect.sct_y,
					dchr[sect.sct_type].d_mnem);
#endif
		}
                if (sect.sct_own == 0 || sect.sct_own == plane_owner)
                        continue;

		if (evaded)
			continue;

		non_missiles = 0;
		for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw){
			struct plist *ip = (struct plist *) qp;
			if (!(plchr[ip->plane.pln_type].pl_flags & P_M))
				non_missiles = 1;
		}

		if (!non_missiles)
			continue;

		if (unfriendly[sect.sct_own] && !gotilist[sect.sct_own]) {
			getilist(&ilist[sect.sct_own], sect.sct_own);
			gotilist[sect.sct_own]++;
		}
		if ((rel = getrel(over, plane_owner)) == ALLIED)
			continue;
		overfly[sect.sct_own]++;
		PR(sect.sct_own, fmt("%s planes spotted over %s\n",
			cname(plane_owner), xyas(x, y, sect.sct_own)));
		if (rel == NEUTRAL)
			continue;
		ac_intercept(bomb_list, esc_list, &ilist[sect.sct_own],
			sect.sct_own, x, y);

	}
#if	defined(BMAP) || defined(AUTONAV)
	if (!ismission)
		writebigmap();
#endif
	if (QEMPTY(bomb_list) && QEMPTY(esc_list)){
#ifdef ASW_PLANES
		if (mission_flags & P_A){
			s=head.next;
			while (s != (struct shiplook *)0){
				s2=s;
				s=s->next;
				free(s2);
			}
		}
#endif /* ASW_PLANES */
		return;
	}

	bzero((s_char *)nats, sizeof(nats));
	snxtitem_xy(&ni, EF_SHIP, x, y);
	while (nxtitem(&ni, (s_char *)&ship)) {
		if (mchr[ship.shp_type].m_flags & M_SUB)
			continue;
		nats[ship.shp_own]++;
	}
	if (!evaded)
		ac_shipflak(bomb_list, x, y);

	bzero((s_char *)lnats, sizeof(lnats));
	snxtitem_xy(&ni, EF_LAND, x, y);
	while (nxtitem(&ni, (s_char *)&land)) {
		lnats[land.lnd_own]++;
	}
	if (!evaded)
		ac_landflak(bomb_list, x, y);

	for (cn=1; cn<MAXNOC && !QEMPTY(bomb_list); cn++) {
		if (plane_owner == cn)
			continue;
		if (overfly[cn] > 0)
			nreport(plane_owner, N_OVFLY_SECT, cn, overfly[cn]);
		if (nats[cn] != 0){
#ifdef MERC
			if(cn != 0)
#endif
			PR(cn, fmt("%s planes spotted over ships in %s\n",
				cname(plane_owner), xyas(x, y, cn)));
			if (unfriendly[cn]){
				if (!gotilist[cn]) {
					getilist(&ilist[cn], cn);
					gotilist[cn]++;
				}
				PR(plane_owner,	fmt("Flying over %d #%d ships...\n", nats[cn], cn));
				/* This makes going for ships in harbors tough */
				if (!evaded)
					ac_intercept(bomb_list, esc_list,
					&ilist[cn], cn, x, y);
			}
		}
		if (lnats[cn] != 0){
#ifdef MERC
			if(cn != 0)
#endif
			PR(cn, fmt("%s planes spotted over land units in %s\n",
				cname(plane_owner), xyas(x, y, cn)));
			if (unfriendly[cn]){
				if (!gotilist[cn]) {
					getilist(&ilist[cn], cn);
					gotilist[cn]++;
				}
				PR(plane_owner,fmt("Flying over %d #%d units...\n", lnats[cn], cn));
				if (!evaded)
					ac_intercept(bomb_list, esc_list,
					&ilist[cn], cn, x, y);
			}
		}
	}

#ifdef ASW_PLANES
	if ((mission_flags & P_A) && (head.uid != -1)){
		s=head.next;
		while (s != (struct shiplook *)0){
			s2=s;
			s=s->next;
			free(s2);
		}
	}
#endif /* ASW_PLANES */
}

ac_intercept(bomb_list, esc_list, def_list, def_own, x, y)
	struct	qelem *bomb_list;
	struct	qelem *esc_list;
	struct	qelem *def_list;
	natid	def_own;
	coord	x;
	coord	y;
{
	register struct plnstr *pp;
	register struct plchrstr *pcp;
	int	gas;
	struct	plist *ip;
	int	icount;
	struct	shpstr ship;
	struct	lndstr land;
	struct	sctstr sect;
	struct	qelem *next;
	struct	qelem *qp;
	struct	qelem int_list;
	s_char	*ptr;
	int	type;
	int	att_count;
	natid	plane_owner;

	ip = (struct plist *)bomb_list->q_forw;
	plane_owner = ip->plane.pln_own;

	icount = 0;
	att_count = 0;

	/* don't intercept missiles */
	for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw){
		struct plist *p = (struct plist *)qp;
		if (!(plchr[p->plane.pln_type].pl_flags & P_M))
			att_count++;
	}
	for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw){
		struct plist *p = (struct plist *)qp;
		if (!(plchr[p->plane.pln_type].pl_flags & P_M))
			att_count++;
	}
	if (att_count == 0)
		return;

	initque(&int_list);
	for (qp = def_list->q_forw; qp != def_list; qp = next) {
		next = qp->q_forw;
		ip = (struct plist *) qp;
		pp = &ip->plane;
		pcp = ip->pcp;
		if (pp->pln_range/2 < mapdist(x, y, pp->pln_x, pp->pln_y))
			continue;
		if (pp->pln_ship >= 0) {
			getship(pp->pln_ship, &ship);
			type = EF_SHIP;
			ptr = (s_char *) &ship;
		} else
		if (pp->pln_land >= 0) {
			getland(pp->pln_land, &land);
			type = EF_LAND;
			ptr = (s_char *) &land;
		} else {
			getsect(pp->pln_x, pp->pln_y, &sect);
			type = EF_SECTOR;
			ptr = (s_char *) &sect;
		}
		gas = getvar(V_PETROL, ptr, type);
		if (((float)gas) < (((float)pcp->pl_fuel)/2.0)){
			remque(qp);
			free((s_char *)qp);
			continue;
		}
		/* got one; delete from def_list, add to int_list */
		remque(qp);
		insque(qp, &int_list);
		pp->pln_mobil -= 10;
		putplane(pp->pln_uid, pp);
		putvar(V_PETROL, gas - (pcp->pl_fuel/2), ptr, type);
		if (pp->pln_ship >= 0) {
			putship(pp->pln_ship, &ship);
		}
		if (pp->pln_land >= 0) {
			putland(pp->pln_land, &land);
		}
		else
			putsect(&sect);
		icount++;
		if (icount > att_count)
			break;
	}
	if (icount == 0)
		return;
	PR(plane_owner,fmt("%d %s fighter%s rising to intercept!\n", icount,
		cname(def_own), icount == 1 ? " is" : "s are"));
	PR(def_own,fmt("%d fighter%s intercepting %s planes!\n", icount,
		icount == 1 ? " is" : "s are", cname(plane_owner)));
#ifdef NEWPAF
	ac_combat_headers(plane_owner, def_own);
#endif
	ac_airtoair(esc_list, &int_list, def_own);
	ac_airtoair(bomb_list, &int_list, def_own);
}

#ifdef NEWPAF
ac_combat_headers(plane_owner, def_own)
natid	plane_owner, def_own;
{
	PR(plane_owner,fmt(" %-10.10s %-10.10s  strength int odds  damage           results\n", cname(plane_owner), cname(def_own)));
	PR(def_own, fmt(" %-10.10s %-10.10s  strength int odds  damage           results\n", cname(def_own), cname(plane_owner)));
}
#endif

/*
 * air-to-air combat.
 */
ac_airtoair(att_list, int_list, def_own)
	struct	qelem *att_list;
	struct	qelem *int_list;
	natid	def_own;
{
	register struct plist *attacker;
	register struct plist *interceptor;
	struct	qelem *att;
	struct	qelem *in;
	int	nplanes;
	int	adam;
	int	idam;
	int	more_att;
	int	more_int;
	s_char	*mesg, *mesg2;
	struct	qelem *att_next;
	struct	qelem *in_next;
	natid	att_own;

	att = att_list->q_forw;
	in = int_list->q_forw;
	more_att = 1;
	more_int = 1;
	if (QEMPTY(att_list) || QEMPTY(int_list)) {
		more_att = 0;
		more_int = 0;
	}
	while (more_att || more_int) {
		in_next = in->q_forw;
		att_next = att->q_forw;
		attacker = (struct plist *) att;

		/* skip missiles. If only missiles left, we're done */
		if (plchr[attacker->plane.pln_type].pl_flags & P_M){
			att = att_next;
			if (att == att_list) {
				more_att = 0;
				if (QEMPTY(att_list))
					more_int = 0;
				else
					att = att->q_forw;
			}
			if (all_missiles(att_list))
				more_att = 0;
			continue;
		}
		interceptor = (struct plist *) in;
		att_own = attacker->plane.pln_own;
		def_own = interceptor->plane.pln_own;
#ifdef 	NEWPAF
                PR(att_own, fmt(" %3.3s #%-4d  %3.3s #%-4d",
                      	attacker->pcp->pl_name,   
       		        attacker->plane.pln_uid,
			interceptor->pcp->pl_name,
			interceptor->plane.pln_uid));
#ifdef MERC
		if(def_own != 0)
#endif MERC
		PR(def_own, fmt(" %3.3s #%-4d  %3.3s #%-4d",
			interceptor->pcp->pl_name,
			interceptor->plane.pln_uid,
			attacker->pcp->pl_name,
			attacker->plane.pln_uid));
#else
		mesg = fmt("  %s %s (#%d) intercepts %s %s (#%d)\n",
			cname(interceptor->plane.pln_own),
			interceptor->pcp->pl_name,
			interceptor->plane.pln_uid,
			cname(attacker->plane.pln_own),
			attacker->pcp->pl_name,
			attacker->plane.pln_uid);
		PR(att_own, mesg);
#ifdef MERC
		if (def_own != 0)
#endif MERC
		PR(def_own, mesg);
#endif /* NEWPAF */
		adam = idam = 0;
		nplanes = attacker->plane.pln_effic;
		if (nplanes > interceptor->plane.pln_effic)
			nplanes = interceptor->plane.pln_effic;
		ac_dog(attacker, interceptor, &adam, &idam);
		if (interceptor->pcp->pl_flags & P_M)
			idam = 100;

#ifdef NEWPAF
			PR(att_own, fmt("%3d/%-3d", adam, idam));
			PR(def_own, fmt("%3d/%-3d", idam, adam));
			mesg = ac_planedamage(attacker,
				interceptor->plane.pln_own,
				adam, def_own, 1, 0);
			mesg = strdup(mesg);
#else
		if (adam > 0)
			ac_planedamage(interceptor, attacker->plane.pln_own,
				idam, att_own, 1, 1);
#endif
#ifdef NEWPAF
			mesg2 = ac_planedamage(interceptor, 
				attacker->plane.pln_own,
				idam, att_own, 1, 0);
			PR(att_own, fmt("%-13.13s %-13.13s\n", mesg, mesg2));
			PR(def_own, fmt("%-13.13s %-13.13s\n", mesg2, mesg));
#else
		if (idam > 0)
			ac_planedamage(attacker, interceptor->plane.pln_own,
				adam, def_own, 1, 1);
#endif
		in = in_next;
		att = att_next;
		if (att == att_list) {
			more_att = 0;
			if (QEMPTY(att_list))
				more_int = 0;
			else
				att = att->q_forw;
		}
		if (in == int_list) {
			more_int = 0;
			if (QEMPTY(int_list))
				more_att = 0;
			else
				in = in->q_forw;
		}
	}
}

all_missiles(att_list)
struct	qelem *att_list;
{
	struct	qelem *qp;
	struct	plist *p;

	qp = att_list->q_forw;
	while (qp != att_list){
		p = (struct plist *)qp;
		if (!(plchr[p->plane.pln_type].pl_flags & P_M))
			return 0;

		qp = qp->q_forw;
	}
	return 1;
}

#define MIN(x,y) (x < y ? x : y)
#define MAX(x,y) (x < y ? y : x)
int
ac_dog(ap, dp, adam, ddam)
	register struct plist *ap;
	register struct plist *dp;
	int *adam, *ddam;
{
	int	att, def;
	double	odds;
	int	intensity, a_cas, d_cas;
	natid	att_own, def_own;
	
	att_own = ap->plane.pln_own;
	def_own = dp->plane.pln_own;

	if (ap->plane.pln_att == 0){
		att = ap->plane.pln_def * ap->plane.pln_effic / 100;
		att = MAX(att,ap->pcp->pl_def/2);
	}else{
		att = ap->plane.pln_att * ap->plane.pln_effic / 100;
		att = MAX(att,ap->pcp->pl_att/2);
	}

	def = dp->plane.pln_def * dp->plane.pln_effic / 100;
	def = MAX(def,dp->pcp->pl_def/2);

        if ((ap->pcp->pl_flags & P_F) && ap->bombs != 0)
                att -= 2;
        if ((dp->pcp->pl_flags & P_F) && dp->bombs != 0)
                def -= 2;
#ifndef STEALTHV
	if (ap->pcp->pl_flags & P_X)
		att += 3;
	if (dp->pcp->pl_flags & P_X)
		def += 3;
#else
	att += ((float)ap->pcp->pl_stealth/25.0);
	def += ((float)dp->pcp->pl_stealth/25.0);
#endif /* STEALTHV */
	odds = ((double)att/((double)def+(double)att));
	intensity = roll(20)+roll(20)+roll(20)+roll(20)+1;
#ifdef NEWPAF                                                                   
        PR(att_own,fmt("   %3d/%-3d %3d  %3.2f  ",att,def,intensity,odds));         
	PR(def_own,fmt("   %3d/%-3d %3d  %3.2f  ",def,att,intensity,odds));
#else
	PR(att_own,fmt("Attacker strength %d\t",att));
	PR(att_own,fmt("Interceptor strength %d\t",def));
	PR(def_own,fmt("Attacker strength %d\t",att));
	PR(def_own,fmt("Interceptor strength %d\t",def));
	PR(att_own,fmt("Odds are %1.2f\t",odds));
	PR(def_own,fmt("Odds are %1.2f\t",odds));
	PR(att_own,fmt("Intensity is %d\n",intensity));
	PR(def_own,fmt("Intensity is %d\n",intensity));
#endif NEWPAF
	a_cas = 0; d_cas = 0;
	while ((intensity--)>0){
#ifndef NEWPAF
		if (((a_cas + d_cas) % 70) == 69){
			PR(att_own,"\n");
			PR(def_own,"\n");
		}
#endif	/* NEWPAF */

		if (chance(odds)) {
#ifndef NEWPAF
			PR(att_own,"!");
			PR(def_own,"@");
#endif
			d_cas += 1;
			if ((dp->plane.pln_effic-d_cas) < PLANE_MINEFF)
				intensity=0;
		} else {
#ifndef NEWPAF
			PR(att_own,"@");
			PR(def_own,"!");
#endif
			a_cas += 1;
			if ((ap->plane.pln_effic-a_cas) < PLANE_MINEFF)
				intensity=0;
		}
	}
#ifndef NEWPAF
	PR(att_own,fmt("\n"));
	PR(def_own,fmt("\n"));
#endif /* NEWPAF */

	*adam = a_cas;
	*ddam = d_cas;
}

/*
 * zap plane associated with plp.
 * Damaging country is "from", damage is "dam".
 * def_own is the country on the other side of the conflict from the plane
 * owner. The only time def_own != from is when the interceptor is getting
 * damaged.
 *
 * NOTE: This routine removes the appropriate plane element from the
 * queue if it gets destroyed.  That means that the caller must assume
 * that the current queue pointer is invalid on return from the ac_planedamage
 * call.  (this has caused bugs in the past)
 */
s_char *
ac_planedamage(plp, from, dam, other, checkabort, show)
	struct	plist *plp;
	natid	from;
	int	dam;
	natid	other;
	int	checkabort;
	int	show;
{
	register struct plnstr *pp;
	int	disp;
	static	s_char	dmess[255];
	int	eff;
	s_char	*mesg;
	struct shpstr ship;
	struct lndstr land;
	s_char *sprintf();
	natid	plane_owner;

	disp = 0;
	pp = &plp->plane;
	plane_owner = pp->pln_own;
	eff = pp->pln_effic;
	if ((dam <= 0) && (checkabort != 2))
		return dmess;
	bzero(dmess,255);
	eff -= dam;
	if (eff < 0)
		eff = 0;
	if (eff < PLANE_MINEFF) {
#ifdef NEWPAF
		sprintf(dmess," shot down");
#else
		sprintf(dmess," -- shot down");
#endif /* NEWPAF */
		disp = 1;
	} else if (chance((100-eff)/100.0) && checkabort){
#ifdef NEWPAF
		sprintf(dmess," aborted @%2d%%", eff);
#else
		sprintf(dmess," -- aborted with %d%% damage", 100-eff);
#endif /* NEWPAF */
		disp = 2;
#ifdef NEWPAF
	} else if (show == 0) {
		sprintf(dmess," cleared");
#endif
	}
	
	if ((plp->pcp->pl_flags & P_M) == 0) {
		if (checkabort != 2){ /* not checking abort after ship flak */
			mesg = fmt("    %s %s (#%d) takes %d%s.",
				cname(pp->pln_own),
				plp->pcp->pl_name, pp->pln_uid, dam, dmess);
			if (show) {
				PR(plane_owner,mesg);
				PR(other,mesg);
			}
		} else{
			if (disp > 0){
				mesg = fmt("    %s %s (#%d)%s",
					cname(pp->pln_own),
					plp->pcp->pl_name, pp->pln_uid, dmess);
				if (show) {
					PR(plane_owner,mesg);
					PR(other,mesg);
				}
			}
		}
	}
	if (show) {
		PR(plane_owner,"\n");
		PR(other,"\n");
	}

	pp->pln_effic = eff;
	if (disp == 1) {
		if (from != 0 && (plp->pcp->pl_flags & P_M) == 0)
			nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
		if (pp->pln_ship >= 0) {
			getship(pp->pln_ship,&ship);
			take_plane_off_ship(pp,&ship);
		}
		if (pp->pln_land >= 0) {
			getland(pp->pln_land,&land);
			take_plane_off_land(pp,&land);
		}
		pp->pln_own = 0;
		putplane(pp->pln_uid, pp);
		remque(&plp->queue);
		free((s_char *)plp);
	} else if (disp == 2) {
		putplane(pp->pln_uid, pp);
		remque(&plp->queue);
		free((s_char *)plp);
	} else
		putplane(pp->pln_uid, pp);
	return dmess;
}

ac_doflak(list, from)
	struct	qelem *list;
	struct	sctstr *from;
{
	int	shell, add;
	int	gun;
	natid	plane_owner;
	struct	plist *plp;

	plp = (struct plist *)list->q_forw;
	plane_owner = plp->plane.pln_own;

	gun = getvar(V_GUN, (s_char *)from, EF_SECTOR);
	shell = getvar(V_SHELL, (s_char *)from, EF_SECTOR);
	add = 0;
	if (shell < (gun/2))
		add = supply_commod(from->sct_own,from->sct_x,from->sct_y,
			I_SHELL,ldround(((double)gun/2.0),1)-shell);
	shell += add;
	if (gun == 0 || shell == 0)
		return;
	if (gun > shell*2)
		gun = shell*2;
	if (gun > 14)
		gun = 14;

	shell -= (gun/2);
	putvar(V_SHELL, shell, (s_char *)from, EF_SECTOR);
	gun = 2.0 * tfact(from->sct_own, (double) gun);
	if (gun > 0) {
		PR(plane_owner,fmt("firing %d flak guns in %s...\n",
			gun, xyas(from->sct_x, from->sct_y, plane_owner)));
#ifdef MERC
		if(from->sct_own != 0)
#endif
		PR(from->sct_own, fmt("firing %d flak guns in %s...\n",
			gun, xyas(from->sct_x, from->sct_y, from->sct_own)));
		ac_fireflak(list, from->sct_own, gun);
	}
}

ac_shipflak(list, x, y)
	struct	qelem *list;
	coord	x;
	coord	y;
{
	struct	nstr_item ni;
	struct	shpstr ship;
	struct	mchrstr *mcp;
	int	firing;
	int	gun;
	int	shell;
	int	rel;
	s_char	*msg;
	struct	qelem *qp;
	struct	qelem *next;
	struct  plist *plp;
	natid	from=0, plane_owner;
	int	fired=0;

	plp = (struct plist *)list->q_forw;
	plane_owner = plp->plane.pln_own;

	snxtitem_xy(&ni, EF_SHIP, x, y);
	while (!QEMPTY(list) && nxtitem(&ni, (s_char *)&ship)) {
		if (ship.shp_own == 0 || ship.shp_own == plane_owner)
			continue;
		mcp = &mchr[ship.shp_type];
		if (mcp->m_flags & M_SUB)
			continue;
		rel = getrel(getnatp(ship.shp_own), plane_owner);
#ifdef SLOW_WAR
		if (rel != AT_WAR && rel != HOSTILE && rel != MOBILIZATION && rel != SITZKRIEG)
			continue;
#else
		if (rel != AT_WAR && rel != HOSTILE)
			continue;
#endif /* SLOW_WAR */
		gun = min(getvar(V_GUN, (s_char *)&ship, EF_SHIP), mcp->m_glim);
		shell = getvar(V_SHELL, (s_char *)&ship, EF_SHIP);
		if (shell == 0)
			shell += supply_commod(ship.shp_own,ship.shp_x,
				ship.shp_y,I_SHELL,1);
		if (gun == 0 || shell == 0)
			continue;
		firing = (int) (techfact(ship.shp_tech, (double)gun) * 4.0);
		fired=1;
#ifdef	SHIPNAMES
		msg = fmt("firing %d flak guns from %s %s(#%d)...\n",
			firing, mcp->m_name, ship.shp_name, ship.shp_uid);
#else
		msg = fmt("firing %d flak guns from %s #%d...\n",
			firing, mcp->m_name, ship.shp_uid);
#endif	SHIPNAMES
		PR(plane_owner,msg);
#ifdef MERC
		if(ship.shp_own != 0)
#endif
		PR(ship.shp_own, msg);
		from = ship.shp_own;
		ac_fireflak(list, ship.shp_own, firing);
		putvar(V_SHELL, shell - 1, (s_char *)&ship, EF_SHIP);
		putship(ni.cur, &ship);
	}

	if (fired)
		for (qp = list->q_forw; qp != list; qp = next) {
			next = qp->q_forw;
			plp = (struct plist *) qp;
			ac_planedamage(plp, plp->plane.pln_own, 0, from, 2, 1);
		}
}

ac_landflak(list, x, y)
	struct	qelem *list;
	coord	x;
	coord	y;
{
	struct	nstr_item ni;
	struct	lndstr land;
	struct	lchrstr *lcp;
	int	firing;
	int	rel;
	s_char	*msg;
	struct	qelem *qp;
	struct	qelem *next;
	struct  plist *plp;
	natid	from=0;
	int	fired=0;
	natid	plane_owner;

	plp = (struct plist *)list->q_forw;
	plane_owner = plp->plane.pln_own;

	snxtitem_xy(&ni, EF_LAND, x, y);
	while (!QEMPTY(list) && nxtitem(&ni, (s_char *)&land)) {
		if (land.lnd_own == 0 || land.lnd_own == plane_owner)
			continue;
		lcp = &lchr[land.lnd_type];

		if ((lcp->l_flags&L_FLAK) == 0)
			continue;

		if (lcp->l_aaf == 0)
			continue;

		rel = getrel(getnatp(land.lnd_own), plane_owner);
#ifdef SLOW_WAR
		if (rel != AT_WAR && rel != HOSTILE && rel != MOBILIZATION && rel != SITZKRIEG)
			continue;
#else
		if (rel != AT_WAR && rel != HOSTILE)
			continue;
#endif /* SLOW_WAR */
		firing = (int)(techfact(land.lnd_tech, (double)lcp->l_aaf)*3.0);
		fired=1;
		msg = fmt("firing flak guns from unit %s #%d (aa rating %d)...\n",
			lcp->l_name, land.lnd_uid, lcp->l_aaf);

		PR(plane_owner,msg);
#ifdef MERC
		if(land.lnd_own != 0)
#endif
		PR(land.lnd_own, msg);
		from = land.lnd_own;
		ac_fireflak(list, land.lnd_own, firing);
	}

	if (fired)
		for (qp = list->q_forw; qp != list; qp = next) {
			next = qp->q_forw;
			plp = (struct plist *) qp;
			ac_planedamage(plp, plp->plane.pln_own, 0, from, 2, 1);
		}
}

/*
 * Called from shipflak, landflak, and doflak.
 */
ac_fireflak(list, from, guns)
	struct	qelem *list;
	natid	from;
	int	guns;
{
	extern	double flakscale;
	register struct plnstr *pp;
	struct	plist *plp;
	int	n;
	float	mult;
	int	diff;
	struct	qelem *qp;
	struct	qelem *next;

	plp = (struct plist *)list->q_forw;

	for (qp = list->q_forw; qp != list; qp = next) {
		/*
		 * fighters don't get shot at by flak
		 * non-tactical bombers are harder to hit with flak.
		 * ('Cause they're not dive-bombing?)
		 */
		next = qp->q_forw;
		plp = (struct plist *) qp;
		pp = &plp->plane;
		diff = guns - pp->pln_def;
		if ((plp->pcp->pl_flags & P_T) == 0)
			diff--;
		if (plp->pcp->pl_flags & P_X)
			diff -= 2;
#ifdef CHOPPER_STEALTH
		if (plp->pcp->pl_flags & P_H)
			diff -= 1;
#endif /* CHOPPER_STEALTH */
		if (diff > 8)
			mult = flaktable[FLAK_MAX] * 1.33;
		else if (diff < -7)
			mult = flaktable[0] * 0.66;
		else {
			diff += 7;
			mult = flaktable[diff];
		}
		mult *= flakscale;
		n = (int) ((roll(8) + 2) * mult);
		if (n > 100)
			n = 100;
		ac_planedamage(plp, from, n, from, 0, 1);
	}
}

/*
 * Get a list of planes available for interception duties.
 */
static
getilist(list, own)
	struct	qelem *list;
	natid	own;
{
	register struct plchrstr *pcp;
	struct	plnstr plane;
	struct	shpstr ship;
	struct	lndstr land;
	struct	sctstr sect;
	struct	nstr_item ni;
	int	type;
	s_char	*ptr;
	struct	plist *ip;

	initque(list);
	snxtitem_all(&ni, EF_PLANE);
	while (nxtitem(&ni, (s_char *)&plane)) {
		if (plane.pln_own != own)
			continue;
		pcp = &plchr[plane.pln_type];
		if ((pcp->pl_flags & P_F) == 0)
			continue;
		if (plane.pln_mission != 0)
			continue;
		if (plane.pln_mobil <= 0)
			continue;
		if (plane.pln_effic < 40)
			continue;
		if (plane.pln_ship >= 0) {
			if (!can_fly(plane.pln_uid))
				continue;
			getship(plane.pln_ship,&ship);
			ptr = (s_char *)&ship;
			type = EF_SHIP;
		} else
		if (plane.pln_land >= 0) {
			if (!can_fly(plane.pln_uid))
				continue;
			getland(plane.pln_land,&land);
			ptr = (s_char *)&land;
			type = EF_LAND;
		} else {
			getsect(plane.pln_x, plane.pln_y, &sect);
			ptr = (s_char *)&sect;
			type = EF_SECTOR;
#if 0
			if (sect.sct_effic < 60 && (pcp->pl_flags & P_V) == 0)
				continue;
#else
			if ((sect.sct_effic < 60 || sect.sct_type != SCT_AIRPT)
			&& (pcp->pl_flags & P_V) == 0)
				continue;
#endif
		}
		if (((float)getvar(V_PETROL, ptr, type)) <
			(((float)pcp->pl_fuel)/2.0))
			continue;
		/* got one! */
		ip = (struct plist *) malloc(sizeof(*ip));
		ip->state = P_OK;
		ip->bombs = 0;
		ip->misc = 0;
		ip->pcp = &plchr[plane.pln_type];
		bcopy((s_char *)&plane, (s_char *)&ip->plane, sizeof(plane));
		insque(&ip->queue, list);
	}
}

#ifdef	ABM

/*
 * Get a list of nuclear interceptors (not including sats).     
 *
 * Alex Shatsky (lex@ucscb.ucsc.edu)
 *
 * liberally borrowed from getilist.
 */
static
getirvlist(list, own)
	struct	qelem *list;
	natid	own;
{
	register struct plchrstr *pcp;
	struct 	plnstr plane;
	struct 	shpstr ship;
	struct 	lndstr land;
	struct 	sctstr sect;
	struct	nstr_item ni;
	int	type;
	s_char	*ptr;
	struct	plist *irv;

	initque (list);
	snxtitem_all (&ni, EF_PLANE);
	while (nxtitem (&ni, (s_char *)&plane)) {
#ifdef	BETTERABM
		/* When BETTERABM is used, own is owner of bomb */
		if (plane.pln_own == own)
			continue;

		if (plane.pln_own == 0)
			continue;
		
		if (getrel(getnatp(plane.pln_own), own) == ALLIED)
			continue;
#else
		/* When BETTERABM is not used, own is owner of target sect */
		if (plane.pln_own != own)
			continue;

		if (plane.pln_own == 0)
			continue;
#endif	BETTERABM
		pcp = &plchr[plane.pln_type];
 		if ((pcp->pl_flags & P_N) == 0)
			continue;
		/* Ignore satellites */
		if (!(pcp->pl_flags & P_O) == 0)
			continue;
		if (plane.pln_mobil <= 0)
			continue;
		if (plane.pln_effic < 100)
			continue;
		if (plane.pln_ship >= 0) {
			if (!getship(plane.pln_ship, &ship) ||
			    !can_fly(plane.pln_uid))
				continue;
			ptr = (s_char *)&ship;
			type = EF_SHIP;
		} else
		if (plane.pln_land >= 0) {
			if (!getland(plane.pln_land, &land) ||
			    !can_fly(plane.pln_uid))
				continue;
			ptr = (s_char *)&land;
			type = EF_LAND;
		} else {
			getsect (plane.pln_x, plane.pln_y, &sect);
			ptr = (s_char *)&sect;
			type = EF_SECTOR;
			if ((sect.sct_effic < 60 || sect.sct_type != SCT_AIRPT)
			&& (pcp->pl_flags & P_V) == 0)
				continue;
		}
		if (((float)getvar(V_PETROL, ptr, type)) <
			(((float)pcp->pl_fuel)/2.0))
			continue;
		/* got a valid interceptor */
		irv = (struct plist *) malloc(sizeof(*irv));
		irv->state = P_OK;
		irv->bombs = 0;
		irv->misc = 0;
		irv->pcp = &plchr[plane.pln_type];
		bcopy((s_char *)&plane, (s_char *)&irv->plane, sizeof(plane));
		insque(&irv->queue, list);
	}
}

/*
 * Attempt to intercept an incoming warhead.  Return 1 if warhead is 
 * successfully destroyed.
 * This really should not allow the safe targeting of unowned adjoining
 * sectors (water, etc.), but it does.
 *
 * Alex Shatsky (lex@ucscb.ucsc.edu)
 */
short
rv_intercept(x, y, bombown)
	coord 	x;
	coord	y;
	natid	bombown;
{
	register struct plnstr *pp;
	register struct plchrstr *pcp;
	register struct plist *interceptor;
	struct	sctstr  sect;
	struct	qelem	*irvlist;
	struct	qelem	foo;
	struct	qelem	int_list;
	struct  qelem	*qp;
	struct	qelem	*next;
	struct 	qelem	*in;
	struct  plist	*ip;
	struct	sctstr	psect;
	struct	lndstr	pland;
	struct	shpstr	pship;
	int 	icount = 0;
	int 	type;
	int	gas;
	s_char	*ptr;
	short	destroyed = 0;
	double	int_chance;

	irvlist = &foo;
	getsect(x, y, &sect);
#ifdef	BETTERABM
	getirvlist(irvlist, bombown);
#else
	getirvlist(irvlist, sect.sct_own);
#endif	BETTERABM
#ifdef MERC
	if(sect.sct_own != 0)
#endif
	PR(sect.sct_own, fmt("%s warhead incoming on sector %s\n", 
		cname(bombown), xyas(x, y, sect.sct_own)));
	initque (&int_list);
	for (qp = irvlist->q_forw; qp != irvlist; qp = next) {
		next = qp->q_forw;
		ip = (struct plist *)qp;
		pp = &ip->plane;
		pcp = ip->pcp;
		/* ABM's go one way, so we can use all the range */
		if (pp->pln_range < mapdist(x, y, pp->pln_x, pp->pln_y))
			continue;
		if (pp->pln_ship >= 0) {
			getship(pp->pln_ship, &pship);
			type = EF_SHIP;
			ptr = (s_char *)&pship;
		} else
		if (pp->pln_land >= 0) {
			getland(pp->pln_land, &pland);
			type = EF_LAND;
			ptr = (s_char *)&pland;
		} else {
			getsect (pp->pln_x, pp->pln_y, &psect);
			type = EF_SECTOR;
			ptr = (s_char *)&psect;
		}
		gas = getvar(V_PETROL, ptr, type);
		if (((float)gas) < (((float)pcp->pl_fuel)/2.0)){
			remque(qp);
			free((s_char *)qp);
			continue;
		}
		/* got one interceptor, delete from irv_list and   
   		 * add to  int_list.
    		 */
		remque(qp);
		insque(qp, &int_list);
		putplane(pp->pln_uid, pp);
		putvar(V_PETROL, gas - (pcp->pl_fuel/2), ptr, type);
		if (pp->pln_ship >= 0){
			putship (pp->pln_ship, &pship);
		} else
		if (pp->pln_land >= 0){
			putland (pp->pln_land, &pland);
		}
		else putsect(&psect);
		/* only allow two abms per warhead */
		if (++icount == 2)
			break;
	}
	if (icount == 0) {
#ifdef MERC
		if(sect.sct_own != 0)
#endif
 		PR(sect.sct_own, fmt("No abms launched to intercept.\n"));
		return (destroyed);
	}
	icount = 0;
	/* attempt to destroy warhead */
/*	in = &int_list->q_forw; */
	in = int_list.q_forw;
	while (!(QEMPTY(&int_list))) {
		interceptor = (struct plist *)in;
#ifdef	BETTERABM
		if (sect.sct_own == interceptor->plane.pln_own) {
			icount++;
			pr (fmt("%s abm launched in defense!\n",
			 	cname(interceptor->plane.pln_own)));
#ifdef MERC
			if(sect.sct_own != 0)
#endif
		    PR(sect.sct_own, "abm launched in defense!\n");
		}
		else
		{
#ifndef BABY_NUKES
			icount++;
			pr (fmt("%s abm launched in defense!\n",
			 	cname(interceptor->plane.pln_own)));
#ifdef MERC
			if(sect.sct_own != 0) {
#endif
		    PR(sect.sct_own, fmt("%s abm launched in defense!\n",
			cname(interceptor->plane.pln_own)));
		    PR(interceptor->plane.pln_own,
			fmt("abm launched to defend %s!\n",
				cname(sect.sct_own)));
#ifdef MERC
			}
#endif
#else
			remque(&interceptor->queue);
			free((s_char *)interceptor);
			in=in->q_forw;
			continue;
#endif /* BABY_NUKES */
		}
#else /* BETTERABM */
		icount++;
		pr (fmt("%s abm launched in defense!\n",
		 	cname(interceptor->plane.pln_own)));

		int_chance = (double)interceptor->plane.pln_def/100.0;
#ifdef MERC
		if(sect.sct_own != 0)
#endif
			PR(sect.sct_own, fmt("abm #%d launched in defense! Intercept chance is %d%%\n",interceptor->plane.pln_uid,ldround(int_chance*100.0,1)));
#endif	BETTERABM
		if (chance(int_chance) && !destroyed)  {
#ifdef	BETTERABM
			pr (fmt("Warhead destroyed by %s abm!\n",
				 cname(interceptor->plane.pln_own)));
#else
			pr (fmt("Warhead device destroyed by %s abm!\n",
				 cname(sect.sct_own)));
#endif	BETTERABM
#ifdef MERC
			if(sect.sct_own != 0)
#endif
			PR(sect.sct_own, fmt("%s missile intercepted!\n",
		    	     cname(bombown)));
#ifdef	BETTERABM
			if (sect.sct_own != interceptor->plane.pln_own)
#ifdef MERC
				if(interceptor->plane.pln_own != 0)
#endif
				PR(interceptor->plane.pln_own, 
					fmt("%s missile intercepted!\n",
					     cname(bombown)));
#endif	BETTERABM
			destroyed = 1;
			/* the news knows all... */
#ifdef	BETTERABM
			nreport(bombown, N_NUKE_STOP,
			    interceptor->plane.pln_own, 1);
#else
			nreport(bombown, N_NUKE_STOP, sect.sct_own, 1);
#endif	BETTERABM
     		}
		/* zap the missile */
#ifdef	BETTERABM
		interceptor->plane.pln_own = 0;
		putplane(interceptor->plane.pln_uid, &(interceptor->plane));
		remque(&interceptor->queue);
		free((s_char *)interceptor);
#else
		interceptor->plane.pln_own = 0;
		putplane(interceptor->plane.pln_uid, &(interceptor->plane));
		remque(&interceptor->queue);
		free((s_char *)interceptor);
/*		ac_planedamage(interceptor, bombown, 100, cnum, 1, 1); */
#endif	BETTERABM
		in = in->q_forw;
	}
	if (destroyed) return (destroyed);
	if (icount)
		pr (fmt("Warhead made it through abm defenses!\n"));
#ifdef MERC
	if(sect.sct_own != 0)
#endif
	if (icount)
		PR(sect.sct_own, fmt("Abm interception failed.\n"));
	return (destroyed);
}

#endif	ABM

can_fly(p) /* Can this plane fly from the ship or land unit it is on? */
int	p;
{
	struct plnstr plane;
	struct shpstr ship;
	struct lndstr land;
	struct plchrstr *pcp;
	struct mchrstr *scp;
	struct lchrstr *lcp;

	getplane(p,&plane);
	pcp = &plchr[plane.pln_type];

	if (plane.pln_ship >= 0){
		if (
			!(pcp->pl_flags & P_L) && !(pcp->pl_flags & P_M)
#ifdef SHIPCHOPPERS
			&& !(pcp->pl_flags & P_K)
#endif /* SHIPCHOPPERS */
#ifdef XLIGHT
			&& !(pcp->pl_flags & P_E)
#endif /* XLIGHT */
			)
			return 0;

		getship(plane.pln_ship,&ship);
		scp = & mchr[ship.shp_type];

		if ((pcp->pl_flags & P_L) && (scp->m_flags & M_FLY)){
			return 1;
		}
	
#ifdef SHIPSAMS
		if ((pcp->pl_flags & P_M) && (scp->m_flags & M_MSL)){
			return 1;
		}
#endif /* SHIPSAMS */
	
#ifdef SHIPCHOPPERS
		if ((pcp->pl_flags & P_K) && (scp->m_flags & M_CHOPPER)){
			return 1;
		}
#endif /* SHIPCHOPPERS */
	
#ifdef XLIGHT
		if ((pcp->pl_flags & P_E) && (scp->m_flags & M_XLIGHT)){
			return 1;
		}
#endif /* XLIGHT */
	}

	if (plane.pln_land >= 0){
#ifdef XLIGHT
		if (!(pcp->pl_flags & P_E))
			return 0;
#endif /* XLIGHT */

		getland(plane.pln_land,&land);
		lcp = & lchr[land.lnd_type];

#ifdef XLIGHT
		if ((pcp->pl_flags & P_E) && (lcp->l_flags & L_XLIGHT)){
			return 1;
		}
#endif /* XLIGHT */
	}

	return 0;
}

PR(cn,s)
natid	cn;
s_char	*s;
{
	register int x,newline=0;
	static s_char longline[MAXNOC][512];
	extern int ismission;

	for(x=0;x<strlen(s);x++)
		if (*(s+x) == '\n')
			newline=1;

	sprintf(&longline[cn][strlen(longline[cn])],"%s",s);

	if (newline){
		if (ismission)
			wu(0,cn,longline[cn]);
		else if (cn == cnum)
			pr(longline[cn]);
		else
			wu(0,cn,longline[cn]);
		bzero(longline[cn],512);
	}
}
PRdate(cn)
natid	cn;
{
	extern	s_char *ctime();
	long	now;

	(void) time(&now);
	PR(cn,ctime(&now));
}

do_evade(bomb_list, esc_list, mission_flags)
struct qelem *bomb_list, *esc_list;
int mission_flags;
{
	struct qelem *qp;
	double evade;
	struct plist *plp;

#ifdef STEALTHV
	evade=100.0;
        for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw){
                plp = (struct plist *) qp;
                if (evade > ((float)plp->pcp->pl_stealth/100.0))
                	evade = (plp->pcp->pl_stealth/100.0);
	}
        for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw){
                plp = (struct plist *) qp;
                if (evade > plp->pcp->pl_stealth/100.0)
                	evade = (plp->pcp->pl_stealth/100.0);
	}
#else
	evade = 0;
	/* Stealth */
	if (mission_flags & P_X)
		evade = 0.88;
#ifdef CHOPPER_STEALTH
	/* Half Stealth */
	if (mission_flags & P_H)
		evade = 0.22;
#endif /* CHOPPER_STEALTH */
#endif /* STEALTHV */

	if (chance(evade))
		return 1;

	return 0;
}
SHAR_EOF
fi
if test -f 'mission.c'
then
	echo shar: "will not over-write existing file 'mission.c'"
else
cat << \SHAR_EOF > 'mission.c'

#ifndef lint
static char *RCSid = "$Header: /users/empire/EMP/empmain/COMS/RCS/mission.c,v 1.10 89/07/02 21:03:38 mr-frog Exp $";
#endif /* not lint */

/*
 * mission.c
 *
 * mission subroutines for planes/ships/units
 *
 */

#include "options.h"
#include "misc.h"
#include "var.h"
#include "xy.h"
#include "sect.h"
#include "retreat.h"
#include "ship.h"
#include "land.h"
#include "plane.h"
#include "nat.h"
#include "nsc.h"
#include "deity.h"
#include "file.h"
#include "path.h"
#include "mission.h"
#include "genitem.h"
#include "nuke.h"
#include "news.h"
#include "item.h"
#include <fcntl.h>

struct airport{
	struct	qelem queue;
	coord	x,y;
	natid	own;
};

struct genlist {
	struct qelem queue;	/* list of units */
	int type;		/* type of unit */
	int x,y;		/* x,y it came from */
	s_char *cp;		/* pointer to desc of thing */
	s_char *thing;		/* thing's struct */
};

/*
 * Check to see if anyone hostile to the actor
 * is running an interdiction mission on this
 * sector. If so, return damage done.
 */
interdict(x,y,actor,s)
int     x,y,actor;
s_char	*s;
{
	register int cn;
	int	dam=0, newdam, rel;
	extern	int ismission;
	struct	genlist mi[MAXNOC];
	int	z;

	bzero(mi,sizeof(mi));
	for(z=1;z<MAXNOC;z++)
		initque((struct qelem *)&mi[z]);

	build_mission_list(mi,x,y,MI_INTERDICT);

	for(cn=1;cn<MAXNOC;cn++){
		rel = getrel(getnatp(cn),actor);
		if (rel != AT_WAR)
			continue;

		if (QEMPTY(&mi[cn].queue))
			continue;

		ismission = 1;
		newdam = perform_mission(x,y,actor,&mi[cn].queue,
			MI_INTERDICT,s);
		dam += newdam;
		ismission = 0;
		if (newdam)
			pr(fmt("%s interdiction mission does %d damage!\n",
				cname(cn), newdam));
	}
	return dam;
}

/*
 *  Interdict subs
 * 
 */
sub_interdict(x,y,actor,s)
int     x,y,actor;
s_char	*s;
{
	register int cn;
	int	dam=0, newdam, rel;
	extern	int ismission;
	struct	genlist mi[MAXNOC];
	int	z;

	bzero(mi,sizeof(mi));
	for(z=1;z<MAXNOC;z++)
		initque((struct qelem *)&mi[z]);

	build_mission_list(mi,x,y,MI_SINTERDICT);

	for(cn=1;cn<MAXNOC;cn++){
		rel = getrel(getnatp(cn),actor);
		if (rel != AT_WAR)
			continue;

		if (QEMPTY(&mi[cn].queue))
			continue;

		ismission = 1;
		newdam = perform_mission(x,y,actor,&mi[cn].queue,
			MI_SINTERDICT,s);
		dam += newdam;
		ismission = 0;
		if (newdam)
			pr(fmt("%s sub interdiction mission does %d damage!\n",
				cname(cn), newdam));
	}
	return dam;
}

/*
 *  Perform a mission against actor, on behalf of actee
 */
off_support(x,y,actor,actee)
int     x,y,actor,actee;
{
	int	dam=0;
	struct	genlist mi[MAXNOC];
	int	z;

	bzero(mi,sizeof(mi));
	for(z=1;z<MAXNOC;z++)
		initque((struct qelem *)&mi[z]);

	build_mission_list(mi,x,y,MI_SUPPORT);
	build_mission_list(mi,x,y,MI_OSUPPORT);

	dam = dosupport(mi,x,y,actor,actee);
	return dam;
}

/*
 *  Perform a mission against actor, on behalf of actee
 */
def_support(x,y,actor,actee)
int     x,y,actor,actee;
{
	int	dam=0;
	struct	genlist mi[MAXNOC];
	int	z;

	bzero(mi,sizeof(mi));
	for(z=1;z<MAXNOC;z++)
		initque((struct qelem *)&mi[z]);

	build_mission_list(mi,x,y,MI_SUPPORT);
	build_mission_list(mi,x,y,MI_DSUPPORT);

	dam = dosupport(mi,x,y,actor,actee);
	return dam;
}

dosupport(mi,x,y,actor,actee)
struct	genlist mi[MAXNOC];
int	x,y,actor,actee;
{
	register int cn;
	extern	int ismission;
	int	rel;
	int	dam=0;

	for(cn=1;cn<MAXNOC;cn++){
		rel = getrel(getnatp(cn),actee);
		if ((cn != actee) && (rel != ALLIED))
			continue;
		rel = getrel(getnatp(cn),actor);
		if ((cn != actee) && (rel != AT_WAR))
			continue;
	
		if (QEMPTY(&mi[cn].queue))
			continue;
	
		ismission = 1;
		dam += perform_mission(x,y,actor,&mi[cn].queue,MI_SUPPORT,
			"");
		ismission = 0;
	}
	return dam;
}

build_mission_list(mi,x,y,mission)
struct	genlist mi[MAXNOC];
int	x,y;
int	mission;
{
	build_mission_list_type(mi,x,y,mission,EF_LAND);
	build_mission_list_type(mi,x,y,mission,EF_SHIP);
	build_mission_list_type(mi,x,y,mission,EF_PLANE);
}

build_mission_list_type(mi,x,y,mission,type)
struct	genlist mi[MAXNOC];
int	x,y;
int	mission,type;
{
	struct	nstr_item ni;
	struct	genlist *glp;
	struct	genitem *gp;
	struct	plnstr *pp;
	struct	sctstr sect;
	s_char	*block;
	int	dist, size;
#ifdef SLOW_WAR
	int	rel;
#endif /* SLOW_WAR */

	ef_close(type);
	ef_open(type,O_RDWR,EFF_MEM);

	size = max(sizeof(struct shpstr),sizeof(struct lndstr));
	size = max(size,sizeof(struct plnstr));
	block = malloc(size);

	snxtitem_all(&ni,type);
	while(nxtitem(&ni, block)){
		gp = (struct genitem *)block;

		if (gp->own == 0)
			continue;

		if ((gp->mission != mission) && (mission != MI_SINTERDICT))
			continue;

		if ((gp->mission != mission) && (mission == MI_SINTERDICT) &&
			(gp->mission != MI_INTERDICT))
			continue;

		dist = mapdist(x, y, gp->opx, gp->opy);

		if (dist > gp->radius)
			continue;

		getsect(x,y,(s_char *)&sect);
#ifdef SLOW_WAR
		rel = getrel(getnatp(gp->own), sect.sct_own);
		if ((rel == HOSTILE) ||
			(rel == MOBILIZATION) ||
			(rel == SITZKRIEG)){

			/*
			 * If the owner of the unit isn't at war
			 * with the actor, and doesn't own the
			 * sect being acted upon, and isn't the
			 * old owner of that sect, bounce them.
			 */
			if ((sect.sct_type != SCT_WATER) &&
				((sect.sct_own != gp->own) &&
				(sect.sct_oldown != gp->own)) &&
				(mission != MI_AIR_DEFENSE))
				continue;
		}
#endif /* SLOW_WAR */

		if (type == EF_PLANE){
			struct	plchrstr *pcp;
			pp = (struct plnstr *)block;
			pcp = &plchr[pp->pln_type];
			if (pcp->pl_flags & P_M){
				if ((pcp->pl_flags & P_T) &&
					(sect.sct_type != SCT_WATER))
					continue;
			}
		}

		glp = (struct genlist *) malloc(sizeof(struct genlist));
		bzero((s_char *)glp,sizeof(struct genlist));
		glp->x = gp->x;
		glp->y = gp->y;
		glp->type = type;
		switch(type){
			case EF_LAND:	glp->cp = (s_char *)&lchr[gp->type];
					break;
			case EF_SHIP:	glp->cp = (s_char *)&mchr[gp->type];
					break;
			case EF_PLANE:	glp->cp = (s_char *)&plchr[gp->type];
					break;
		}
		glp->thing = malloc(size);
		bcopy(block,glp->thing,size);
		insque(&glp->queue, &mi[gp->own].queue);
	}

	ef_close(type);
	ef_open(type,O_RDWR,0);
}

find_escorts(x,y,cn,escorts)
int	x,y;
natid	cn;
struct	qelem *escorts;
{
	struct	nstr_item ni;
	struct	plist *plp;
	struct	plnstr plane;
	int	dist;

	snxtitem_all(&ni,EF_PLANE);
	while(nxtitem(&ni, (s_char *)&plane)){
		if (plane.pln_own != cn)
			continue;

		if (plane.pln_mission != MI_ESCORT)
			continue;

		dist = mapdist(x,y,plane.pln_x,plane.pln_y);

		if (dist > ((int)((float)plane.pln_range/2.0)) )
			continue;

		plp = (struct plist *) malloc(sizeof(struct plist));
		bzero((s_char *)plp,sizeof(struct plist));
		plp->pcp = &plchr[plane.pln_type];
		bcopy((s_char *)&plane,(s_char *)&plp->plane,sizeof(struct plnstr));
		insque(&plp->queue, escorts);
	}
}

perform_mission(x,y,actor,list,mission,s)
int	x,y,actor;
struct	qelem *list;
int	mission;
s_char	*s;
{
	extern	int land_max_interdiction_range;
	extern	int ship_max_interdiction_range;
	s_char	*mission_name(), *nameofitem();
	struct	qelem *qp, bombers, escorts, airp, b, e;
	struct	genlist *glp;
	struct	plist *plp;
	struct	genitem *gp;
	struct	lndstr *lp;
	struct	shpstr *sp;
	struct	sctstr sect;
	struct	lchrstr *lcp;
	struct	mchrstr *mcp;
	s_char	*bestpath();
	int	dam=0,dam2, mission_flags, tech;
	natid	plane_owner;
	double	landunitgun();
	int	gun,shell,md, air_dam=0;
	double  range2, prb, range, mobcost, hitchance;
	extern	int torpedo_damage;
	
	getsect(x,y,&sect);

	initque(&bombers);
	initque(&escorts);
	initque(&airp);

	for(qp = list->q_forw; qp != list; qp = qp->q_forw){
		glp = (struct genlist *)qp;
		gp = (struct genitem *)glp->thing;
		
		md = mapdist(x,y,gp->x,gp->y);

		if (glp->type == EF_LAND){
			lp = (struct lndstr *)glp->thing;
			lcp = (struct lchrstr *)glp->cp;

			if (mission == MI_SINTERDICT)
				continue;

			if ((mission == MI_INTERDICT) &&
				(md > land_max_interdiction_range))
				continue;

			if (md > lcp->l_frg)
				continue;

			range = techfact((int)lp->lnd_tech,
				(double)lchr[lp->lnd_type].l_frg);
			range2 = (double)roundrange(range);

			if (md > range2)
				continue;

			if (has_supply(lp)){
				use_supply(lp);
				putland(lp->lnd_uid,lp);
				dam2 = ldround(landunitgun(lp->lnd_type,
					lp->lnd_effic),1);
				if (sect.sct_type == SCT_WATER){
					double dam3 = (double)dam2;
					if (chance(((double)lcp->l_acc)/100.0))
						dam2=ldround((dam3/2.0),1);
				}
				dam += dam2;
				if (sect.sct_type == SCT_WATER)
					nreport(lp->lnd_own,N_SHP_SHELL,actor,1);
				else
					nreport(lp->lnd_own,N_SCT_SHELL,actor,1);
				wu(0,lp->lnd_own,
					fmt("%s #%d fires at %s %s at %s\n",
					lcp->l_name, lp->lnd_uid, cname(cnum),
					s, xyas(x,y,lp->lnd_own)));

				wu(0,cnum,fmt("%s %s #%d fires at you at %s\n",
					cname(lp->lnd_own), lcp->l_name,
					lp->lnd_uid, xyas(x,y,cnum)));
			}
		}else if (glp->type == EF_SHIP){
			sp = (struct shpstr *)glp->thing;
			mcp = (struct mchrstr *)glp->cp;

			if (sp->shp_effic < 60)
				continue;
			if (mcp->m_frnge == 0)
				continue;
			if (((mission == MI_INTERDICT) ||
				(mission == MI_SINTERDICT)) &&
				(md > land_max_interdiction_range))
				continue;
			if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
				continue;
			if ((mcp->m_flags & M_SUB) &&
				(sect.sct_type != SCT_WATER))
				continue;

			if (mission == MI_SINTERDICT){
				if (!(mcp->m_flags & M_SONAR))
					continue;
				if (!(mcp->m_flags & M_DCH) && 
					!(mcp->m_flags & M_TORP))
					continue;
				range2 = techfact(sp->shp_tech,
					(double)mcp->m_vrnge);
				range2 *= (double)sp->shp_effic / 200.0;
				if (md > range2)
					continue;
				/* can't look all the time */
				if (chance(0.5))
					continue;
			}
			if (mcp->m_flags & M_SUB){
				if (sp->shp_mobil < 0)
					continue;
				gun = getvar(V_GUN,(s_char *)sp,EF_SHIP);
				if (gun < 1)
					continue;
				shell = getvar(V_SHELL,(s_char *)sp,EF_SHIP);
				if (shell < 3)
					shell += supply_commod(sp->shp_own,
						sp->shp_x,sp->shp_y,I_SHELL,
						3-shell);
				if (shell < 3)
					continue;

				range = sp->shp_effic* techfact(sp->shp_tech,
       					((double)mcp->m_frnge)) / 100.0;

				range2 = (double)roundrange(range);
				if (md > range)
					continue;

				putvar(V_SHELL,shell-3,(s_char *)sp,EF_SHIP);
				mobcost = sp->shp_effic * 0.01 * mcp->m_speed;
				mobcost = (480.0/(mobcost +
					techfact(sp->shp_tech, mobcost)));
				mobcost /= 2.0;
				sp->shp_mobil -= mobcost;
				putship(sp->shp_uid,sp);
				hitchance = 0.90 / (md + 1);
				hitchance += ((5.0-(double)mcp->m_visib)
					*3.0)/100.0;

				if (!chance(hitchance))
					continue;

				dam2 = torpedo_damage +
					(random() % torpedo_damage);

				dam += dam2;
				nreport(sp->shp_own,N_TORP_SHIP,actor,1);
				wu(0,sp->shp_own,
					fmt("%s #%d torps %s %s at %s\n",
					mcp->m_name,sp->shp_uid,cname(cnum),s,
					xyas(x,y,sp->shp_own)));

				wu(0,cnum,fmt("%s %s #%d torps you at %s\n",
					cname(sp->shp_own), mcp->m_name,
					sp->shp_uid, xyas(x,y,sp->shp_own)));
			}else{
				range = techfact(sp->shp_tech,
					(double)mcp->m_frnge/2.0);
				range2 = (double)roundrange(range);
				if (md > range2)
					continue;
				gun = getvar(V_GUN, (s_char *)sp, EF_SHIP);
				gun = min(gun,  mcp->m_glim);
				shell = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
				if (shell < gun)
					shell += supply_commod(sp->shp_own,
						sp->shp_x,sp->shp_y,I_SHELL,
						gun-shell);
				gun = min(gun, shell);
				gun = min(gun,(int)((float)getvar(V_MILIT,
					(s_char *)sp,EF_SHIP)/2.0));
				if (gun ==0)
					continue;
				gun = max(gun,1);
				dam2 = shelldam(seagun(sp->shp_effic,gun),160);
				prb = ((double)md) / range2;
				prb *= prb;
				if (chance(prb))
					dam2 = (int)((float)dam2/2.0);
				dam += dam2;
				if (sect.sct_type == SCT_WATER)
					nreport(sp->shp_own,N_SHP_SHELL,actor,1);
				else
					nreport(sp->shp_own,N_SCT_SHELL,actor,1);
				wu(0,sp->shp_own,
					fmt("%s #%d fires at %s %s at %s\n",
					mcp->m_name,
					sp->shp_uid,
					cname(cnum), s,
					xyas(x,y,sp->shp_own)));

				wu(0,cnum,fmt("%s %s #%d fires at you at %s\n",
					cname(sp->shp_own), mcp->m_name,
					sp->shp_uid, xyas(x,y,cnum)));

				putvar(V_SHELL,shell-gun,(s_char *)sp, EF_SHIP);
				putship(sp->shp_uid,sp);
			}
		}else if (glp->type == EF_PLANE){
			/* save planes for later */
			plp = (struct plist *)malloc(sizeof(struct plist));

			bzero((s_char *)plp,sizeof(struct plist));
			plp->pcp = (struct plchrstr *)glp->cp;
			bcopy(glp->thing, (s_char *)&plp->plane,
				sizeof(struct plnstr));
			insque(&plp->queue, &bombers);
			plane_owner = plp->plane.pln_own;
		}
	}

	if (QEMPTY(&bombers))
		return dam;
	/*
	 * If there are planes performing an 
	 * interdict or support mission, find
	 * some escorts for them, if possible.
	 * Up to 2 per bomber, if possible.
	 */
	find_escorts(x,y,plane_owner,&escorts);

	if (mission == MI_SINTERDICT)
		mission_pln_sel(&bombers,P_T|P_A,0);
	else
		mission_pln_sel(&bombers,P_T,P_A);

	mission_pln_sel(&escorts,P_ESC|P_F,0);

	for(qp = bombers.q_forw; qp != (&bombers); qp = qp->q_forw){
		plp = (struct plist *)qp;
		if (!find_airport(&airp,plp->plane.pln_x,plp->plane.pln_y))
			add_airport(&airp,plp->plane.pln_x,
				plp->plane.pln_y);
	}

	for(qp = airp.q_forw; qp != (&airp); qp = qp->q_forw){
        	struct	airport *air;
        	s_char	*path, pp[512];

		air = (struct airport *)qp;
		
		initque(&b);
		initque(&e);

		/* Split off the bombers at this base into b */
		divide(&bombers,&b,air->x,air->y);

		/* Split off the escorts at this base into e */
		divide(&escorts,&e,air->x,air->y);

        	tech = 0;
        	mission_flags = 0;
        	mission_flags |= P_X;           /* stealth (shhh) */
#ifdef CHOPPER_STEALTH
        	mission_flags |= P_H; /* gets turned off if not all choppers */
#endif /* CHOPPER_STEALTH */

		mission_flags = mission_pln_arm(&b,air->x,air->y,'p',0,
			0,mission_flags,&tech);

		if (QEMPTY(&b)){
			continue;
		}

		mission_flags = mission_pln_arm(&e,air->x,air->y,'p',0,
			P_F|P_ESC,mission_flags,&tech);

		path = bestpath(air->x,air->y,x,y,"\0");
		bzero(pp,512);
		if (path != (s_char *)0)
			bcopy(path,pp,strlen(path));
		wu(0,air->own,fmt("\nFlying %s mission\n",
			mission_name(mission)));

		ac_encounter(&b,&e,air->x,air->y,pp,mission_flags,0);

		if (!QEMPTY(&b))
			air_dam += air_damage(&b,x,y,mission,actor,s);

		pln_put(&b);
		pln_put(&e);
	}

	if (air_dam > 0){
		dam += air_dam;
		if (sect.sct_type == SCT_WATER)
			nreport(plane_owner,N_SHP_BOMB,actor,1);
		else
			nreport(plane_owner,N_SCT_BOMB,actor,1);
	}

	/* free up all this memory */
	qp=list->q_forw;
	while(qp != list){
		glp = (struct genlist *)qp;
		qp=qp->q_forw;

		free(glp->thing);
		free((s_char *)glp);
	}

	qp=escorts.q_forw;
	while(qp != (&escorts)){
		qp=qp->q_forw;
		free(qp);
	}

	qp=bombers.q_forw;
	while(qp != (&bombers)){
		qp=qp->q_forw;
		free(qp);
	}

	return dam;
}

cando(mission,type)
int	mission, type;
{
	switch (mission){
		case MI_ESCORT:
			if (type == EF_PLANE)
				return 1;
			return 0;
		case MI_AIR_DEFENSE:
			if (type == EF_PLANE)
				return 1;
			return 0;
		case MI_SINTERDICT:
			if ((type == EF_PLANE) || (type == EF_SHIP))
				return 1;
			return 0;
		case MI_INTERDICT:
			return 1;
		case MI_SUPPORT:
		case MI_OSUPPORT:
		case MI_DSUPPORT:
			if (type == EF_PLANE)
				return 1;
			return 0;
		case MI_RESERVE:
			if (type == EF_LAND)
				return 1;
			return 0;
	}

	return 0;
}

s_char *nameofitem(gp,type)
struct	genitem *gp;
int	type;
{
	static	s_char buf[80];
	struct	shpstr *sp = (struct shpstr *)gp;
	struct	plnstr *pp = (struct plnstr *)gp;
	struct	lndstr *lp = (struct lndstr *)gp;

	switch(type){
		case EF_SHIP:
			sprintf(buf,"%s #%d", mchr[sp->shp_type].m_name,
				sp->shp_uid);
			break;
		case EF_PLANE:
			sprintf(buf,"%s #%d", plchr[lp->lnd_type].pl_name,
				pp->pln_uid);
			break;
		case EF_LAND:
			sprintf(buf,"%s #%d", lchr[lp->lnd_type].l_name,
				lp->lnd_uid);
			break;
	}
	return buf;
}

s_char *mission_short_name(mission)
{
	switch (mission){
		case MI_INTERDICT:	return "interdict";
		case MI_SUPPORT:	return "support  ";
		case MI_OSUPPORT:	return "offensive";
		case MI_DSUPPORT:	return "defensive";
		case MI_RESERVE:	return "reserve  ";
		case MI_ESCORT:		return "escort   ";
		case MI_SINTERDICT:	return "interdict";
		case MI_AIR_DEFENSE:	return "air def  ";
		default:		return "         ";
	}
}

s_char *mission_name(mission)
{
	switch (mission){
		case MI_INTERDICT:	return "an interdiction";
		case MI_SUPPORT:	return "a support";
		case MI_OSUPPORT:	return "a offensive support";
		case MI_DSUPPORT:	return "a defensive support";
		case MI_RESERVE:	return "a reserve";
		case MI_ESCORT:		return "an escort";
		case MI_SINTERDICT:	return "a sub interdiction";
		case MI_AIR_DEFENSE:	return "an air defense";
	}
}

show_mission(type,np)
int	type;
struct	nstr_item *np;
{
	int	size, first=1, radius;
	s_char	*block;
	struct	genitem *gp;
	s_char	*mission_name();

	size = max(sizeof(struct lndstr),sizeof(struct plnstr));
	size = max(size,sizeof(struct shpstr));
	block = malloc(size);

	while (nxtitem(np, block)) {
		gp = (struct genitem *)block;
		if (!owner || gp->own == 0)
			continue;
		
		if (first){
			pr(fmt("Thing                         x,y   op-sect rad mission\n"));
			first=0;
		}
		pr(fmt("%-25s", nameofitem(gp,type)));
		pr(fmt(" %7s", xyas(gp->x,gp->y,cnum)));
		if (gp->mission == MI_INTERDICT || gp->mission == MI_SUPPORT ||
			gp->mission == MI_OSUPPORT ||
			gp->mission == MI_DSUPPORT ||
			gp->mission == MI_AIR_DEFENSE){
			radius = 999;
			oprange(gp,type,&radius);
			pr(fmt(" %7s", xyas(gp->opx,gp->opy,cnum)));
			if (radius < gp->radius)
				pr(fmt("  %4d", radius));
			else
				pr(fmt("  %4d", gp->radius));
		}else if (gp->mission == MI_RESERVE){
			struct	sctstr sect;
			int	plus=2;

			getsect(gp->x,gp->y,&sect);
			if ((sect.sct_type == SCT_HEADQ) &&
				(sect.sct_effic >= 60))
				plus++;

			pr(fmt(" %7s", xyas(gp->x,gp->y,cnum)));
			pr(fmt("  %4d", lchr[gp->type].l_rad + plus));
		}else if (gp->mission == MI_ESCORT){
			pr("        ");
			pr(fmt("  %4d", (int)
				((float)((struct plnstr *)block)->pln_range/2.0)
				));
		}else
			pr("              ");
		if (gp->mission)
			pr(fmt(" is on %s mission\n",
				mission_name(gp->mission)));
		else
			pr(fmt(" has no mission.\n"));
	}
}

oprange(gp,type,radius)
struct	genitem *gp;
int	type;
int	*radius;
{
	double	techfact();
	int	range;
	struct	shpstr ship;
	struct	lndstr land;
	struct	plnstr plane;

	switch (type){
		case EF_SHIP:
			getship(gp->uid,&ship);
			range = ldround(techfact(gp->tech,
				mchr[gp->type].m_frnge / 2.0),1);
			break;
		case EF_LAND:
			getland(gp->uid,&land);
			range = ldround(techfact((int)land.lnd_tech,
				(double)lchr[land.lnd_type].l_frg),1);
			break;
		case EF_PLANE:
			getplane(gp->uid,&plane);
			range = ldround((double)plane.pln_range/2.0,1);;
			break;
	}

	if ((*radius) > range)
		*radius = range;

	return range;
}

/*
 *  Remove all planes who cannot go on
 *  the mission from the plane list.
 */
mission_pln_sel(list, wantflags, nowantflags)
	struct	qelem *list;
	int	wantflags;
	int	nowantflags;
{
	struct	qelem *qp, *next;
	struct	plnstr *pp;
	struct	shpstr ship;
	struct	lndstr land;
	struct	sctstr sect;
	struct	plchrstr *pcp;
	struct	plist *plp;
	register int	x,y,bad,bad1;

	for (qp = list->q_forw; qp != list; qp = next) {
		next = qp->q_forw;
		plp = (struct plist *)qp;
		pp = &plp->plane;
		pcp = plp->pcp;

		if (pp->pln_effic < 40){
			remque(qp);
			free((s_char *)qp);
			continue;
		}

		bad=0;
		bad1=0;
		if (wantflags) {
			for(x=0;x<sizeof(wantflags)*8;x++){
				y=(1<<x);
				if ((wantflags & y) == y)
					if ((pcp->pl_flags & y) != y){
						switch(y){
							case P_F:
							case P_ESC: bad1=2;
								    break;
#if defined(XLIGHT) || defined(SHIPCHOPPERS)
							case P_E:
							case P_L:
							case P_K: bad1=1;
								  break;
							default:  bad=1;
						}
#else
						}
						bad=1;
#endif
					}
			}
			if (bad){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
			if (bad1 == 2){
				if ((pcp->pl_flags &  P_ESC) ||
					(pcp->pl_flags & P_F))
					bad1=0;
			}
#if defined(XLIGHT) || defined(SHIPCHOPPERS)
			if (bad1 == 1){
				if ((pcp->pl_flags &  P_E) ||
					(pcp->pl_flags & P_K) ||
					(pcp->pl_flags & P_L))
					bad1=0;
			}
			if (bad1){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
#endif
		}
		bad=0;
		bad1=0;
		if (nowantflags) {
			for(x=0;x<sizeof(nowantflags)*8;x++){
				y=(1<<x);
				if ((nowantflags & y) == y)
					if ((pcp->pl_flags & y) == y)
						bad=1;
			}
			if (bad){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
		}
		if (pp->pln_ship >= 0) {
			if (!getship(pp->pln_ship, &ship)){
	shipsunk:
				pp->pln_effic = 0;
				putplane(pp->pln_uid, pp);
				remque(qp);
				free((s_char *)qp);
				continue;
			}
#if defined (SHIPCHOPPERS) || defined (XLIGHT)
			if (!can_be_on_ship(pp->pln_uid,ship.shp_uid)){
				goto shipsunk;
			}
#else
			if ((mchr[ship.shp_type].m_flags & M_FLY) == 0)
				goto shipsunk;
#endif
			if (ship.shp_effic < SHIP_MINEFF){
				goto shipsunk;
			}
			if (ship.shp_effic < 50){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
		}
		if (pp->pln_land >= 0) {
			if (!getland(pp->pln_land, &land)){
	landdead:
				pp->pln_effic = 0;
				putplane(pp->pln_uid, pp);
				remque(qp);
				free((s_char *)qp);
				continue;
			}
#ifdef XLIGHT
			if (!(pcp->pl_flags & P_E))
				goto landdead;
#endif /* XLIGHT */
			if (land.lnd_effic < LAND_MINEFF)
				goto landdead;

			if (land.lnd_effic < 50){
				remque(qp);
				free((s_char *)qp);
				continue;
			}

			/* Can't fly off units in ships */
			if (land.lnd_ship >= 0){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
		}
		/*
		 * if not vtol and not at an airport, you lose!
		 */
		if ((pcp->pl_flags & P_V) == 0 && (pp->pln_ship < 0) &&
			(pp->pln_land < 0)) {
			if (!getsect(pp->pln_x, pp->pln_y, &sect) ||
			    sect.sct_type != SCT_AIRPT){
				remque(qp);
				free((s_char *)qp);
				continue;
			}

			if (sect.sct_effic < 40){
				remque(qp);
				free((s_char *)qp);
				continue;
			}
		}

		putplane(pp->pln_uid, pp);
	}
}

/*
 * Arm only the planes at x,y
 *
 */
int
mission_pln_arm(list, x,y, mission, ip, flags, mission_flags, tech)
	struct	qelem *list;
	int	x,y;
	int	mission;
	struct	ichrstr *ip;
	int	flags;
	int	mission_flags;
	int	*tech;
{
	struct	qelem *qp;
	struct	qelem *next;
	struct	plist *plp;

	if (*tech == 0)
		*tech = 9999;
	for (qp = list->q_forw; qp != list; qp = next) {
		next = qp->q_forw;
		plp = (struct plist *) qp;

		if (plp->plane.pln_x != x)
			continue;
		if (plp->plane.pln_y != y)
			continue;

		if (mission_pln_equip(plp, ip, flags, mission) < 0) {
			remque(qp);
			free((s_char *)qp);
			continue;
		}
		if (flags & (P_S|P_I)) {
			if(plp->pcp->pl_flags & P_S)
				mission_flags |= P_S;
			if(plp->pcp->pl_flags & P_I)
				mission_flags |= P_I;
		}
		if (*tech > plp->plane.pln_tech)
			*tech = plp->plane.pln_tech;
#ifdef CHOPPER_STEALTH
		if (!(plp->pcp->pl_flags & P_H))
			/* no stealth on this mission */
			mission_flags &= ~P_H;
#endif /* CHOPPER_STEALTH */
		if (!(plp->pcp->pl_flags & P_X))
			/* no stealth on this mission */
			mission_flags &= ~P_X;
#ifdef ASW_PLANES
		if (!(plp->pcp->pl_flags & P_A)) {
			/* no asw on this mission */
			mission_flags &= ~P_A;
		}
#endif /* ASW_PLANES */
#ifdef MINE_PLANES
		if (!(plp->pcp->pl_flags & P_MINE)) {
			/* no asw on this mission */
			mission_flags &= ~P_MINE;
		}
#endif /* MINE_PLANES */

		/*
		 *	Mob costs for missions are 1/2 normal
		 */
#ifdef MERC
		if ((flags & P_F) || (flags & P_ESC))
			plp->plane.pln_mobil -= (s_char) (min(32 + plp->plane.pln_mobil,(12 * 100 / (s_char) plp->plane.pln_effic))/4);
		else
			plp->plane.pln_mobil -= (s_char) (min(32 + plp->plane.pln_mobil,(20 * 100 / (s_char) plp->plane.pln_effic))/4);
#else
		if ((flags & P_F) || (flags & P_ESC))
			plp->plane.pln_mobil -= 3;
		else
			plp->plane.pln_mobil -= 5;
#endif
	}
	return mission_flags;
}

int
mission_pln_equip(plp, ip, flags, mission)
	struct	plist *plp;
	struct	ichrstr *ip;
	int	flags;
	s_char	mission;
{
	register struct plchrstr *pcp;
	struct	plnstr *pp;
	int	needed;
	struct	lndstr land;
	struct	shpstr ship;
	struct	sctstr sect;
	int	type;
	s_char	*ptr;
	int	item;
	int	rval;
	int	vec[I_MAX+1];

	pp = &plp->plane;
	pcp = plp->pcp;
	if (pp->pln_ship >= 0) {
		getship(pp->pln_ship, &ship);
		type = EF_SHIP;
		ptr = (s_char *) &ship;
	} else
	if (pp->pln_land >= 0) {
		getland(pp->pln_land, &land);
		type = EF_LAND;
		ptr = (s_char *) &land;
	} else {
		getsect(pp->pln_x, pp->pln_y, &sect);
		type = EF_SECTOR;
		ptr = (s_char *) &sect;
	}
	getvec(VT_ITEM, vec, ptr, type);
	if (pcp->pl_fuel > vec[I_PETROL]) {
		return -1;
	}
	vec[I_PETROL] -= pcp->pl_fuel;
	rval = 0;
	if ((flags & P_F) == 0) {
		item = 0;
		needed = 0;
		switch (mission) {
		case 's':
		case 'p':
			item = I_SHELL;
			needed = pcp->pl_load;
			break;
		case 't':
			if ((pcp->pl_flags & P_C) == 0 || ip == 0)
				break;
			item = ip - ichr;
			needed = (pcp->pl_load * 2) / ip->i_lbs;
			break;
		case 'd':
			if ((pcp->pl_flags & P_C) == 0 || ip == 0)
				break;
			item = ip - ichr;
			needed = (pcp->pl_load * 2) / ip->i_lbs;
			break;
		case 'a':
			if ((pcp->pl_flags & (P_V|P_C)) == 0)
				break;
			item = I_MILIT;
			needed = pcp->pl_load / ip->i_lbs;
			break;
		case 'n':
			if (pp->pln_nukeamt == 0)
				rval = -1;
			break;
		default:
			break;
		}
		if (rval < 0 || (item && needed <= 0)) {
			return -1;
		}
		if ((vec[item] < needed) && (item == I_SHELL))
			vec[item] += supply_commod(plp->plane.pln_own,
					plp->plane.pln_x,plp->plane.pln_y,
					I_SHELL,needed);
		if (vec[item] < needed) {
			return -1;
		} else {
			vec[item] -= needed;
		}
		if (item == I_SHELL && (mission == 's' || mission == 'p'))
			plp->bombs = needed;
		else
			plp->misc = needed;
	}
		putvec(VT_ITEM, vec, ptr, type);
	if (type == EF_SHIP)
		putship(ship.shp_uid,&ship);
	else if (type == EF_LAND)
		putland(land.lnd_uid,&land);
	else
		putsect(&sect);
	return rval;
}

/*
 *  Return 1 if this x,y pair is in the list
 */
find_airport(airp,x,y)
struct	qelem *airp;
int	x,y;
{
	struct	qelem *qp;
	struct	airport *a;

	for (qp = airp->q_forw; qp != airp; qp = qp->q_forw){
		a = (struct airport *)qp;
		if ((a->x == x) && (a->y == y))
			return 1;
	}

	return 0;
}

add_airport(airp,x,y)
struct	qelem *airp;
int	x,y;
{
	struct	airport *a;
	struct	sctstr sect;

	a = (struct airport *)malloc(sizeof(struct airport));

	a->x = x;
	a->y = y;
	getsect(x,y,&sect);
	a->own = sect.sct_own;

	insque((struct qelem *)a,airp);
}

/*
 *  Take all the planes in list 1 that
 *  are at x,y, and put them into list 2.
 */
divide(l1,l2,x,y)
struct	qelem *l1, *l2;
int	x,y;
{
	struct	qelem *qp, *next;
	struct	plist *plp;

	for (qp = l1->q_forw; qp != l1; qp = next){
		next = qp->q_forw;
		plp = (struct plist *)qp;

		if (plp->plane.pln_x != x)
			continue;
		if (plp->plane.pln_y != y)
			continue;

		remque(qp);
		insque(qp,l2);
	}
}

air_damage(bombers,x,y,mission,hurtee,s)
struct	qelem *bombers;
int	x,y,mission;
int	hurtee;
s_char	*s;
{
	struct	qelem *qp;
	struct	plist *plp;
	struct	plnstr *pp;
	struct	sctstr sect;
	int	newdam,dam=0, acc, bombs;

	getsect(x,y,&sect);
	for(qp = bombers->q_forw; qp != bombers; qp = qp->q_forw){
		plp = (struct plist *)qp;
		pp = &plp->plane;

		if ((mission == MI_SINTERDICT) && !(plp->pcp->pl_flags & P_A))
			continue;

		if ((mission == MI_SINTERDICT) &&
			!chance((100.0-(double)plp->pcp->pl_acc)/100.0)){
			wu(0,pp->pln_own,
				fmt("\t%s #%d unable to find %s %s\n",
				plp->pcp->pl_name, pp->pln_uid,
				cname(hurtee), s));
			continue;
		}

		acc = plp->pcp->pl_acc;
		bombs = plp->bombs;
                acc += dchr[sect.sct_type].d_dstr * 2;
                newdam = 0;
                while(bombs--){
                	if (roll(100) > acc)
                                newdam += (roll(8)+6);
                	else
                                newdam += (int)((double)(roll(8)+6)/3.0);
                }
		wu(0,pp->pln_own,
			fmt("\t%s #%d does %d damage to %s %s\n",
			plp->pcp->pl_name, pp->pln_uid, newdam,
			cname(hurtee), s));
		dam += newdam;
	}

	return dam;
}

/*
 * Check to see if anyone hostile to the actor
 * is running an air defense mission on this
 * sector. If so, do air combat
 */
air_defense(x,y,actor,bomb_list,esc_list)
int     x,y,actor;
struct	qelem *bomb_list;
struct	qelem *esc_list;
{
	s_char	*mission_name(), *nameofitem();
	s_char	*path, *bestpath();
	int	dam=0,cn;
	extern	int ismission;
	int	mission_flags, tech, combat=0, rel, dist, z;
	struct	qelem *qp, interceptors, airp, i, empty;
	struct	plist *plp;
	struct	genlist *glp;
	struct	genitem *gp;
	struct	genlist mi[MAXNOC];

	bzero(mi,sizeof(mi));
	for(z=1;z<MAXNOC;z++)
		initque((struct qelem *)&mi[z]);

	build_mission_list_type(mi,x,y,MI_AIR_DEFENSE,EF_PLANE);

	for(cn=1;cn<MAXNOC;cn++){

		rel = getrel(getnatp(cn),actor);
		if (rel != AT_WAR)
			continue;

		if (QEMPTY(&mi[cn].queue))
			continue;

		initque(&interceptors);
		for(qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = qp->q_forw){
			glp = (struct genlist *)qp;
			gp = (struct genitem *)glp->thing;
			plp = (struct plist *)qp;

			dist = mapdist(x,y,gp->x,gp->y) * 2;

			plp = (struct plist *)malloc(sizeof(struct plist));
			bzero((s_char *)plp,sizeof(struct plist));
			plp->pcp = (struct plchrstr *)glp->cp;
			bcopy(glp->thing, (s_char *)&plp->plane,
				sizeof(struct plnstr));

			if (dist > plp->plane.pln_range){
				free(plp);
				continue;
			}
			insque(&plp->queue, &interceptors);
		}

		mission_pln_sel(&interceptors,P_F,0);

		if (QEMPTY(&interceptors))
			continue;

		initque(&airp);
		for(qp=interceptors.q_forw;qp!=(&interceptors);qp=qp->q_forw){
			plp = (struct plist *)qp;
			if (!find_airport(&airp,plp->plane.pln_x,
				plp->plane.pln_y))
				add_airport(&airp,plp->plane.pln_x,
					plp->plane.pln_y);
		}

		for(qp = airp.q_forw; qp != (&airp); qp = qp->q_forw){
        		struct	airport *air;

			air = (struct airport *)qp;
		
			initque(&i);

			/* Split off the interceptors at this base into i */
			divide(&interceptors,&i,air->x,air->y);

        		tech = 0;
        		mission_flags = 0;
        		mission_flags |= P_X;           /* stealth (shhh) */
#ifdef CHOPPER_STEALTH
			/* gets turned off if not all choppers */
        		mission_flags |= P_H;
#endif /* CHOPPER_STEALTH */

			mission_flags = mission_pln_arm(&i,air->x,air->y,'r',0,
				P_F,mission_flags,&tech);

			if (QEMPTY(&i)){
				continue;
			}

			path = bestpath(air->x,air->y,x,y,"\0");
			wu(0,air->own,fmt("\nFlying %s mission\n",
				mission_name(MI_AIR_DEFENSE)));
			initque(&empty);
			ac_encounter(&i,&empty,air->x,air->y,
				path,mission_flags,1);

			if (QEMPTY(&i))
				continue;

			combat = 0;
			if (!QEMPTY(esc_list)){
				pr(fmt("\n%s air defense planes intercept!\n",
					cname(cn)));
#ifdef NEWPAF
				ac_combat_headers(cnum, cn);
#endif
				ac_airtoair(esc_list,&i,air->own);
				combat = 1;
			}
			if (!QEMPTY(bomb_list)){
				if (!combat) {
					pr(fmt("\n%s air defense planes intercept!\n",
						cname(cn)));
#ifdef NEWPAF
					ac_combat_headers(cnum, cn);
#endif
				}
				ac_airtoair(bomb_list,&i,air->own);
			}

			pln_put(&i);
		}
	}
	return dam;
}


SHAR_EOF
fi
exit 0
#	End of shell archive
