#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
 */

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

static	getilist();

#define	DOG_MAX		15
#define FLAK_MAX	15

		/*       -7    -6    -5    -4    -3    -2    -1    0 */
float dogtable[16] = {	0.10, 0.15, 0.28, 0.45, 0.60, 0.75, 0.82, 1.00,
			1.10, 1.25, 1.50, 1.80, 2.15, 2.55, 3.00, 3.50, };
float flaktable[16] = {	0.30, 0.30, 0.35, 0.40, 0.40, 0.45, 0.50, 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, tech)
	struct	qelem *bomb_list;
	struct	qelem *esc_list;
	coord	x;
	coord	y;
	char	*path;
	int	mission_flags;
	int	*tech;
{
	int	val;
	int	rel;
	int	dir;
	int	nats[MAXNOC];
	int	gotilist[MAXNOC];
	int	unfriendly[MAXNOC];
	int	overfly[MAXNOC];
	struct	qelem ilist[MAXNOC];
#ifdef BIGINT
	int	xxx;
	int	tot;
#endif /* BIGINT */
	int	civ, mil;
	struct	sctstr sect;
	double	evade;
	double	tlev;
	double	diff;
	struct	shpstr ship;
	struct	nstr_item ni;
	natid	cn;
	struct	natstr *over, *mynatp;
#ifdef STEALTHV
	double	evade2;
	struct	qelem *qp;
	struct	plist *plp;
#endif /* STEALTHV */
#ifdef ASW_PLANES
	struct	shiplook head;
	struct	shiplook *s,*s2;

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

	bzero((char *)overfly, sizeof(overfly));
	bzero((char *)gotilist, sizeof(gotilist));
	bzero((char *)unfriendly, sizeof(unfriendly));
#ifdef	INCARRANGE
	unfriendly[0] = 1;
#endif	INCARRANGE
	for (cn=1; cn<MAXNOC; cn++) {
		if ((mynatp=getnatp(cn)) == 0)
			continue;
		rel = getrel(mynatp, cnum);
#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 (cnum == cn)
			continue;
		unfriendly[cn]++;
	}
	if (mission_flags & PM_R) {
		if (mission_flags & P_S) {
			pr("\nSPY Plane report\n");
			prdate();
			sathead();
#ifdef ASW_PLANES
		} else if (mission_flags & P_A){
			pr("\nAnti-Sub Patrol report\n");
#endif /* ASW_PLANES */
		} else {
			pr("\nReconnaissance report\n");
			prdate();
		}
	}
#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);
	}
#endif /* STEALTHV */
	pln_removedupes(bomb_list, esc_list);
	while ((dir = *path++) && !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);
#ifndef STEALTHV
		if (unfriendly[sect.sct_own])
			if (mission_flags & P_X) {
				/* Stealth */
				tlev = *tech;
				diff = tlev - over->nat_level[NAT_TLEV];
				evade = 0.88 + 0.12 * diff / tlev;
				if (!chance(evade))
					ac_doflak(bomb_list, &sect);
			} else
#ifdef CHOPPER_STEALTH
			if (mission_flags & P_H) {
				/* Stealth */
				tlev = *tech;
				diff = tlev - over->nat_level[NAT_TLEV];
				evade = 0.22 + 0.06 * diff / tlev;
				if (!chance(evade))
					ac_doflak(bomb_list, &sect);
			} else
#endif /* CHOPPER_STEALTH */
				ac_doflak(bomb_list, &sect);
#else
		if (unfriendly[sect.sct_own])
			if (!chance(evade))
				ac_doflak(bomb_list, &sect);
#endif /* STEALTHV */
		/* mission planes aborted due to flak -- don't send escorts */
		if (QEMPTY(bomb_list))
			break;
		if (mission_flags & PM_R) {
			if (sect.sct_type == SCT_WATER) {
#ifdef SWEEP_PLANES
				plane_sweep(bomb_list,x,y,&head);
#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)
				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);
			} else {
				/* This is borrowed from lookout */
				if (sect.sct_own == cnum)
					pr("Your ");
				else
					pr(fmt("%s (#%d) ", cname(sect.sct_own),
						sect.sct_own));
				pr(dchr[sect.sct_type].d_name);
#if	defined(BMAP) || defined(AUTONAV)
				setbigmap(sect.sct_x, sect.sct_y,
					dchr[sect.sct_type].d_mnem);
#endif
				pr(fmt(" %d%% efficient ",
					owner ? sect.sct_effic :
					round((int)sect.sct_effic,25)));
				civ = getvar(V_CIVIL, (char *)&sect, EF_SECTOR);
				mil = getvar(V_MILIT, (char *)&sect, EF_SECTOR);
				if (civ)
					pr(fmt("with %s%d civ ",
					    owner ? "" : "approx ",
					    owner? civ : round(civ, 25)));
				if (mil)
					pr(fmt("with %s%d mil ",
						owner ? "" : "approx ",
						owner? mil : round(mil, 25)));
				pr(fmt("@ %s\n", xyas(x, y, cnum)));
			}
		} else {
			pr(fmt("flying over %s at %s\n",
				dchr[sect.sct_type].d_name,
				xyas(x, y, cnum)));
#if	defined(BMAP) || defined(AUTONAV)
			setbigmap(sect.sct_x, sect.sct_y,
				dchr[sect.sct_type].d_mnem);
#endif
		}
