#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/subs/mission.c,v 2.20 1995/10/25 20:07:27 empire Exp $";
#endif /* not lint */

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

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

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

s_char  *mission_name();
s_char  *nameofitem();
s_char  *bestpath();

/*
 * Interdict commodities & transported planes
 */
ground_interdict(x,y,victim,s)
	coord     x,y;
	natid	victim;
	s_char  *s;
{
        register int cn;
        int     dam=0, newdam, rel;
        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,victim);

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

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

                newdam = perform_mission(x,y,victim,&mi[cn].queue,
                        MI_INTERDICT,s,SECT_HARDTARGET);
                dam += newdam;
                if (newdam)
                        mpr(victim, "%s interdiction mission does %d damage!\n",
                                cname(cn), newdam);
        }
	if (dam) {
		collateral_damage(x,y,dam);
	}		
        return dam;
}

int
collateral_damage(x,y,dam)
	coord	x,y;
	int dam;
{
	extern double collateral_dam;
	int	coll;
	struct	sctstr sect;

	if (!dam)
		return 0;

	getsect(x,y,&sect);
	if (sect.sct_own) {
		coll = ldround((double)dam * collateral_dam, 1);
		if (coll == 0)
			return 0;
		mpr(sect.sct_own, "%s takes %d%% collateral damage\n",
		    xyas(x,y,sect.sct_own), coll);
		sectdamage(&sect, coll);
		putsect(&sect);
		return coll;
	}
	return 0;
}

/*
 *  Interdict ships & land units
 *
 */
unit_interdict(x,y,victim,s,hardtarget,mission)
	coord   x,y;
	natid	victim;
	s_char  *s;
	int	hardtarget;
	int	mission;
{
        register int cn;
        int     dam=0, newdam;
        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,mission,victim);

        for(cn=1;cn<MAXNOC;cn++){
		if (cn == victim)
			continue;
                if (mission == MI_SINTERDICT) {
			if (getrel(getnatp(cn),victim) >= FRIENDLY)
				continue;
		} else if (getrel(getnatp(cn),victim) != AT_WAR)
                        continue;

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

                newdam = perform_mission(x,y,victim,&mi[cn].queue,
                        mission,s,hardtarget);
                dam += newdam;
                if (newdam)
                        mpr(victim, "%s interdiction mission does %d damage!\n",
                                cname(cn), newdam);
        }
	if (dam) {
		collateral_damage(x,y,dam);
	}		
        return dam;
}

/*
 *  Perform a mission against victim, on behalf of actee
 */
off_support(x,y,victim,actee)
	coord	x,y;
	natid	victim, 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,victim);
        build_mission_list(mi,x,y,MI_OSUPPORT,victim);

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

/*
 *  Perform a mission against victim, on behalf of actee
 */
def_support(x,y,victim,actee)
	coord	x,y;
	natid	victim,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,victim);
        build_mission_list(mi,x,y,MI_DSUPPORT,victim);

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

dosupport(mi,x,y,victim,actee)
	struct  genlist mi[MAXNOC];
	coord   x,y;
	natid	victim,actee;
{
        register int cn;
        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),victim);
                if ((cn != actee) && (rel != AT_WAR))
                        continue;

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

                dam += perform_mission(x,y,victim,&mi[cn].queue,MI_SUPPORT,
                        "", SECT_HARDTARGET);
        }
        return dam;
}

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

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

        size = max(sizeof(struct shpstr),sizeof(struct lndstr));
        size = max(size,sizeof(struct plnstr));
        block = (s_char *)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);

		radius = gp->radius;
		if (mission != MI_RESERVE) /* XXX */
			oprange(gp,type,&radius);

                if (dist > radius)
                        continue;

		if (mission == MI_SINTERDICT &&
		    type != EF_PLANE &&
		    getrel(getnatp(gp->own), victim) > AT_WAR)
			continue;
#ifdef SLOW_WAR
		else if (mission != MI_AIR_DEFENSE) {
			getsect(x,y,(s_char *)&sect);
			if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) {

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

                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 = (s_char *)malloc(size);
                bcopy(block,glp->thing,size);
                insque(&glp->queue, &mi[gp->own].queue);
        }
}

