#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/subs/aircombat.c,v 2.17 1995/10/24 04:27:22 empire Exp $";
#endif

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

#include <varargs.h>
#include <string.h>

#include "misc.h"
#include "player.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"

extern int update_pending;

#ifndef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x,y) ((x) < (y) ? (y) : (x))
#endif

static  getilist();

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

void
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;
{
        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;
        struct  shiplook head;
        struct  shiplook *s,*s2;
        int     changed = 0;

        bzero(&head,sizeof(struct shiplook));
        head.uid = -1;

        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);
		if (rel > HOSTILE)
			continue;
                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();
                } else if (mission_flags & P_A){
                        PR(plane_owner,"\nAnti-Sub Patrol report\n");
                } 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);
#ifdef HIDDEN
                setcont(plane_owner,sect.sct_own, FOUND_FLY);
#endif /* HIDDEN */

                if (mission_flags & PM_R) {
                        if (sect.sct_type == SCT_WATER){
				PR(plane_owner,"flying over %s at %s\n",
				   dchr[sect.sct_type].d_name,
				   xyas(x, y, plane_owner));
                                if (mission_flags & PM_S)
                                        plane_sweep(bomb_list,x,y);
                                if (mission_flags & P_A){
                                        plane_sona(bomb_list,x,y,&head);
                                }
				changed += map_set(plane_owner,
						  sect.sct_x, sect.sct_y,
						  dchr[sect.sct_type].d_mnem,
						  0);
                        } else 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,
                                                "%s (#%d) ",
                                                cname(sect.sct_own),
                                                sect.sct_own);
                                PR(plane_owner,dchr[sect.sct_type].d_name);
				changed += map_set(plane_owner,
						  sect.sct_x, sect.sct_y,
						  dchr[sect.sct_type].d_mnem,
						  0);
                                PR(plane_owner," %d%% efficient ",
                                        (sect.sct_own == plane_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,"with %s%d civ ",
                                            (sect.sct_own == plane_owner) ? 
						"" : "approx ",
                                            (sect.sct_own == plane_owner) ? 
						civ : round(civ, 25));
                                if (mil)
                                        PR(plane_owner,"with %s%d mil ",
                                                (sect.sct_own == plane_owner) ? 
						"" : "approx ",
                                                (sect.sct_own == plane_owner) ?
					 	mil : round(mil, 25));
                                PR(plane_owner,"@ %s\n", xyas(x, y, plane_owner));
                        }
                } else {
                        PR(plane_owner,"flying over %s at %s\n",
                                dchr[sect.sct_type].d_name,
                                xyas(x, y, plane_owner));
			changed += map_set(plane_owner, sect.sct_x,
					   sect.sct_y,
					   dchr[sect.sct_type].d_mnem, 0);
                }
                if ((rel = getrel(over, plane_owner)) == ALLIED)
                        continue;

                if (sect.sct_own != 0 && sect.sct_own != plane_owner) {
                	overfly[sect.sct_own]++;
                	PR(sect.sct_own, "%s planes spotted over %s\n",
                            cname(plane_owner), xyas(x, y, sect.sct_own));
		}

                evaded = do_evade(bomb_list,esc_list);
                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);

                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 > HOSTILE)
                        continue;
                ac_intercept(bomb_list, esc_list, &ilist[sect.sct_own],
                        sect.sct_own, x, y);
	}

        if (changed)
		if (!update_pending && plane_owner == player->cnum)
			writemap(player->cnum);
        if (QEMPTY(bomb_list) && QEMPTY(esc_list)){
                if (mission_flags & P_A){
                        s=head.next;
                        while (s != (struct shiplook *)0){
                                s2=s;
                                s=s->next;
                                free(s2);
                        }
                }
                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]++;
        }
        bzero((s_char *)lnats, sizeof(lnats));
        snxtitem_xy(&ni, EF_LAND, x, y);
        while (nxtitem(&ni, (s_char *)&land)) {
                lnats[land.lnd_own]++;
        }

        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){
                        if(cn != 0)
                        PR(cn, "%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, "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){
                        if(cn != 0)
                        PR(cn, "%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,"Flying over %d #%d units...\n",
                                        lnats[cn], cn);
                                if (!evaded)
                                        ac_intercept(bomb_list, esc_list,
                                        &ilist[cn], cn, x, y);
                        }
                }
        }
        if (!evaded) {
		ac_landflak(bomb_list, x, y);
                ac_shipflak(bomb_list, x, y);
	}

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