#ifndef BIGINT
#ifdef	INCARRANGE
		if (sect.sct_own == cnum)
#else
		if (sect.sct_own == 0 || sect.sct_own == cnum)
#endif	INCARRANGE
			continue;
#endif /* BIGINT */
#ifndef STEALTHV
		if (mission_flags & P_X) {
			/* Stealth */
			tlev = *tech;
			diff = tlev - over->nat_level[NAT_TLEV];
			evade = 0.88 + 0.12 * diff / tlev;
			if (chance(evade))
				continue;
		}
#ifdef CHOPPER_STEALTH
		if (mission_flags & P_H) {
			/* Stealth */
			tlev = *tech;
			diff = tlev - over->nat_level[NAT_TLEV];
			evade = 0.22 + 0.06 * diff / tlev;
			if (chance(evade))
				continue;
		}
#endif /* CHOPPER_STEALTH */
#else
pr(fmt("    Evasion chance is %d%%\n",(int)(evade*100.0)));
		if (chance(evade))
			continue;
#endif /* STEALTHV */
#ifdef BIGINT
		new_getilist(ilist, cnum,sect.sct_own);
#else
		if (unfriendly[sect.sct_own] && !gotilist[sect.sct_own]) {
			getilist(&ilist[sect.sct_own], sect.sct_own);
			gotilist[sect.sct_own]++;
		}
#endif /* BIGINT */
#ifdef BIGINT
		if (sect.sct_own){
#endif /* BIGINT */
		if ((rel = getrel(over, cnum)) == ALLIED)
			continue;
		overfly[sect.sct_own]++;
#ifdef BIGINT
		}
#endif /* BIGINT */
#ifdef	INCARRANGE
		if (sect.sct_own) {
#endif	INCARRANGE
		wu(0, sect.sct_own, fmt("%s planes spotted over %s",
			cname(cnum), xyas(x, y, sect.sct_own)));
#ifdef BIGINT
		if (sect.sct_own){
#endif /* BIGINT */
		if (rel == NEUTRAL)
			continue;
#ifdef BIGINT
		}
#endif /* BIGINT */
#ifdef	INCARRANGE
		};
#endif	INCARRANGE
#ifdef BIGINT
		tot=0;
		for(xxx=0;xxx<MAXNOC;xxx++)
        		if (!QEMPTY(&ilist[xxx]))
        			tot++;

		while (tot){
			xxx=roll(MAXNOC)-1;
        		if (!QEMPTY(&ilist[xxx])){
				pr(fmt("%s planes intercepting!\n",cname(xxx)));
				ac_intercept(bomb_list, esc_list, &ilist[xxx],
					sect.sct_own, x, y);
				ilist[xxx].q_forw = &ilist[xxx];
				ilist[xxx].q_back = &ilist[xxx];
				tot--;
			}
		}
#else
		ac_intercept(bomb_list, esc_list, &ilist[sect.sct_own],
			sect.sct_own, x, y);
#endif /* BIGINT */
	}