find_escorts(x,y,cn,escorts)
	coord   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,victim,list,mission,s,hardtarget)
	coord   x,y;
	natid	victim;
	struct  qelem *list;
	int     mission;
	s_char  *s;
	int	hardtarget;
{
        extern  int land_max_interdiction_range;
        extern  int ship_max_interdiction_range;
        s_char  *mission_name(), *nameofitem();
        struct  qelem *qp, missiles, 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;
        struct  plchrstr *pcp;
        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(&missiles);
        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 (lp->lnd_effic < LAND_MINFIREEFF)
                                continue;

                        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,victim,1);
                                else
                                        nreport(lp->lnd_own,N_SCT_SHELL,victim,1);
                                wu(0,lp->lnd_own,
				   "%s fires at %s %s at %s\n",
				   prland(lp), cname(victim),
				   s, xyas(x,y,lp->lnd_own));

                                mpr(victim, "%s %s fires at you at %s\n",
				    cname(lp->lnd_own), prland(lp),
				    xyas(x,y,victim));
                        }
                }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 > ship_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_SUBT))
                                        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 < (s_char)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 = DTORP_HITCHANCE(md, mcp->m_visib);
				
				wu(0,sp->shp_own,
				   "%s locking on %s %s in %s\n",
				   prship(sp),cname(victim),s,
				   xyas(x,y,sp->shp_own));
				wu(0,sp->shp_own,
				   "\tEffective torpedo range is %.1f\n", range);
				wu(0,sp->shp_own,
				   "\tWhooosh... Hitchance = %d%%\n",
				   (int)(hitchance*100));

                                if (hitchance < 1.0 && !chance(hitchance)) {
					wu(0,sp->shp_own,
					   "\tMissed\n");
					mpr(victim, "Torpedo sighted @ %s\n",
					    xyas(x,y,victim));
                                        continue;
				}
				wu(0,sp->shp_own,
				   "\tBOOM!...\n");
                                dam2 = TORP_DAMAGE();

                                dam += dam2;
                                nreport(victim,N_TORP_SHIP,sp->shp_own,1);
                                wu(0,sp->shp_own,
                                        "\tTorpedo hit %s %s for %d damage\n",
                                        cname(victim),s,dam2);

                                mpr(victim, "%s @ %s torpedoed you for %d damage.\n",
				    prship(sp),
				    xyas(sp->shp_x,sp->shp_y,victim),
				    dam2);
                        }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 = seagun(sp->shp_effic,gun);
#ifdef linux /* special case if md = range2 = 0.0 bugs linux */
				if ( range2 == 0.0 )
				        prb = 1.0;
				else