static int
count_non_missiles(list)
	struct	qelem *list;
{
        struct  qelem *next;
        struct  qelem *qp;
	struct	plist *plp;
	int att_count = 0;

        /* don't intercept missiles */
        for (qp = list->q_forw; qp != list; qp = qp->q_forw){
                plp = (struct plist *)qp;
                if (!(plp->pcp->pl_flags & P_M))
                        att_count++;
        }
	return att_count;
}

sam_intercept(att_list, def_list, def_own, plane_owner, x, y, delete_missiles)
        struct  qelem *att_list;
        struct  qelem *def_list;
        natid   def_own;
	natid	plane_owner;
        coord   x;
        coord   y;
	int	delete_missiles;
{
        struct  qelem *aqp;
        struct  qelem *anext;
        struct  qelem *dqp;
        struct  qelem *dnext;
	struct	plist *aplp;
	struct	plist *dplp;
	int	first = 1;

        for (aqp = att_list->q_forw,
	     dqp = def_list->q_forw;
	     aqp != att_list &&
	     dqp != def_list;
	     aqp = anext) {
		anext = aqp->q_forw;
                aplp = (struct plist *)aqp;
                if (aplp->pcp->pl_flags & P_M)
                        continue;
		if (aplp->pcp->pl_cost < 1000)
			continue;
		for (;dqp != def_list; dqp = dnext) {
			dnext = dqp->q_forw;
			dplp = (struct plist *)dqp;
			if (!(dplp->pcp->pl_flags & P_M))
				continue;

			if (dplp->plane.pln_range < mapdist(x, y, dplp->plane.pln_x, dplp->plane.pln_y)) {
				remque(dqp);
				free((s_char *)dqp);
				continue;
			}
			if (mission_pln_equip(dplp, 0, P_F, 0) < 0) {
				remque(dqp);
				free((s_char *)dqp);
				continue;
			}
			if (first) {
				first = 0;
				PR(plane_owner,"%s launches SAMs!\n", cname(def_own));
				PR(def_own, "Launching SAMs at %s planes!\n", cname(plane_owner));
				ac_combat_headers(plane_owner, def_own);
			}
			ac_dog(aplp, dplp);
			dqp = dnext;
			break;
		}
        }
	if (!first) {
		PR(plane_owner, "\n");
		PR(def_own,  "\n");
	}
	if (delete_missiles) {
		for (;dqp != def_list; dqp = dnext) {
			dnext = dqp->q_forw;
			dplp = (struct plist *)dqp;
			if (!(dplp->pcp->pl_flags & P_M))
				continue;
			remque(dqp);
			free((s_char *)dqp);
			continue;
		}
	}
}