#if	defined(BMAP) || defined(AUTONAV)
	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((char *)nats, sizeof(nats));
	snxtitem_xy(&ni, EF_SHIP, x, y);
	while (nxtitem(&ni, (char *)&ship)) {
		if (mchr[ship.shp_type].m_flags & M_SUB)
			continue;
		nats[ship.shp_own]++;
	}
	if (!chance(evade))
		ac_shipflak(bomb_list, x, y);
	for (cn=1; cn<MAXNOC && !QEMPTY(bomb_list); cn++) {
		if (cnum == cn)
			continue;
		if (overfly[cn] > 0)
			nreport(cnum, N_OVFLY_SECT, cn, overfly[cn]);
		if (nats[cn] == 0)
			continue;
#ifdef MERC
		if(cn != 0)
#endif
		wu(0, cn, fmt("%s planes spotted over ships in %s",
			cname(cnum), xyas(x, y, cn)));
		if (!unfriendly[cn])
			continue;
#ifdef BIGINT
		new_getilist(ilist, cnum,sect.sct_own);
#else
		if (!gotilist[cn]) {
			getilist(&ilist[cn], cn);
			gotilist[cn]++;
		}
#endif /* BIGINT */
		pr(fmt("Flying over %d #%d ships...\n", nats[cn], cn));
		/* This makes going for ships in harbors tough */
#ifdef BIGINT
                tot=0;
                for(xxx=0;xxx<MAXNOC;xxx++)
        		if (!QEMPTY(&ilist[xxx]))
                                tot++;

                while (tot){
                        xxx=roll(MAXNOC)-1;
        		if (!QEMPTY(&ilist[xxx])){
				if (!chance(evade)){
					pr(fmt("%s planes intercepting!\n",cname(xxx)));
					ac_intercept(bomb_list, esc_list, &ilist[xxx], cn, x, y);
				}
				ilist[xxx].q_forw = &ilist[xxx];
				ilist[xxx].q_back = &ilist[xxx];
                                tot--;
                        }
		}
#else
		if (!chance(evade))
			ac_intercept(bomb_list, esc_list, &ilist[cn], cn, x, y);
#endif /* BIGINT */
	}
#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	sctstr sect;
	struct	qelem *next;
	struct	qelem *qp;
	struct	qelem int_list;
	char	*ptr;
	int	type;
	int	att_count;
#ifdef	SHIPINTER
	int	fromship;
#endif	SHIPINTER

	icount = 0;
	att_count = 0;
#ifdef	SHIPINTER
	fromship = 0;
#endif	SHIPINTER
	for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw)
		att_count++;
	for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw)
		att_count++;
	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 = (char *) &ship;
#ifdef	INCARRANGE
			if (!def_own) {
			    int	range;

			    range = (int)(techfact(ship.shp_tech,
				(double) mchr[ship.shp_type].m_vrnge) *
				ship.shp_effic);
			    if (range < mapdist(x, y, pp->pln_x, pp->pln_y))
					continue;
			}
#endif	INCARRANGE
		} else {
			getsect(pp->pln_x, pp->pln_y, &sect);
			type = EF_SECTOR;
			ptr = (char *) &sect;
		}
		gas = getvar(V_PETROL, ptr, type);
		if (gas < (pcp->pl_fuel/2)) {
			remque(qp);
			free((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) {
#ifdef	SHIPINTER
			fromship++;
#endif	SHIPINTER
			putship(pp->pln_ship, &ship);
		}
		else
			putsect(&sect);
		icount++;
#ifdef	SHIPINTER
		if ((icount - fromship) > att_count)
#else
		if (icount > att_count)
#endif	SHIPINTER
			break;
	}
	if (icount == 0)
		return;
#ifdef BIGINT
#else
	pr(fmt("%d %s fighter%s rising to intercept!\n", icount,
		cname(def_own), icount == 1 ? " is" : "s are"));
#endif /* BIGINT */
	ac_airtoair(esc_list, &int_list, def_own);
	ac_airtoair(bomb_list, &int_list, def_own);
#ifdef	SHIPINTER
	for (qp = int_list.q_forw; qp != &int_list; qp = qp->q_forw) {
		ip = (struct plist *) qp;
		pp = &ip->plane;
		pcp = ip->pcp;
		if (pp->pln_ship >= 0) {
			getship(pp->pln_ship, &ship);
			type = EF_SHIP;
			ptr = (char *) &ship;
			pp->pln_mobil += 10;
			putplane(pp->pln_uid, pp);
			gas = getvar(V_PETROL, ptr, type);
			putvar(V_PETROL,gas - (pcp->pl_fuel/2),ptr,type);
		}
	}
#endif	SHIPINTER
}