#endif /* linux */  
                                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,victim,1);
                                else
                                        nreport(sp->shp_own,N_SCT_SHELL,victim,1);
                                wu(0,sp->shp_own,
				   "%s fires at %s %s at %s\n",
				   prship(sp),
				   cname(victim), s,
				   xyas(x,y,sp->shp_own));

                                mpr(victim, "%s fires at you at %s\n",
                                        cname(sp->shp_own), prship(sp),
                                        xyas(x,y,victim));

                                putvar(V_SHELL,shell-gun,(s_char *)sp, EF_SHIP);
                                putship(sp->shp_uid,sp);
                        }
                }else if (glp->type == EF_PLANE) {
			pcp = (struct plchrstr *)glp->cp;
			if (pcp->pl_flags & P_M)
				/* units have their own missile interdiction */
				if (hardtarget != SECT_HARDTARGET ||
				    pcp->pl_flags & P_MAR)
					continue;

                        /* save planes for later */
                        plp = (struct plist *)malloc(sizeof(struct plist));

                        bzero((s_char *)plp,sizeof(struct plist));
                        plp->pcp = pcp;
                        bcopy(glp->thing, (s_char *)&plp->plane,
                                sizeof(struct plnstr));
			if (plp->pcp->pl_flags & P_M)
				insque(&plp->queue, &missiles);
			else
				insque(&plp->queue, &bombers);
                        plane_owner = plp->plane.pln_own;
                }
        }
	if (!QEMPTY(&missiles)) {
		/* I arbitrarily chose 100 mindam -KHS */
		dam += msl_launch_mindam(&missiles, x, y, hardtarget, EF_SECTOR, 100, "sector", victim, mission);
		qp=missiles.q_forw;
		while(qp != (&missiles)){
			qp=qp->q_forw;
			free(qp);
		}
	}

        if (QEMPTY(&bombers)) {
		qp=list->q_forw;
		while(qp != list){
			glp = (struct genlist *)qp;
			qp=qp->q_forw;

			free(glp->thing);
			free((s_char *)glp);
		}
                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,hardtarget);
        else
                mission_pln_sel(&bombers,P_T,P_A,SECT_HARDTARGET);

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

        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  pp[512];

                air = (struct airport *)qp;
                md = mapdist(x,y,air->x,air->y);

                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) */
                mission_flags |= P_H; /* gets turned off if not all choppers */

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

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

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

                bestpath(pp, air->x,air->y,x,y,"");
		if (air->own)
			wu(0, air->own, "Flying %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,victim,s,hardtarget);

                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,victim,1);
                else
                        nreport(plane_owner,N_SCT_BOMB,victim,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(buf, gp,type)
	s_char  *buf;
	struct  genitem *gp;
	int     type;
{
        switch(type){
	case EF_SHIP:
		return prship(gp);
		break;
	case EF_PLANE:
		return prplane(gp);
		break;
	case EF_LAND:
		return prland(gp);
        }
}

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)
	short 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";
        }
	return "a mysterious";
}

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

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

        while (nxtitem(np, block)) {
                gp = (struct genitem *)block;
                if (!player->owner || gp->own == 0)
                        continue;

                if (first){
                        pr("Thing                         x,y   op-sect rad mission\n");
                        first=0;
                }
                pr("%-25s", nameofitem(buf, gp,type));
                pr(" %7s", xyas(gp->x,gp->y,player->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(" %7s", xyas(gp->opx,gp->opy,player->cnum));
                        if (radius < gp->radius)
                                pr("  %4d", radius);
                        else
                                pr("  %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(" %7s", xyas(gp->x,gp->y,player->cnum));
                        pr("  %4d", lchr[gp->type].l_rad + plus);
                }else if (gp->mission == MI_ESCORT){
                        pr("        ");
                        pr("  %4d", (int)
                                ((float)((struct plnstr *)block)->pln_range/2.0)
                                );
                }else
                        pr("              ");
                if (gp->mission)
                        pr(" is on %s mission\n",
                                mission_name(gp->mission));
                else
                        pr(" 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);
			/* missiles go one way, so we can use all the range */
			if (plchr[plane.pln_type].pl_flags & P_M)
				range = plane.pln_range;
			else
				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, hardtarget)
        struct  qelem *list;
        int     wantflags;
        int     nowantflags;
	int	hardtarget;
{
        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;
                                                        case P_E:
                                                        case P_L:
                                                        case P_K: bad1=1;
                                                                  break;
                                                        default:  bad=1;
                                                }
                                        }
                        }
                        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 (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;
                        }
                }
                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 (!can_be_on_ship(pp->pln_uid,ship.shp_uid)){
                                goto shipsunk;
                        }
                        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;
                        }
                        if (!(pcp->pl_flags & P_E))
                                goto landdead;
                        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;
                        }
                }

		if (pcp->pl_flags & P_A) {
			if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
                                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, dist, mission, ip, flags, mission_flags, tech)
        struct  qelem *list;
        coord   x,y;
	int	dist;
        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;
                if (!(plp->pcp->pl_flags & P_H))
                        /* no stealth on this mission */
                        mission_flags &= ~P_H;
                if (!(plp->pcp->pl_flags & P_X))
                        /* no stealth on this mission */
                        mission_flags &= ~P_X;
                if (!(plp->pcp->pl_flags & P_A)) {
                        /* no asw on this mission */
                        mission_flags &= ~P_A;
                }
                if (!(plp->pcp->pl_flags & P_MINE)) {
                        /* no asw on this mission */
                        mission_flags &= ~P_MINE;
                }

                /*
                 *      Mob costs for missions are 1/2 normal
                 */
		plp->plane.pln_mobil -= pln_mobcost(dist,&plp->plane,flags)/2;
        }
        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)) {
                item = 0;
                needed = 0;
                switch (mission) {
                case 's':
                case 'p':
                        if (pp->pln_nuketype == -1) {
				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_nuketype == -1)
                                rval = -1;
                        break;
		case 'i': /* missile interception */
			if (pcp->pl_load) {
				item = I_SHELL;
				needed = pcp->pl_load;
			}
			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;
	coord   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;
}

/* #*# This needs to be changed to include acc's -KHS */
add_airport(airp,x,y)
	struct  qelem *airp;
	coord   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;
	coord   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,victim,s,hardtarget)
	struct  qelem *bombers;
	coord	x,y;
	int     mission;
	natid   victim;
	s_char  *s;
	int	hardtarget;
{
        struct  qelem *qp;
        struct  plist *plp;
        struct  plnstr *pp;
        int     newdam,dam=0;
	int	hitchance;
	int	nukedam;
	
        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 (!plp->bombs)
			continue;

		newdam = 0;
		if (plp->pcp->pl_flags & P_A) {
			if (roll(100) >  pln_identchance(pp, hardtarget, EF_SHIP)) {
				wu(0,pp->pln_own,
				   "\t%s detects sub movement in %s\n",
				   prplane(pp), xyas(x,y,pp->pln_own));
				continue;
			}
			if (getrel(getnatp(pp->pln_own),victim) > HOSTILE) {
				wu(0,pp->pln_own,
				   "\t%s tracks %s %s at %s\n",
				   prplane(pp),
				   cname(victim), s, xyas(x,y,pp->pln_own));
				continue;
			}
			wu(0,pp->pln_own,
			   "\t%s depth-charging %s %s in %s\n",
			   prplane(pp),
			   cname(victim),
			   s,
			   xyas(x,y,pp->pln_own));
                } else {
			wu(0,pp->pln_own,
			   "\t%s pinbombing %s %s in %s\n",
			   prplane(pp),
			   cname(victim),
			   s,
			   xyas(x,y,pp->pln_own));
		}		
		hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
		if (plp->plane.pln_nuketype != -1)
			hitchance = 100;
		else if (hardtarget != SECT_HARDTARGET)
			wu(0,pp->pln_own,"\t\t%d%% hitchance...", hitchance);
		if (roll(100) <= hitchance) {
			newdam = pln_damage(&plp->plane,x,y,'p',&nukedam);
			if (nukedam) {
				if (mission == MI_INTERDICT) {
					wu(0,pp->pln_own,
					   "\t\tnuclear warhead does %d damage to %s %s\n",
					   prplane(pp),
					   nukedam, cname(victim), s);
					dam += nukedam;
				}
			} else {
				wu(0,pp->pln_own,
				   "\t\thit %s %s for %d damage\n",
				   cname(victim), s, newdam);
				dam += newdam;
			}
		} else { /* must be ships */
			wu(0,pp->pln_own,"missed\n");
			mpr(victim,
			    "RUMBLE... your sub in %s hears a depth-charge explode nearby\n",
			    xyas(x,y,victim));
		}
			
		/* use up missiles */
                if (plp->pcp->pl_flags & P_M) pp->pln_own=0;
        }

        return dam;
}