void
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;
        struct  plist *plp;
        int     icount;
        struct  qelem *next;
        struct  qelem *qp;
        struct  qelem int_list;
        int     att_count;
        natid   plane_owner;
	int	dist;

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

        icount = 0;

	sam_intercept(bomb_list, def_list, def_own, plane_owner, x, y, 0);
	sam_intercept(esc_list, def_list, def_own, plane_owner, x, y, 1);
	if (!(att_count = count_non_missiles(bomb_list) +
	      count_non_missiles(esc_list)))
                return;

        initque(&int_list);
        for (qp = def_list->q_forw; qp != def_list; qp = next) {
                next = qp->q_forw;
                plp = (struct plist *) qp;
                pp = &plp->plane;
		/* SAMs interdict separately */
		if (plp->pcp->pl_flags & P_M)
			continue;
		dist = mapdist(x, y, pp->pln_x, pp->pln_y) * 2;
                if (pp->pln_range < dist)
                        continue;
		if (mission_pln_equip(plp, 0, P_F, 0) < 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 -= pln_mobcost(dist,pp,P_F);
                putplane(pp->pln_uid, pp);
		icount++;
                if (icount > att_count)
                        break;
        }
        if (icount == 0)
                return;
        PR(plane_owner,"%d %s fighter%s rising to intercept!\n", icount,
                cname(def_own), icount == 1 ? " is" : "s are");
        PR(def_own,"%d fighter%s intercepting %s planes!\n", icount,
                icount == 1 ? " is" : "s are", cname(plane_owner));
        ac_combat_headers(plane_owner, def_own);
        ac_airtoair(esc_list, &int_list, def_own);
        ac_airtoair(bomb_list, &int_list, def_own);
	PR(plane_owner, "\n");
	PR(def_own,  "\n");
}

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

/*
 * 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     more_att;
        int     more_int;
        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;
                nplanes = attacker->plane.pln_effic;
                if (nplanes > interceptor->plane.pln_effic)
                        nplanes = interceptor->plane.pln_effic;
                ac_dog(attacker, interceptor);
                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;
}

static int
ac_dog(ap, dp)
        register struct plist *ap;
        register struct plist *dp;
{
        int     att, def;
        double  odds;
        int     intensity;
        natid   att_own, def_own;
        int 	adam, ddam;
        s_char  mesg[1024];
        s_char  temp[14];

        att_own = ap->plane.pln_own;
        def_own = dp->plane.pln_own;

	PR(att_own," %3.3s #%-4d  %3.3s #%-4d",
	   ap->pcp->pl_name,
	   ap->plane.pln_uid,
	   dp->pcp->pl_name,
	   dp->plane.pln_uid);
	if (def_own)
		PR(def_own," %3.3s #%-4d  %3.3s #%-4d",
		   dp->pcp->pl_name,
		   dp->plane.pln_uid,
		   ap->pcp->pl_name,
		   ap->plane.pln_uid);
        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;
        att += ((float)ap->pcp->pl_stealth/25.0);
        def += ((float)dp->pcp->pl_stealth/25.0);
        odds = ((double)att/((double)def+(double)att));
/* next 2 lines: Zepplin bug fix... from Jim Ortlieb */
        if(odds<=0)
                odds = 0.05;
        intensity = roll(20)+roll(20)+roll(20)+roll(20)+1;

        PR(att_own,"   %3d/%-3d %3d  %3.2f  ",att,def,intensity,odds);
        PR(def_own,"   %3d/%-3d %3d  %3.2f  ",def,att,intensity,odds);

        adam = 0; ddam = 0;
        while ((intensity--)>0){

                if (chance(odds)) {
                        ddam += 1;
                        if ((dp->plane.pln_effic-ddam) < PLANE_MINEFF)
                                intensity=0;
                } else {
                        adam += 1;
                        if ((ap->plane.pln_effic-adam) < PLANE_MINEFF)
                                intensity=0;
                }
        }

	if (dp->pcp->pl_flags & P_M)
		ddam = 100;
	
	PR(att_own,"%3d/%-3d", adam, ddam);
	PR(def_own,"%3d/%-3d", ddam, adam);
	ac_planedamage(ap, dp->plane.pln_own,
		       adam, def_own, 1, 0, mesg);
	strncpy(temp,mesg,14);
	ac_planedamage(dp, ap->plane.pln_own,
		       ddam, att_own, 1, 0, mesg);
	PR(att_own, "%-13.13s %-13.13s\n", temp, mesg);
	PR(def_own, "%-13.13s %-13.13s\n", mesg, temp);
}

/*
 * 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)
 */