/*
 * air-to-air combat.
 * The interceptors aren't affected by efficiency loss; they
 * always engage at 1:1 odds.
 */
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;
	char	*mesg;
	struct	qelem *att_next;
	struct	qelem *in_next;

	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;
		interceptor = (struct plist *) in;
		mesg = fmt("  %s %s (#%d) intercepts %s %s",
			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(mesg); pr("\n");
#ifdef MERC
		if(def_own != 0)
#endif
		wu(0, def_own, mesg);
		adam = idam = 0;
		nplanes = attacker->plane.pln_effic;
		if (nplanes > interceptor->plane.pln_effic)
			nplanes = interceptor->plane.pln_effic;
		if (interceptor->plane.pln_att)
			adam = ac_dog(interceptor, attacker);
		if (interceptor->pcp->pl_flags & P_M)
			idam = 100;
		else if (attacker->plane.pln_att)
			idam = ac_dog(attacker, interceptor);
		if (adam > 0)
			ac_planedamage(attacker, interceptor->plane.pln_own,
				adam, def_own,1);
		if (idam > 0)
			ac_planedamage(interceptor, attacker->plane.pln_own,
				idam, def_own,1);
#ifdef	SHIPINTER
		if ((in_next->q_back != in) || (interceptor->plane.pln_ship < 0))
#endif	SHIPINTER
		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;
		}
	}
}

#define MIN(x,y) (x < y ? x : y)
#define MAX(x,y) (x < y ? y : x)
int
ac_dog(ap, dp)
	register struct plist *ap;
	register struct plist *dp;
{
	int	n;
	float	mult;
	int	diff, att, def;

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

	diff = (att - def) / 2;

        if ((ap->pcp->pl_flags & P_F) && ap->bombs != 0)
                diff -= 2;
        if ((dp->pcp->pl_flags & P_F) && dp->bombs != 0)
                diff += 2;
#ifndef STEALTHV
	if (ap->pcp->pl_flags & P_X)
		diff += 3;
	if (dp->pcp->pl_flags & P_X)
		diff -= 3;
#else
	diff += (ap->pcp->pl_stealth - dp->pcp->pl_stealth)/25;
#endif /* STEALTHV */
	diff += 7;
	if (diff > 15)
		mult = dogtable[DOG_MAX] * 1.33;
	else if (diff < 0)
		mult = 0.66 * dogtable[0];
	else
		mult = dogtable[diff];
	n = roll(33);
	n *= mult;
	if (n > 100)
		n = 100;
	return n;
}

/*
 * 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 cnum.
 * 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)
 */
ac_planedamage(plp, from, dam, def_own, checkabort)
	struct	plist *plp;
	natid	from;
	int	dam;
	natid	def_own;
	int	checkabort;
{
	register struct plnstr *pp;
	int	disp;
	char	*dmess, buf[80];
	int	eff;
	char	*mesg;
	struct shpstr ship;

	disp = 0;
	pp = &plp->plane;
	eff = pp->pln_effic;
	if ((dam <= 0) && (checkabort != 2))
		return;
	dmess = "";
	eff -= dam;
	if (eff < 0)
		eff = 0;
	if (eff < 10) {
		dmess = " -- shot down";
		disp = 1;
	} else if (chance((100-eff)/100.0) && checkabort){
		dmess = (char *)sprintf(buf," -- aborted with %d%% damage", 100-eff);
		disp = 2;
	}
	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);
			pr(mesg); pr("\n");
		} else{
			if (disp > 0){
				mesg = fmt("    %s %s (#%d)%s",
					cname(pp->pln_own),
					plp->pcp->pl_name, pp->pln_uid, dmess);
				pr(mesg); pr("\n");
			}
		}
#ifdef MERC
		if(def_own != 0)
#endif
		wu(0, def_own, mesg);
	}
	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);
pr(fmt("Taking plane %d off ship %d = %d\n",pp->pln_uid,ship.shp_uid,
			take_plane_off_ship(pp,&ship)));
		}
		pp->pln_own = 0;
		putplane(pp->pln_uid, pp);
		remque(&plp->queue);
		free((char *)plp);
	} else if (disp == 2) {
		putplane(pp->pln_uid, pp);
		remque(&plp->queue);
		free((char *)plp);
	} else
		putplane(pp->pln_uid, pp);
}

ac_doflak(list, from)
	struct	qelem *list;
	struct	sctstr *from;
{
	int	shell;
	int	gun;