/*
 * Check to see if anyone hostile to the victim
 * is running an air defense mission on this
 * sector. If so, do air combat
 */
air_defense(x,y,victim,bomb_list,esc_list)
	coord   x,y;
	natid	victim;
	struct  qelem *bomb_list;
	struct  qelem *esc_list;
{
        int     dam=0,cn;
        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];
        s_char  path[512];

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

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

                rel = getrel(getnatp(cn),victim);

		if (rel >= HOSTILE)
			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);

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

			/* missiles go one way, so we can use all the range */
			if (!(plp->pcp->pl_flags & P_M))
				dist *= 2;
                        if (dist > plp->plane.pln_range){
                                free(plp);
                                continue;
                        }
                        insque(&plp->queue, &interceptors);
                }

                mission_pln_sel(&interceptors,P_F,0,SECT_HARDTARGET);

                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;
                        dist = mapdist(x,y,air->x,air->y);

                        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) */
                        /* gets turned off if not all choppers */
                        mission_flags |= P_H;
			sam_intercept(bomb_list, &i, cn, victim, x, y, 0);
			sam_intercept(esc_list, &i, cn, victim, x, y, 1);
			if (QEMPTY(&i) || QEMPTY(bomb_list))
				continue;
                        mission_flags = mission_pln_arm(&i,air->x,air->y,2*dist,'r',0,
                                P_F,mission_flags,&tech);

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

                        bestpath(path, air->x,air->y,x,y,"");
			if (air->own)
				wu(0, air->own, "Flying %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)){
				mpr(victim, "%s air defense planes intercept!\n",
                                                cname(cn));
                                ac_combat_headers(victim, cn);
                                ac_airtoair(esc_list,&i,air->own);
                                combat = 1;
                        }
                        if (!QEMPTY(bomb_list)){
                                if (!combat) {
					mpr(victim, "%s air defense planes intercept!\n", cname(cn));
                                        ac_combat_headers(victim, cn);
                                }
                                ac_airtoair(bomb_list,&i,air->own);
				PR(cn, "\n");
				PR(victim, "\n");
                        }

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