void
ac_planedamage(plp, from, dam, other, checkabort, show, mesg)
        struct  plist *plp;
        natid   from;
        int     dam;
        natid   other;
        int     checkabort;
        int     show;
        s_char  *mesg;
{
        register struct plnstr *pp;
        int     disp;
        s_char  dmess[255];
        int     eff;
        struct shpstr ship;
        struct lndstr land;
        /* s_char *sprintf();		already in misc.h [JFW] */
        natid   plane_owner;

        disp = 0;
        pp = &plp->plane;
        plane_owner = pp->pln_own;
        eff = pp->pln_effic;
        sprintf(dmess," no damage");
        if (dam <= 0) 
		{
		strcpy(mesg, dmess);
		return;
		}
        bzero(dmess,255);
        eff -= dam;
        if (eff < 0)
                eff = 0;
        if (eff < PLANE_MINEFF) {
                sprintf(dmess," shot down");
                disp = 1;
        } else if (eff < 80 && chance((100-eff)/100.0) && checkabort){
                sprintf(dmess," aborted @%2d%%", eff);
                disp = 2;
        } else if (show == 0) {
                sprintf(dmess," cleared");
        }

        if ((plp->pcp->pl_flags & P_M) == 0) {
		if (show) {
                        PR(plane_owner, "    %s %s takes %d%s.\n",
                            cname(pp->pln_own),
			   prplane(pp), dam, dmess);
                        PR(other, "    %s %s takes %d%s.\n",
			   cname(pp->pln_own),
			   prplane(pp), dam, dmess);
                }
        }
        if (show && checkabort == 1) {
                PR(plane_owner,"\n");
                PR(other,"\n");
        }

        pp->pln_effic = eff;
	pp->pln_mobil -= min(32 + pp->pln_mobil, dam/2);
        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);
        strcpy(mesg,dmess);
}

void
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,"firing %d flak guns in %s...\n",
                        gun, xyas(from->sct_x, from->sct_y, plane_owner));
                if(from->sct_own != 0)
                PR(from->sct_own, "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[1024];
        struct  plist *plp;
        natid   plane_owner;

        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);
		if (rel > HOSTILE)
			continue;
                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);
                sprintf(msg, "firing %d flak guns from %s...\n",
                        firing, prship(&ship));
                PR(plane_owner,msg);
                if(ship.shp_own != 0)
                        PR(ship.shp_own, msg);
                ac_fireflak(list, ship.shp_own, firing);
                putvar(V_SHELL, shell - 1, (s_char *)&ship, EF_SHIP);
                putship(ni.cur, &ship);
        }
}

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[1024];
        struct  plist *plp;
        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);
		if (rel > HOSTILE)
			continue;
                firing = (int)(techfact(land.lnd_tech, (double)lcp->l_aaf)*3.0);
                sprintf(msg, "firing flak guns from unit %s (aa rating %d)...\n",
                        prland(&land), lcp->l_aaf);

                PR(plane_owner,msg);
                if(land.lnd_own != 0)
                        PR(land.lnd_own, msg);
                ac_fireflak(list, land.lnd_own, firing);
        }
}

/*
 * 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;
        s_char  msg[255];

        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;
                if (plp->pcp->pl_flags & P_H)
                        diff -= 1;
                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, 2, 1, msg);
        }
}

/*
 * 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);
        }
}



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)
                        && !(pcp->pl_flags & P_K)
                        && !(pcp->pl_flags & P_E)
                        )
                        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;
                }

                if ((pcp->pl_flags & P_M) && (scp->m_flags & M_MSL)){
                        return 1;
                }

                if ((pcp->pl_flags & P_K) && (scp->m_flags & M_CHOPPER)){
                        return 1;
                }

                if ((pcp->pl_flags & P_E) && (scp->m_flags & M_XLIGHT)){
                        return 1;
                }
        }

        if (plane.pln_land >= 0){
                if (!(pcp->pl_flags & P_E))
                        return 0;

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

                if ((pcp->pl_flags & P_E) && (lcp->l_flags & L_XLIGHT)){
                        return 1;
                }
        }

        return 0;
}

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

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

        if (chance(evade))
                return 1;

        return 0;
}