	shell = getvar(V_SHELL, (char *)from, EF_SECTOR);
	gun = getvar(V_GUN, (char *)from, EF_SECTOR);
	if (gun == 0 || shell == 0)
		return;
	if (gun > shell*2)
		gun = shell*2;
	if (gun > 14)
		gun = 14;
	putvar(V_SHELL, shell - gun/2, (char *)from, EF_SECTOR);
	gun = 2.0 * tfact(from->sct_own, (double) gun);
	if (gun > 0) {
		pr(fmt("firing %d flak guns in %s...\n",
			gun, xyas(from->sct_x, from->sct_y, cnum)));
#ifdef MERC
		if(from->sct_own != 0)
#endif
		wu(0, from->sct_own, fmt("firing %d flak guns in %s...",
			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;
	char	*msg;
	struct	qelem *qp;
	struct	qelem *next;
	struct  plist *plp;
	natid	from=0;
	int	fired=0;

	snxtitem_xy(&ni, EF_SHIP, x, y);
	while (!QEMPTY(list) && nxtitem(&ni, (char *)&ship)) {
		if (ship.shp_own == 0 || ship.shp_own == cnum)
			continue;
		mcp = &mchr[ship.shp_type];
		if (mcp->m_flags & M_SUB)
			continue;
		rel = getrel(getnatp(ship.shp_own), cnum);
#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, (char *)&ship, EF_SHIP), mcp->m_glim);
		shell = getvar(V_SHELL, (char *)&ship, EF_SHIP);
		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)...",
			firing, mcp->m_name, ship.shp_name, ship.shp_uid);
#else
		msg = fmt("firing %d flak guns from %s #%d...",
			firing, mcp->m_name, ship.shp_uid);
#endif	SHIPNAMES
		pr(msg); pr("\n");
#ifdef MERC
		if(ship.shp_own != 0)
#endif
		wu(0, ship.shp_own, msg);
		from = ship.shp_own;
		ac_fireflak(list, ship.shp_own, firing);
		putvar(V_SHELL, shell - 1, (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);
		}
}

/*
 * Called from shipflak 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;

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

/*
 * 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	sctstr sect;
	struct	nstr_item ni;
	int	type;
	char	*ptr;
	struct	plist *ip;

	initque(list);
	snxtitem_all(&ni, EF_PLANE);
	while (nxtitem(&ni, (char *)&plane)) {
#ifdef	INCARRANGE
		if (own && (plane.pln_own != own))
#else
		if (plane.pln_own != own)
#endif	INCARRANGE
			continue;
		pcp = &plchr[plane.pln_type];
		if ((pcp->pl_flags & P_F) == 0)
			continue;
		if (plane.pln_mobil <= 0)
			continue;
		if (plane.pln_effic < 40)
			continue;
#ifdef	INCARRANGE
		if (!own) {
			int	rel;

			rel = getrel(getnatp(plane.pln_own), cnum);
#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 (cnum == plane.pln_own)
				continue;
		}
#endif	INCARRANGE
		if (plane.pln_ship >= 0) {
			if (!can_fly(plane.pln_uid))
				continue;
			getship(plane.pln_uid,&ship);
			ptr = (char *)&ship;
			type = EF_SHIP;
		} else {
			getsect(plane.pln_x, plane.pln_y, &sect);
			ptr = (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 (getvar(V_PETROL, ptr, type) < (pcp->pl_fuel/2))
			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((char *)&plane, (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 	sctstr sect;
	struct	nstr_item ni;
	int	type;
	char	*ptr;
	struct	plist *irv;

	initque (list);
	snxtitem_all (&ni, EF_PLANE);
	while (nxtitem (&ni, (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 = (char *)&ship;
			type = EF_SHIP;
		} else {
			getsect (plane.pln_x, plane.pln_y, &sect);
			ptr = (char *)&sect;
			type = EF_SECTOR;
			if ((sect.sct_effic < 60 || sect.sct_type != SCT_AIRPT)
			&& (pcp->pl_flags & P_V) == 0)
				continue;
		}
		if (getvar(V_PETROL, ptr, type) < (pcp->pl_fuel/2))
			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((char *)&plane, (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	natstr  *over;
	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	shpstr	pship;
	int 	icount = 0;
	int 	type;
	int	gas;
	char	*ptr;
	short	destroyed = 0;
	

	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
	wu(0, sect.sct_own, fmt("%s warhead incoming on sector %s", 
		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;
		if (pp->pln_range/2 < mapdist(x, y, pp->pln_x, pp->pln_y))
			continue;
		if (pp->pln_ship >= 0) {
			getship(pp->pln_ship, &pship);
			type = EF_SHIP;
			ptr = (char *)&pship;
		} else {
			getsect (pp->pln_x, pp->pln_y, &psect);
			type = EF_SECTOR;
			ptr = (char *)&psect;
		}
		gas = getvar(V_PETROL, ptr, type);
		if (gas < (pcp->pl_fuel/2)) {
			remque(qp);
			free((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 putsect(&psect);
		/* only allow two abms per warhead */
		if (++icount == 2)
			break;
	}
	if (icount == 0) {
#ifdef MERC
		if(sect.sct_own != 0)
#endif
 		wu (0, 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
		    wu(0, 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
		    wu(0, sect.sct_own, fmt("%s abm launched in defense!\n",
			cname(interceptor->plane.pln_own)));
		    wu(0, interceptor->plane.pln_own,
			fmt("abm launched to defend %s!\n",
				cname(sect.sct_own)));
#ifdef MERC
			}
#endif
#else
			remque(&interceptor->queue);
			free((char *)interceptor);
			in=in->q_forw;
			continue;
#endif /* BABY_NUKES */
		}
#endif	BETTERABM
		if (chance(((double)interceptor->plane.pln_att/100.0)) &&
			!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
			wu(0, 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
				wu(0, 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((char *)interceptor);
#else
		ac_planedamage(interceptor, bombown, 100, sect.sct_own, 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)
		wu(0, sect.sct_own, fmt("Abm interception failed.\n"));
	return (destroyed);
}

#endif	ABM

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

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

#ifdef TOM_DEBUG
pr(fmt("Plane %d trying to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */
	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;

#ifdef TOM_DEBUG
pr(fmt("Plane %d ok to try to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */

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

	if ((pcp->pl_flags & P_L) && (scp->m_flags & M_FLY)){
#ifdef TOM_DEBUG
pr(fmt("Plane %d light plane ok to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */
		return 1;
	}
	
#ifdef SHIPSAMS
	if ((pcp->pl_flags & P_M) && (scp->m_flags & M_MSL)){
#ifdef TOM_DEBUG
pr(fmt("Plane %d missile plane ok to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */
		return 1;
	}
#endif /* SHIPSAMS */
	
#ifdef SHIPCHOPPERS
	if ((pcp->pl_flags & P_K) && (scp->m_flags & M_CHOPPER)){
#ifdef TOM_DEBUG
pr(fmt("Plane %d chopper plane ok to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */
		return 1;
	}
#endif /* SHIPCHOPPERS */
	
#ifdef XLIGHT
	if ((pcp->pl_flags & P_E) && (scp->m_flags & M_XLIGHT)){
#ifdef TOM_DEBUG
pr(fmt("Plane %d xlight plane ok to take off!\n",plane.pln_uid));
#endif /* TOM_DEBUG */
		return 1;
	}
#endif /* XLIGHT */

	return 0;
}

#ifdef BIGINT
/*
 * Get a list of planes available for interception duties.
 */
static
new_getilist(list, intruder, sector_owner)
	struct  qelem list[MAXNOC];
	natid	intruder;
	natid	sector_owner;
{
	register struct plchrstr *pcp;
	struct	plnstr plane;
	struct	shpstr ship;
	struct	sctstr sect;
	struct	nstr_item ni;
	int	type;
	char	*ptr;
	struct	plist *ip;
	struct	natstr *n;
	int	x;
	int	rel;

	for(x=0;x<MAXNOC;x++)
		initque(&list[x]);

	snxtitem_all(&ni, EF_PLANE);
	while (nxtitem(&ni, (char *)&plane)) {
		if ((sector_owner) && (plane.pln_own != sector_owner))
			continue;
		pcp = &plchr[plane.pln_type];
		if ((pcp->pl_flags & P_F) == 0)
			continue;
		if (plane.pln_mobil <= 0)
			continue;
		if (plane.pln_effic < 40)
			continue;
#ifdef	INCARRANGE
		if (!own) {
			int	rel;

			rel = getrel(getnatp(plane.pln_own), cnum);
#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 */
		}
#endif	INCARRANGE
		if (intruder == plane.pln_own)
			continue;

		rel = getrel(getnatp(plane.pln_own), intruder);
		if (rel != HOSTILE && rel != AT_WAR && rel != MOBILIZATION &&
			rel != SITZKRIEG)
			continue;

		if (plane.pln_ship >= 0) {
			if (!can_fly(plane.pln_uid))
				continue;
			getship(plane.pln_uid,&ship);
			ptr = (char *)&ship;
			type = EF_SHIP;
		} else {
			getsect(plane.pln_x, plane.pln_y, &sect);
			ptr = (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 (getvar(V_PETROL, ptr, type) < (pcp->pl_fuel/2))
			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((char *)&plane, (char *)&ip->plane, sizeof(plane));
		insque(&ip->queue, &list[plane.pln_own]);
	}
}
#endif /* BIGINT */
