]> git.pond.sub.org Git - empserver/blob - src/lib/subs/mission.c
1b61462d798966018eb0d504894f651b17b38704
[empserver] / src / lib / subs / mission.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  mission.c: Mission subroutines for planes/ships/units
29  * 
30  *  Known contributors to this file:
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1996-2000
33  */
34
35 #include "options.h"
36 #include "misc.h"
37 #include "player.h"
38 #include "var.h"
39 #include "xy.h"
40 #include "sect.h"
41 #include "retreat.h"
42 #include "ship.h"
43 #include "land.h"
44 #include "plane.h"
45 #include "nat.h"
46 #include "nsc.h"
47 #include "deity.h"
48 #include "file.h"
49 #include "path.h"
50 #include "mission.h"
51 #include "genitem.h"
52 #include "nuke.h"
53 #include "news.h"
54 #include "item.h"
55 #include <fcntl.h>
56 #include "damage.h"
57 #include "prototypes.h"
58 #include "optlist.h"
59
60 struct airport {
61     struct emp_qelem queue;
62     coord x, y;
63     natid own;
64 };
65
66 static void add_airport(struct emp_qelem *, coord, coord);
67 static int air_damage(struct emp_qelem *, coord, coord, int, natid,
68                       s_char *, int);
69 static void build_mission_list(struct genlist *, coord, coord, int, natid);
70 static void build_mission_list_type(struct genlist *, coord, coord, int,
71                                     int, natid);
72 static void divide(struct emp_qelem *, struct emp_qelem *, coord, coord);
73 static int dosupport(struct genlist *, coord, coord, natid, natid);
74 static int find_airport(struct emp_qelem *, coord, coord);
75 static int mission_pln_arm(struct emp_qelem *, coord, coord, int,
76                            int, struct ichrstr *, int, int, int *);
77 static void mission_pln_sel(struct emp_qelem *, int, int, int);
78 static int perform_mission(coord, coord, natid, struct emp_qelem *, int,
79                            s_char *, int);
80
81 /*
82  * Interdict commodities & transported planes
83  */
84 int
85 ground_interdict(coord x, coord y, natid victim, s_char *s)
86 {
87     register int cn;
88     int dam = 0, newdam, rel;
89     struct genlist mi[MAXNOC];
90     int z;
91
92     memset(mi, 0, sizeof(mi));
93     for (z = 1; z < MAXNOC; z++)
94         emp_initque((struct emp_qelem *)&mi[z]);
95
96     build_mission_list(mi, x, y, MI_INTERDICT, victim);
97
98     for (cn = 1; cn < MAXNOC; cn++) {
99         rel = getrel(getnatp(cn), victim);
100         if (rel > HOSTILE)
101             continue;
102
103         if (QEMPTY(&mi[cn].queue))
104             continue;
105
106         newdam = perform_mission(x, y, victim, &mi[cn].queue,
107                                  MI_INTERDICT, s, SECT_HARDTARGET);
108         dam += newdam;
109         if (newdam)
110             mpr(victim, "%s interdiction mission does %d damage!\n",
111                 cname(cn), newdam);
112     }
113     if (dam) {
114         collateral_damage(x, y, dam, 0);
115     }
116     return dam;
117 }
118
119 int
120 collateral_damage(coord x, coord y, int dam, struct emp_qelem *list)
121 {
122     extern double collateral_dam;
123     int coll;
124     struct sctstr sect;
125
126     if (!dam)
127         return 0;
128
129     getsect(x, y, &sect);
130     if (sect.sct_own) {
131         coll = ldround((double)dam * collateral_dam, 1);
132         if (coll == 0)
133             return 0;
134         mpr(sect.sct_own, "%s takes %d%% collateral damage\n",
135             xyas(x, y, sect.sct_own), coll);
136         sectdamage(&sect, coll, list);
137         putsect(&sect);
138         return coll;
139     }
140     return 0;
141 }
142
143 static int
144 only_subs(struct emp_qelem *list)
145 {
146     struct emp_qelem *qp;
147     struct genlist *glp;
148     struct genitem *gp;
149     struct mchrstr *mcp;
150
151     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
152         glp = (struct genlist *)qp;
153         gp = (struct genitem *)glp->thing;
154
155         if (glp->type != EF_SHIP)
156             return 0;
157         mcp = (struct mchrstr *)glp->cp;
158         if (!(mcp->m_flags & M_SUB))
159             return 0;
160         /* It's a sub! */
161     }
162     /* They were all subs! */
163     return 1;
164 }
165
166
167 /*
168  *  Interdict ships & land units
169  *
170  */
171 int
172 unit_interdict(coord x, coord y, natid victim, s_char *s, int hardtarget,
173                int mission)
174 {
175     register int cn;
176     int dam = 0, newdam;
177     struct genlist mi[MAXNOC];
178     int z;
179     int osubs;
180
181     memset(mi, 0, sizeof(mi));
182     for (z = 1; z < MAXNOC; z++)
183         emp_initque((struct emp_qelem *)&mi[z]);
184
185     build_mission_list(mi, x, y, mission, victim);
186
187     for (cn = 1; cn < MAXNOC; cn++) {
188         if (cn == victim)
189             continue;
190         if (mission == MI_SINTERDICT) {
191             if (getrel(getnatp(cn), victim) >= FRIENDLY)
192                 continue;
193         } else if (getrel(getnatp(cn), victim) > HOSTILE)
194             continue;
195
196         if (QEMPTY(&mi[cn].queue))
197             continue;
198
199         osubs = only_subs(&mi[cn].queue);
200         newdam = perform_mission(x, y, victim, &mi[cn].queue,
201                                  mission, s, hardtarget);
202         dam += newdam;
203         if (newdam) {
204             /* If only subs responded, then we don't know who's
205                subs they are */
206             if (osubs) {
207                 mpr(victim,
208                     "Enemy interdiction mission does %d damage!\n",
209                     newdam);
210             } else {
211                 mpr(victim,
212                     "%s interdiction mission does %d damage!\n",
213                     cname(cn), newdam);
214             }
215         }
216     }
217     if (dam) {
218         collateral_damage(x, y, dam, 0);
219     }
220     return dam;
221 }
222
223 /*
224  *  Perform a mission against victim, on behalf of actee
225  */
226 int
227 off_support(coord x, coord y, natid victim, natid actee)
228 {
229     int dam = 0;
230     struct genlist mi[MAXNOC];
231     int z;
232
233     memset(mi, 0, sizeof(mi));
234     for (z = 1; z < MAXNOC; z++)
235         emp_initque((struct emp_qelem *)&mi[z]);
236
237     build_mission_list(mi, x, y, MI_SUPPORT, victim);
238     build_mission_list(mi, x, y, MI_OSUPPORT, victim);
239
240     dam = dosupport(mi, x, y, victim, actee);
241     return dam;
242 }
243
244 /*
245  *  Perform a mission against victim, on behalf of actee
246  */
247 int
248 def_support(coord x, coord y, natid victim, natid actee)
249 {
250     int dam = 0;
251     struct genlist mi[MAXNOC];
252     int z;
253
254     memset(mi, 0, sizeof(mi));
255     for (z = 1; z < MAXNOC; z++)
256         emp_initque((struct emp_qelem *)&mi[z]);
257
258     build_mission_list(mi, x, y, MI_SUPPORT, victim);
259     build_mission_list(mi, x, y, MI_DSUPPORT, victim);
260
261     dam = dosupport(mi, x, y, victim, actee);
262     return dam;
263 }
264
265 static int
266 dosupport(struct genlist *mi, coord x, coord y, natid victim, natid actee)
267 {
268     register int cn;
269     int rel;
270     int dam = 0;
271
272     for (cn = 1; cn < MAXNOC; cn++) {
273         rel = getrel(getnatp(cn), actee);
274         if ((cn != actee) && (rel != ALLIED))
275             continue;
276         rel = getrel(getnatp(cn), victim);
277         if ((cn != actee) && (rel != AT_WAR))
278             continue;
279
280         if (QEMPTY(&mi[cn].queue))
281             continue;
282
283         dam += perform_mission(x, y, victim, &mi[cn].queue, MI_SUPPORT,
284                                "", SECT_HARDTARGET);
285     }
286     return dam;
287 }
288
289 static void
290 build_mission_list(struct genlist *mi, coord x, coord y, int mission,
291                    natid victim)
292 {
293     build_mission_list_type(mi, x, y, mission, EF_LAND, victim);
294     build_mission_list_type(mi, x, y, mission, EF_SHIP, victim);
295     build_mission_list_type(mi, x, y, mission, EF_PLANE, victim);
296 }
297
298 static void
299 build_mission_list_type(struct genlist *mi, coord x, coord y, int mission,
300                         int type, natid victim)
301 {
302     struct nstr_item ni;
303     struct genlist *glp;
304     struct genitem *gp;
305     union {
306         struct shpstr u_sp;
307         struct lndstr u_lp;
308         struct plnstr u_pp;
309     } u_block;
310     s_char *block;
311     int dist, size;
312     int radius;
313     int relat;
314     struct sctstr sect;
315
316 /*
317         size = max(sizeof(struct shpstr),sizeof(struct lndstr));
318         size = max(size,sizeof(struct plnstr));
319         block = (s_char *)malloc(size);
320  */
321     size = sizeof(u_block);
322     block = (s_char *)&u_block;
323
324     snxtitem_all(&ni, type);
325     while (nxtitem(&ni, block)) {
326         gp = (struct genitem *)block;
327
328         if (gp->own == 0)
329             continue;
330
331         if (gp->mobil < 1)
332             continue;
333
334         if ((gp->mission != mission) && (mission != MI_SINTERDICT))
335             continue;
336
337         if ((gp->mission != mission) && (mission == MI_SINTERDICT) &&
338             (gp->mission != MI_INTERDICT))
339             continue;
340
341         relat = getrel(getnatp(gp->own), victim);
342         if (mission == MI_SINTERDICT) {
343             if (relat >= FRIENDLY)
344                 continue;
345             else if (type != EF_PLANE && relat > HOSTILE)
346                 continue;
347         } else if (relat > HOSTILE)
348             continue;
349
350         dist = mapdist(x, y, gp->opx, gp->opy);
351
352         radius = gp->radius;
353         if (mission != MI_RESERVE)      /* XXX */
354             oprange(gp, type, &radius);
355
356         if (dist > radius)
357             continue;
358
359         /* Ok, it is within the operations range. */
360         /* Now check from where the object actually is */
361         dist = mapdist(x, y, gp->x, gp->y);
362         radius = 999;
363         oprange(gp, type, &radius);
364         if (dist > radius)
365             continue;
366         /* Ok, the object can get to where the x,y is */
367
368         if (opt_SLOW_WAR) {
369             if (mission != MI_AIR_DEFENSE) {
370                 getsect(x, y, (s_char *)&sect);
371                 if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) {
372
373                     /*
374                      * If the player->owner of the unit isn't at war
375                      * with the victim, and doesn't own the
376                      * sect being acted upon, and isn't the
377                      * old player->owner of that sect, bounce them.
378                      */
379                     if (sect.sct_type != SCT_WATER &&
380                         sect.sct_own != gp->own &&
381                         sect.sct_oldown != gp->own)
382                         continue;
383                 }
384             }
385         }
386
387         glp = (struct genlist *)malloc(sizeof(struct genlist));
388         memset(glp, 0, sizeof(struct genlist));
389         glp->x = gp->x;
390         glp->y = gp->y;
391         glp->type = type;
392         switch (type) {
393         case EF_LAND:
394             glp->cp = (s_char *)&lchr[(int)gp->type];
395             break;
396         case EF_SHIP:
397             glp->cp = (s_char *)&mchr[(int)gp->type];
398             break;
399         case EF_PLANE:
400             glp->cp = (s_char *)&plchr[(int)gp->type];
401             break;
402         }
403         glp->thing = (s_char *)malloc(size);
404         memcpy(glp->thing, block, size);
405         emp_insque(&glp->queue, &mi[gp->own].queue);
406     }
407 }
408
409 static void
410 find_escorts(coord x, coord y, natid cn, struct emp_qelem *escorts)
411 {
412     struct nstr_item ni;
413     struct plist *plp;
414     struct plnstr plane;
415     int dist;
416
417     snxtitem_all(&ni, EF_PLANE);
418     while (nxtitem(&ni, (s_char *)&plane)) {
419         if (plane.pln_own != cn)
420             continue;
421
422         if (plane.pln_mission != MI_ESCORT)
423             continue;
424
425         dist = mapdist(x, y, plane.pln_x, plane.pln_y);
426
427         if (dist > ((int)((float)plane.pln_range / 2.0)))
428             continue;
429
430         plp = (struct plist *)malloc(sizeof(struct plist));
431         memset(plp, 0, sizeof(struct plist));
432         plp->pcp = &plchr[(int)plane.pln_type];
433         plp->plane = plane;
434         emp_insque(&plp->queue, escorts);
435     }
436 }
437
438 static int
439 perform_mission(coord x, coord y, natid victim, struct emp_qelem *list,
440                 int mission, s_char *s, int hardtarget)
441 {
442     extern int land_max_interdiction_range;
443     extern int ship_max_interdiction_range;
444     struct emp_qelem *qp, missiles, bombers, escorts, airp, b, e;
445     struct emp_qelem *newqp;
446     struct genlist *glp;
447     struct plist *plp;
448     struct genitem *gp;
449     struct lndstr *lp;
450     struct shpstr *sp;
451     struct sctstr sect;
452     struct lchrstr *lcp;
453     struct mchrstr *mcp;
454     struct plchrstr *pcp;
455     int dam = 0, dam2, mission_flags, tech;
456     natid plane_owner = 0;
457     int gun, shell, md, air_dam = 0;
458     double range2, prb, range, mobcost, hitchance;
459     extern int torpedo_damage;
460
461     getsect(x, y, &sect);
462
463     emp_initque(&missiles);
464     emp_initque(&bombers);
465     emp_initque(&escorts);
466     emp_initque(&airp);
467
468     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
469         glp = (struct genlist *)qp;
470         gp = (struct genitem *)glp->thing;
471
472         md = mapdist(x, y, gp->x, gp->y);
473
474         if (glp->type == EF_LAND) {
475             lp = (struct lndstr *)glp->thing;
476             lcp = (struct lchrstr *)glp->cp;
477
478             if (lp->lnd_effic < LAND_MINFIREEFF)
479                 continue;
480
481             if (mission == MI_SINTERDICT)
482                 continue;
483
484             if ((mission == MI_INTERDICT) &&
485                 (md > land_max_interdiction_range))
486                 continue;
487
488             if (md > (lp->lnd_frg / 2))
489                 continue;
490
491             if (lnd_getmil(lp) < 1)
492                 continue;
493
494             range = techfact((int)lp->lnd_tech, (double)lp->lnd_frg / 2.0);
495             range2 = (double)roundrange(range);
496
497             if (md > range2)
498                 continue;
499
500             shell = getvar(V_SHELL, (s_char *)lp, EF_LAND);
501             gun = getvar(V_GUN, (s_char *)lp, EF_LAND);
502             if (shell == 0 || gun == 0)
503                 continue;
504
505             if (has_supply(lp)) {
506                 use_supply(lp);
507                 putland(lp->lnd_uid, lp);
508                 dam2 = ldround(landunitgun(lp->lnd_effic, lp->lnd_dam, gun,
509                                            lp->lnd_ammo, shell), 1);
510                 if (sect.sct_type == SCT_WATER) {
511                     double dam3 = (double)dam2;
512                     if (chance(((double)lp->lnd_acc) / 100.0))
513                         dam2 = ldround((dam3 / 2.0), 1);
514                 }
515                 dam += dam2;
516                 if (sect.sct_type == SCT_WATER)
517                     nreport(lp->lnd_own, N_SHP_SHELL, victim, 1);
518                 else
519                     nreport(lp->lnd_own, N_SCT_SHELL, victim, 1);
520                 wu(0, lp->lnd_own,
521                    "%s fires at %s %s at %s\n",
522                    prland(lp), cname(victim), s, xyas(x, y, lp->lnd_own));
523
524                 mpr(victim, "%s %s fires at you at %s\n",
525                     cname(lp->lnd_own), prland(lp), xyas(x, y, victim));
526             }
527         } else if (glp->type == EF_SHIP) {
528             sp = (struct shpstr *)glp->thing;
529             mcp = (struct mchrstr *)glp->cp;
530
531             if (sp->shp_effic < 60)
532                 continue;
533             if (sp->shp_frnge == 0)
534                 continue;
535             if (((mission == MI_INTERDICT) ||
536                  (mission == MI_SINTERDICT)) &&
537                 (md > ship_max_interdiction_range))
538                 continue;
539             if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
540                 continue;
541 /*
542   if ((mcp->m_flags & M_SUB) &&
543   (sect.sct_type != SCT_WATER))
544   continue;
545 */
546             if (mission == MI_SINTERDICT) {
547                 if (!(mcp->m_flags & M_SONAR))
548                     continue;
549                 if (!(mcp->m_flags & M_DCH) && !(mcp->m_flags & M_SUBT))
550                     continue;
551                 range2 = techfact(sp->shp_tech, (double)mcp->m_vrnge);
552                 range2 *= (double)sp->shp_effic / 200.0;
553                 if (md > range2)
554                     continue;
555                 /* can't look all the time */
556                 if (chance(0.5))
557                     continue;
558             }
559             if (mcp->m_flags & M_SUB) {
560 /* If we aren't shooting at "subs" or "ships" don't fire at all from
561    a sub. */
562                 if (*s != 's')
563                     continue;
564                 if (sp->shp_mobil < (s_char)0)
565                     continue;
566                 gun = getvar(V_GUN, (s_char *)sp, EF_SHIP);
567                 if (gun < 1)
568                     continue;
569                 shell = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
570                 if (shell < 3)
571                     shell += supply_commod(sp->shp_own,
572                                            sp->shp_x, sp->shp_y, I_SHELL,
573                                            3 - shell);
574                 if (shell < 3)
575                     continue;
576
577                 range = sp->shp_effic * techfact(sp->shp_tech,
578                                                  ((double)sp->shp_frnge)) /
579                     100.0;
580
581                 range2 = (double)roundrange(range);
582                 if (md > range)
583                     continue;
584
585                 if (!line_of_sight((s_char **)0, x, y, gp->x, gp->y))
586                     continue;
587                 putvar(V_SHELL, shell - 3, (s_char *)sp, EF_SHIP);
588                 mobcost = sp->shp_effic * 0.01 * sp->shp_speed;
589                 mobcost = (480.0 / (mobcost +
590                                     techfact(sp->shp_tech, mobcost)));
591                 sp->shp_mobil -= mobcost;
592                 putship(sp->shp_uid, sp);
593                 hitchance = DTORP_HITCHANCE(md, sp->shp_visib);
594
595                 wu(0, sp->shp_own,
596                    "%s locking on %s %s in %s\n",
597                    prship(sp), cname(victim), s, xyas(x, y, sp->shp_own));
598                 wu(0, sp->shp_own,
599                    "\tEffective torpedo range is %.1f\n", range);
600                 wu(0, sp->shp_own,
601                    "\tWhooosh... Hitchance = %d%%\n",
602                    (int)(hitchance * 100));
603
604                 if (hitchance < 1.0 && !chance(hitchance)) {
605                     wu(0, sp->shp_own, "\tMissed\n");
606                     mpr(victim,
607                         "Incoming torpedo sighted @ %s missed (whew)!\n",
608                         xyas(x, y, victim));
609                     continue;
610                 }
611                 wu(0, sp->shp_own, "\tBOOM!...\n");
612                 dam2 = TORP_DAMAGE();
613
614                 dam += dam2;
615                 nreport(victim, N_TORP_SHIP, sp->shp_own, 1);
616                 wu(0, sp->shp_own,
617                    "\tTorpedo hit %s %s for %d damage\n",
618                    cname(victim), s, dam2);
619
620                 mpr(victim,
621                     "Incoming torpedo sighted @ %s hits and does %d damage!\n",
622                     xyas(x, y, victim), dam2);
623             } else {
624                 range = techfact(sp->shp_tech, (double)mcp->m_frnge / 2.0);
625                 range2 = (double)roundrange(range);
626                 if (md > range2)
627                     continue;
628                 gun = getvar(V_GUN, (s_char *)sp, EF_SHIP);
629                 gun = min(gun, sp->shp_glim);
630                 shell = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
631                 if (shell < gun)
632                     shell += supply_commod(sp->shp_own,
633                                            sp->shp_x, sp->shp_y, I_SHELL,
634                                            gun - shell);
635                 gun = min(gun, shell);
636                 gun = min(gun, (int)((float)getvar(V_MILIT,
637                                                    (s_char *)sp,
638                                                    EF_SHIP) / 2.0));
639                 if (gun == 0)
640                     continue;
641                 gun = max(gun, 1);
642                 dam2 = seagun(sp->shp_effic, gun);
643                 if (range2 == 0.0)
644                     prb = 1.0;
645                 else
646                     prb = ((double)md) / range2;
647                 prb *= prb;
648                 if (chance(prb))
649                     dam2 = (int)((float)dam2 / 2.0);
650                 dam += dam2;
651                 if (sect.sct_type == SCT_WATER)
652                     nreport(sp->shp_own, N_SHP_SHELL, victim, 1);
653                 else
654                     nreport(sp->shp_own, N_SCT_SHELL, victim, 1);
655                 wu(0, sp->shp_own,
656                    "%s fires at %s %s at %s\n",
657                    prship(sp), cname(victim), s, xyas(x, y, sp->shp_own));
658
659                 mpr(victim, "%s fires at you at %s\n",
660                     cname(sp->shp_own), prship(sp), xyas(x, y, victim));
661
662                 putvar(V_SHELL, shell - gun, (s_char *)sp, EF_SHIP);
663                 putship(sp->shp_uid, sp);
664             }
665         } else if (glp->type == EF_PLANE) {
666             pcp = (struct plchrstr *)glp->cp;
667             if (pcp->pl_flags & P_M)
668                 /* units have their own missile interdiction */
669                 if (hardtarget != SECT_HARDTARGET || pcp->pl_flags & P_MAR)
670                     continue;
671
672             /* save planes for later */
673             plp = (struct plist *)malloc(sizeof(struct plist));
674
675             memset(plp, 0, sizeof(struct plist));
676             plp->pcp = pcp;
677             memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
678             if (plp->pcp->pl_flags & P_M)
679                 emp_insque(&plp->queue, &missiles);
680             else
681                 emp_insque(&plp->queue, &bombers);
682             plane_owner = plp->plane.pln_own;
683         }
684     }
685     if (!QEMPTY(&missiles)) {
686         /* I arbitrarily chose 100 mindam -KHS */
687         dam +=
688             msl_launch_mindam(&missiles, x, y, hardtarget, EF_SECTOR, 100,
689                               "sector", victim, mission);
690         qp = missiles.q_forw;
691         while (qp != (&missiles)) {
692             newqp = qp->q_forw;
693             emp_remque(qp);
694             free(qp);
695             qp = newqp;
696         }
697     }
698
699     if (QEMPTY(&bombers)) {
700         qp = list->q_forw;
701         while (qp != list) {
702             glp = (struct genlist *)qp;
703             qp = qp->q_forw;
704
705             free(glp->thing);
706             free((s_char *)glp);
707         }
708         return dam;
709     }
710     /*
711      * If there are planes performing an
712      * interdict or support mission, find
713      * some escorts for them, if possible.
714      * Up to 2 per bomber, if possible.
715      */
716     find_escorts(x, y, plane_owner, &escorts);
717
718     if (mission == MI_SINTERDICT)
719         mission_pln_sel(&bombers, P_T | P_A, 0, hardtarget);
720     else
721         mission_pln_sel(&bombers, P_T, P_A, SECT_HARDTARGET);
722
723     mission_pln_sel(&escorts, P_ESC | P_F, 0, SECT_HARDTARGET);
724
725     for (qp = bombers.q_forw; qp != (&bombers); qp = qp->q_forw) {
726         plp = (struct plist *)qp;
727         if (!find_airport(&airp, plp->plane.pln_x, plp->plane.pln_y))
728             add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
729     }
730
731     for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
732         struct airport *air;
733         s_char pp[512];
734
735         air = (struct airport *)qp;
736         md = mapdist(x, y, air->x, air->y);
737
738         emp_initque(&b);
739         emp_initque(&e);
740
741         /* Split off the bombers at this base into b */
742         divide(&bombers, &b, air->x, air->y);
743
744         /* Split off the escorts at this base into e */
745         divide(&escorts, &e, air->x, air->y);
746
747         tech = 0;
748         mission_flags = 0;
749         mission_flags |= P_X;   /* stealth (shhh) */
750         mission_flags |= P_H;   /* gets turned off if not all choppers */
751
752         mission_flags = mission_pln_arm(&b, air->x, air->y, 2 * md, 'p', 0,
753                                         0, mission_flags, &tech);
754
755         if (QEMPTY(&b)) {
756             continue;
757         }
758
759         mission_flags = mission_pln_arm(&e, air->x, air->y, 2 * md, 'p', 0,
760                                         P_F | P_ESC, mission_flags, &tech);
761
762         BestAirPath(pp, air->x, air->y, x, y);
763         wu(0, plane_owner, "Flying %s mission from %s\n",
764            mission_name(mission), xyas(air->x, air->y, plane_owner));
765         if (air->own && (air->own != plane_owner)) {
766             wu(0, air->own, "%s is flying %s mission from %s\n",
767                cname(plane_owner), mission_name(mission),
768                xyas(air->x, air->y, air->own));
769         }
770
771         ac_encounter(&b, &e, air->x, air->y, pp, mission_flags, 0, 0, 0);
772
773         if (!QEMPTY(&b))
774             air_dam +=
775                 air_damage(&b, x, y, mission, victim, s, hardtarget);
776
777         pln_put(&b);
778         pln_put(&e);
779     }
780
781     if (air_dam > 0) {
782         dam += air_dam;
783         if (sect.sct_type == SCT_WATER)
784             nreport(plane_owner, N_SHP_BOMB, victim, 1);
785         else
786             nreport(plane_owner, N_SCT_BOMB, victim, 1);
787     }
788
789     /* free up all this memory */
790     qp = list->q_forw;
791     while (qp != list) {
792         glp = (struct genlist *)qp;
793         qp = qp->q_forw;
794
795         free(glp->thing);
796         free((s_char *)glp);
797     }
798
799     qp = escorts.q_forw;
800     while (qp != (&escorts)) {
801         newqp = qp->q_forw;
802         emp_remque(qp);
803         free((s_char *)qp);
804         qp = newqp;
805     }
806
807     qp = bombers.q_forw;
808     while (qp != (&bombers)) {
809         newqp = qp->q_forw;
810         emp_remque(qp);
811         free((s_char *)qp);
812         qp = newqp;
813     }
814
815     return dam;
816 }
817
818 int
819 cando(int mission, int type)
820 {
821     switch (mission) {
822     case MI_ESCORT:
823         if (type == EF_PLANE)
824             return 1;
825         return 0;
826     case MI_AIR_DEFENSE:
827         if (type == EF_PLANE)
828             return 1;
829         return 0;
830     case MI_SINTERDICT:
831         if ((type == EF_PLANE) || (type == EF_SHIP))
832             return 1;
833         return 0;
834     case MI_INTERDICT:
835         return 1;
836     case MI_SUPPORT:
837     case MI_OSUPPORT:
838     case MI_DSUPPORT:
839         if (type == EF_PLANE)
840             return 1;
841         return 0;
842     case MI_RESERVE:
843         if (type == EF_LAND)
844             return 1;
845         return 0;
846     }
847
848     return 0;
849 }
850
851 s_char *
852 nameofitem(s_char *buf, struct genitem *gp, int type)
853 {
854     switch (type) {
855     case EF_SHIP:
856         return prship((struct shpstr *)gp);
857         break;
858     case EF_PLANE:
859         return prplane((struct plnstr *)gp);
860         break;
861     case EF_LAND:
862         return prland((struct lndstr *)gp);
863     }
864     return NULL;
865 }
866
867 s_char *
868 mission_short_name(int mission)
869 {
870     switch (mission) {
871     case MI_INTERDICT:
872         return "interdict";
873     case MI_SUPPORT:
874         return "support  ";
875     case MI_OSUPPORT:
876         return "offensive";
877     case MI_DSUPPORT:
878         return "defensive";
879     case MI_RESERVE:
880         return "reserve  ";
881     case MI_ESCORT:
882         return "escort   ";
883     case MI_SINTERDICT:
884         return "interdict";
885     case MI_AIR_DEFENSE:
886         return "air def  ";
887     default:
888         return "         ";
889     }
890 }
891
892 s_char *
893 mission_name(short int mission)
894 {
895     switch (mission) {
896     case MI_INTERDICT:
897         return "an interdiction";
898     case MI_SUPPORT:
899         return "a support";
900     case MI_OSUPPORT:
901         return "a offensive support";
902     case MI_DSUPPORT:
903         return "a defensive support";
904     case MI_RESERVE:
905         return "a reserve";
906     case MI_ESCORT:
907         return "an escort";
908     case MI_SINTERDICT:
909         return "a sub interdiction";
910     case MI_AIR_DEFENSE:
911         return "an air defense";
912     }
913     return "a mysterious";
914 }
915
916 void
917 show_mission(int type, struct nstr_item *np)
918 {
919     int size, first = 1, radius;
920     s_char *block;
921     struct genitem *gp;
922     s_char buf[128];
923
924     size = max(sizeof(struct lndstr), sizeof(struct plnstr));
925     size = max(size, sizeof(struct shpstr));
926     block = (s_char *)malloc(size);
927
928     while (nxtitem(np, block)) {
929         gp = (struct genitem *)block;
930         if (!player->owner || gp->own == 0)
931             continue;
932
933         if (first) {
934             pr("Thing                         x,y   op-sect rad mission\n");
935             first = 0;
936         }
937         pr("%-25s", nameofitem(buf, gp, type));
938         pr(" %7s", xyas(gp->x, gp->y, player->cnum));
939         if (gp->mission == MI_INTERDICT || gp->mission == MI_SUPPORT ||
940             gp->mission == MI_OSUPPORT ||
941             gp->mission == MI_DSUPPORT || gp->mission == MI_AIR_DEFENSE) {
942             radius = 999;
943             oprange(gp, type, &radius);
944             pr(" %7s", xyas(gp->opx, gp->opy, player->cnum));
945             if (radius < gp->radius)
946                 pr("  %4d", radius);
947             else
948                 pr("  %4d", gp->radius);
949         } else if (gp->mission == MI_RESERVE) {
950             struct sctstr sect;
951             int plus = 2;
952
953             getsect(gp->x, gp->y, &sect);
954             if ((sect.sct_type == SCT_HEADQ) && (sect.sct_effic >= 60))
955                 plus++;
956
957             if (((struct lndstr *)block)->lnd_rad_max == 0)
958                 plus = 0;
959             else
960                 plus += ((struct lndstr *)block)->lnd_rad_max;
961             pr(" %7s", xyas(gp->x, gp->y, player->cnum));
962             pr("  %4d", plus);
963         } else if (gp->mission == MI_ESCORT) {
964             pr("        ");
965             pr("  %4d", (int)
966                ((float)((struct plnstr *)block)->pln_range / 2.0)
967                 );
968         } else
969             pr("              ");
970         if (gp->mission)
971             pr(" is on %s mission\n", mission_name(gp->mission));
972         else
973             pr(" has no mission.\n");
974     }
975 }
976
977 int
978 oprange(struct genitem *gp, int type, int *radius)
979 {
980     int range;
981     struct shpstr ship;
982     struct lndstr land;
983     struct plnstr plane;
984
985     switch (type) {
986     case EF_SHIP:
987         getship(gp->uid, &ship);
988         range = ldround(techfact(gp->tech,
989                                  (double)ship.shp_frnge / 2.0), 1);
990         break;
991     case EF_LAND:
992         getland(gp->uid, &land);
993         range = ldround(techfact((int)land.lnd_tech,
994                                  (double)land.lnd_frg / 2.0), 1);
995         break;
996     case EF_PLANE:
997         getplane(gp->uid, &plane);
998         /* missiles go one way, so we can use all the range */
999         if (plchr[(int)plane.pln_type].pl_flags & P_M)
1000             range = plane.pln_range;
1001         else
1002             range = ldround((double)plane.pln_range / 2.0, 1);;
1003         break;
1004     }
1005
1006     if ((*radius) > range)
1007         *radius = range;
1008
1009     return range;
1010 }
1011
1012 /*
1013  *  Remove all planes who cannot go on
1014  *  the mission from the plane list.
1015  */
1016 static void
1017 mission_pln_sel(struct emp_qelem *list, int wantflags, int nowantflags,
1018                 int hardtarget)
1019 {
1020     struct emp_qelem *qp, *next;
1021     struct plnstr *pp;
1022     struct shpstr ship;
1023     struct lndstr land;
1024     struct sctstr sect;
1025     struct plchrstr *pcp;
1026     struct plist *plp;
1027     register int y, bad, bad1;
1028     unsigned int x;
1029
1030     for (qp = list->q_forw; qp != list; qp = next) {
1031         next = qp->q_forw;
1032         plp = (struct plist *)qp;
1033         pp = &plp->plane;
1034         pcp = plp->pcp;
1035
1036         if (pp->pln_effic < 40) {
1037             emp_remque(qp);
1038             free((s_char *)qp);
1039             continue;
1040         }
1041
1042         if (pp->pln_mobil < 1) {
1043             emp_remque(qp);
1044             free((s_char *)qp);
1045             continue;
1046         }
1047
1048         if (opt_MARKET) {
1049             if (ontradingblock(EF_PLANE, (int *)pp)) {
1050                 emp_remque(qp);
1051                 free((s_char *)qp);
1052                 continue;
1053             }
1054         }
1055
1056         bad = 0;
1057         bad1 = 0;
1058         if (wantflags) {
1059             for (x = 0; x < sizeof(wantflags) * 8; x++) {
1060                 y = (1 << x);
1061                 if ((wantflags & y) == y)
1062                     if ((pcp->pl_flags & y) != y) {
1063                         switch (y) {
1064                         case P_F:
1065                         case P_ESC:
1066                             bad1 = 2;
1067                             break;
1068                         case P_E:
1069                         case P_L:
1070                         case P_K:
1071                             bad1 = 1;
1072                             break;
1073                         default:
1074                             bad = 1;
1075                         }
1076                     }
1077             }
1078             if (bad) {
1079                 emp_remque(qp);
1080                 free((s_char *)qp);
1081                 continue;
1082             }
1083             if (bad1 == 2) {
1084                 if ((pcp->pl_flags & P_ESC) || (pcp->pl_flags & P_F))
1085                     bad1 = 0;
1086             }
1087             if (bad1 == 1) {
1088                 if ((pcp->pl_flags & P_E) ||
1089                     (pcp->pl_flags & P_K) || (pcp->pl_flags & P_L))
1090                     bad1 = 0;
1091             }
1092             if (bad1) {
1093                 emp_remque(qp);
1094                 free((s_char *)qp);
1095                 continue;
1096             }
1097         }
1098         bad = 0;
1099         bad1 = 0;
1100         if (nowantflags) {
1101             for (x = 0; x < sizeof(nowantflags) * 8; x++) {
1102                 y = (1 << x);
1103                 if ((nowantflags & y) == y)
1104                     if ((pcp->pl_flags & y) == y)
1105                         bad = 1;
1106             }
1107             if (bad) {
1108                 emp_remque(qp);
1109                 free((s_char *)qp);
1110                 continue;
1111             }
1112         }
1113         if (pp->pln_ship >= 0) {
1114             if (!getship(pp->pln_ship, &ship)) {
1115               shipsunk:
1116                 pp->pln_effic = 0;
1117                 putplane(pp->pln_uid, pp);
1118                 emp_remque(qp);
1119                 free((s_char *)qp);
1120                 continue;
1121             }
1122             if (!can_be_on_ship(pp->pln_uid, ship.shp_uid)) {
1123                 goto shipsunk;
1124             }
1125             if (ship.shp_effic < SHIP_MINEFF) {
1126                 goto shipsunk;
1127             }
1128             /* Can't fly off of ships < 50%, or non-owned or non-allied ships */
1129             if ((ship.shp_effic < 50) ||
1130                 ((ship.shp_own != pp->pln_own) &&
1131                  (getrel(getnatp(ship.shp_own), pp->pln_own) != ALLIED))) {
1132                 emp_remque(qp);
1133                 free((s_char *)qp);
1134                 continue;
1135             }
1136         }
1137         if (pp->pln_land >= 0) {
1138             if (!getland(pp->pln_land, &land)) {
1139               landdead:
1140                 pp->pln_effic = 0;
1141                 putplane(pp->pln_uid, pp);
1142                 emp_remque(qp);
1143                 free((s_char *)qp);
1144                 continue;
1145             }
1146             if (!(pcp->pl_flags & P_E))
1147                 goto landdead;
1148             if (land.lnd_effic < LAND_MINEFF)
1149                 goto landdead;
1150
1151             /* Can't fly off of units < 50%, or non-owned or non-allied units */
1152             if ((land.lnd_effic < 50) ||
1153                 ((land.lnd_own != pp->pln_own) &&
1154                  (getrel(getnatp(land.lnd_own), pp->pln_own) != ALLIED))) {
1155                 emp_remque(qp);
1156                 free((s_char *)qp);
1157                 continue;
1158             }
1159
1160             /* Can't fly off units in ships or other units */
1161             if ((land.lnd_ship >= 0) || (land.lnd_land >= 0)) {
1162                 emp_remque(qp);
1163                 free((s_char *)qp);
1164                 continue;
1165             }
1166         }
1167         /* Now, check the sector status if not on a plane or unit */
1168         if ((pp->pln_ship < 0) && (pp->pln_land < 0)) {
1169             /* If we can't get the sector, we can't check it, and can't fly */
1170             if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
1171                 emp_remque(qp);
1172                 free((s_char *)qp);
1173                 continue;
1174             }
1175             /* First, check allied status */
1176             /* Can't fly from non-owned sectors or non-allied sectors */
1177             if ((sect.sct_own != pp->pln_own) &&
1178                 (getrel(getnatp(sect.sct_own), pp->pln_own) != ALLIED)) {
1179                 emp_remque(qp);
1180                 free((s_char *)qp);
1181                 continue;
1182             }
1183             /* non-vtol plane */
1184             if ((pcp->pl_flags & P_V) == 0) {
1185                 if ((sect.sct_type != SCT_AIRPT) || (sect.sct_effic < 40)) {
1186                     emp_remque(qp);
1187                     free((s_char *)qp);
1188                     continue;
1189                 }
1190             }
1191         }
1192         if (pcp->pl_flags & P_A) {
1193             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1194                 emp_remque(qp);
1195                 free((s_char *)qp);
1196                 continue;
1197             }
1198         }
1199
1200         putplane(pp->pln_uid, pp);
1201     }
1202 }
1203
1204 /*
1205  * Arm only the planes at x,y
1206  *
1207  */
1208 static int
1209 mission_pln_arm(struct emp_qelem *list, coord x, coord y, int dist,
1210                 int mission, struct ichrstr *ip, int flags,
1211                 int mission_flags, int *tech)
1212 {
1213     struct emp_qelem *qp;
1214     struct emp_qelem *next;
1215     struct plist *plp;
1216
1217     if (*tech == 0)
1218         *tech = 9999;
1219     for (qp = list->q_forw; qp != list; qp = next) {
1220         next = qp->q_forw;
1221         plp = (struct plist *)qp;
1222
1223         if (plp->plane.pln_x != x)
1224             continue;
1225         if (plp->plane.pln_y != y)
1226             continue;
1227
1228         if (mission_pln_equip(plp, ip, flags, mission) < 0) {
1229             emp_remque(qp);
1230             free((s_char *)qp);
1231             continue;
1232         }
1233         if (flags & (P_S | P_I)) {
1234             if (plp->pcp->pl_flags & P_S)
1235                 mission_flags |= P_S;
1236             if (plp->pcp->pl_flags & P_I)
1237                 mission_flags |= P_I;
1238         }
1239         if (*tech > plp->plane.pln_tech)
1240             *tech = plp->plane.pln_tech;
1241         if (!(plp->pcp->pl_flags & P_H))
1242             /* no stealth on this mission */
1243             mission_flags &= ~P_H;
1244         if (!(plp->pcp->pl_flags & P_X))
1245             /* no stealth on this mission */
1246             mission_flags &= ~P_X;
1247         if (!(plp->pcp->pl_flags & P_A)) {
1248             /* no asw on this mission */
1249             mission_flags &= ~P_A;
1250         }
1251         if (!(plp->pcp->pl_flags & P_MINE)) {
1252             /* no asw on this mission */
1253             mission_flags &= ~P_MINE;
1254         }
1255
1256         /*
1257          *      Mob costs for missions are 1/2 normal
1258          *       Not anymore. :)
1259          */
1260 /*      plp->plane.pln_mobil -= pln_mobcost(dist,&plp->plane,flags)/2;*/
1261         plp->plane.pln_mobil -= pln_mobcost(dist, &plp->plane, flags);
1262
1263     }
1264     return mission_flags;
1265 }
1266
1267 int
1268 mission_pln_equip(struct plist *plp, struct ichrstr *ip, int flags,
1269                   s_char mission)
1270 {
1271     register struct plchrstr *pcp;
1272     struct plnstr *pp;
1273     int needed;
1274     struct lndstr land;
1275     struct shpstr ship;
1276     struct sctstr sect;
1277     int type;
1278     s_char *ptr;
1279     int item;
1280     int rval;
1281     int vec[I_MAX + 1];
1282
1283     pp = &plp->plane;
1284     pcp = plp->pcp;
1285     if (pp->pln_ship >= 0) {
1286         getship(pp->pln_ship, &ship);
1287         type = EF_SHIP;
1288         ptr = (s_char *)&ship;
1289     } else if (pp->pln_land >= 0) {
1290         getland(pp->pln_land, &land);
1291         type = EF_LAND;
1292         ptr = (s_char *)&land;
1293     } else {
1294         getsect(pp->pln_x, pp->pln_y, &sect);
1295         type = EF_SECTOR;
1296         ptr = (s_char *)&sect;
1297     }
1298     getvec(VT_ITEM, vec, ptr, type);
1299     if (pcp->pl_fuel > vec[I_PETROL]) {
1300         return -1;
1301     }
1302     vec[I_PETROL] -= pcp->pl_fuel;
1303     rval = 0;
1304     if (!(flags & P_F)) {
1305         item = 0;
1306         needed = 0;
1307         switch (mission) {
1308         case 's':
1309         case 'p':
1310             if (pp->pln_nuketype == -1) {
1311                 item = I_SHELL;
1312                 needed = pp->pln_load;
1313             }
1314             break;
1315         case 't':
1316             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1317                 break;
1318             item = ip - ichr;
1319             needed = (pp->pln_load * 2) / ip->i_lbs;
1320             break;
1321         case 'd':
1322             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1323                 break;
1324             item = ip - ichr;
1325             needed = (pp->pln_load * 2) / ip->i_lbs;
1326             break;
1327         case 'a':
1328             if ((pcp->pl_flags & (P_V | P_C)) == 0)
1329                 break;
1330             item = I_MILIT;
1331             needed = pp->pln_load / ip->i_lbs;
1332             break;
1333         case 'n':
1334             if (pp->pln_nuketype == -1)
1335                 rval = -1;
1336             break;
1337         case 'i':               /* missile interception */
1338             if (pp->pln_load) {
1339                 item = I_SHELL;
1340                 needed = pp->pln_load;
1341             }
1342             break;
1343         default:
1344             break;
1345         }
1346         if (rval < 0 || (item && needed <= 0)) {
1347             return -1;
1348         }
1349         if ((vec[item] < needed) && (item == I_SHELL))
1350             vec[item] += supply_commod(plp->plane.pln_own,
1351                                        plp->plane.pln_x, plp->plane.pln_y,
1352                                        I_SHELL, needed);
1353         if (vec[item] < needed) {
1354             return -1;
1355         } else {
1356             vec[item] -= needed;
1357         }
1358         if (item == I_SHELL && (mission == 's' || mission == 'p'))
1359             plp->bombs = needed;
1360         else
1361             plp->misc = needed;
1362     }
1363     putvec(VT_ITEM, vec, ptr, type);
1364     if (type == EF_SHIP)
1365         putship(ship.shp_uid, &ship);
1366     else if (type == EF_LAND)
1367         putland(land.lnd_uid, &land);
1368     else
1369         putsect(&sect);
1370     return rval;
1371 }
1372
1373 /*
1374  *  Return 1 if this x,y pair is in the list
1375  */
1376 static int
1377 find_airport(struct emp_qelem *airp, coord x, coord y)
1378 {
1379     struct emp_qelem *qp;
1380     struct airport *a;
1381
1382     for (qp = airp->q_forw; qp != airp; qp = qp->q_forw) {
1383         a = (struct airport *)qp;
1384         if ((a->x == x) && (a->y == y))
1385             return 1;
1386     }
1387
1388     return 0;
1389 }
1390
1391 /* #*# This needs to be changed to include acc's -KHS */
1392 static void
1393 add_airport(struct emp_qelem *airp, coord x, coord y)
1394 {
1395     struct airport *a;
1396     struct sctstr sect;
1397
1398     a = (struct airport *)malloc(sizeof(struct airport));
1399
1400     a->x = x;
1401     a->y = y;
1402     getsect(x, y, &sect);
1403     a->own = sect.sct_own;
1404
1405     emp_insque((struct emp_qelem *)a, airp);
1406 }
1407
1408 /*
1409  *  Take all the planes in list 1 that
1410  *  are at x,y, and put them into list 2.
1411  */
1412 static void
1413 divide(struct emp_qelem *l1, struct emp_qelem *l2, coord x, coord y)
1414 {
1415     struct emp_qelem *qp, *next;
1416     struct plist *plp;
1417
1418     for (qp = l1->q_forw; qp != l1; qp = next) {
1419         next = qp->q_forw;
1420         plp = (struct plist *)qp;
1421
1422         if (plp->plane.pln_x != x)
1423             continue;
1424         if (plp->plane.pln_y != y)
1425             continue;
1426
1427         emp_remque(qp);
1428         emp_insque(qp, l2);
1429     }
1430 }
1431
1432 static int
1433 air_damage(struct emp_qelem *bombers, coord x, coord y, int mission,
1434            natid victim, s_char *s, int hardtarget)
1435 {
1436     struct emp_qelem *qp;
1437     struct plist *plp;
1438     struct plnstr *pp;
1439     int newdam, dam = 0;
1440     int hitchance;
1441     int nukedam;
1442
1443     for (qp = bombers->q_forw; qp != bombers; qp = qp->q_forw) {
1444         plp = (struct plist *)qp;
1445         pp = &plp->plane;
1446
1447         if ((mission == MI_SINTERDICT) && !(plp->pcp->pl_flags & P_A))
1448             continue;
1449
1450         if (!plp->bombs)
1451             continue;
1452
1453         newdam = 0;
1454         if (plp->pcp->pl_flags & P_A) {
1455             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1456                 wu(0, pp->pln_own,
1457                    "\t%s detects sub movement in %s\n",
1458                    prplane(pp), xyas(x, y, pp->pln_own));
1459                 continue;
1460             }
1461             if (getrel(getnatp(pp->pln_own), victim) > HOSTILE) {
1462                 wu(0, pp->pln_own,
1463                    "\t%s tracks %s %s at %s\n",
1464                    prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1465                 continue;
1466             }
1467             wu(0, pp->pln_own,
1468                "\t%s depth-charging %s %s in %s\n",
1469                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1470         } else {
1471             wu(0, pp->pln_own,
1472                "\t%s pinbombing %s %s in %s\n",
1473                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1474         }
1475         hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
1476         if (plp->plane.pln_nuketype != -1)
1477             hitchance = 100;
1478         else if (hardtarget != SECT_HARDTARGET)
1479             wu(0, pp->pln_own, "\t\t%d%% hitchance...", hitchance);
1480         /* Always calculate damage */
1481         if (roll(100) <= hitchance) {
1482             newdam = pln_damage(&plp->plane, x, y, 'p', &nukedam, 1);
1483             if (nukedam) {
1484                 if (mission == MI_INTERDICT) {
1485                     wu(0, pp->pln_own,
1486                        "\t\tnuclear warhead does %d damage to %s %s\n",
1487                        prplane(pp), nukedam, cname(victim), s);
1488                     dam += nukedam;
1489                 }
1490             } else {
1491                 wu(0, pp->pln_own,
1492                    "\t\thit %s %s for %d damage\n",
1493                    cname(victim), s, newdam);
1494                 dam += newdam;
1495             }
1496         } else {
1497             newdam = pln_damage(&plp->plane, x, y, 'p', &nukedam, 0);
1498             wu(0, pp->pln_own, "missed\n");
1499             if (mission == MI_SINTERDICT) {
1500                 mpr(victim,
1501                     "RUMBLE... your sub in %s hears a depth-charge explode nearby\n",
1502                     xyas(x, y, victim));
1503             } else if (*s == 's') {
1504                 mpr(victim,
1505                     "SPLASH!  Bombs miss your %s in %s\n",
1506                     s, xyas(x, y, victim));
1507             } else {
1508                 mpr(victim, "SPLAT!  Bombs miss your %s in %s\n",
1509                     s, xyas(x, y, victim));
1510             }
1511             /* Now, even though we missed, the bombs
1512                land somewhere. */
1513             collateral_damage(x, y, newdam, bombers);
1514         }
1515
1516         /* use up missiles */
1517         if (plp->pcp->pl_flags & P_M) {
1518             makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x,
1519                      pp->pln_y);
1520             pp->pln_own = 0;
1521         }
1522     }
1523
1524     return dam;
1525 }
1526
1527 /*
1528  * Check to see if anyone hostile to the victim
1529  * is running an air defense mission on this
1530  * sector. If so, do air combat
1531  */
1532 int
1533 air_defense(coord x, coord y, natid victim, struct emp_qelem *bomb_list,
1534             struct emp_qelem *esc_list)
1535 {
1536     int dam = 0, cn;
1537     int mission_flags, tech, combat = 0, rel, dist, z;
1538     struct emp_qelem *qp, interceptors, airp, i, empty, *next;
1539     struct plist *plp;
1540     struct genlist *glp;
1541     struct genitem *gp;
1542     struct genlist mi[MAXNOC];
1543     s_char path[512];
1544     int count;
1545     int tcount;
1546
1547     count = 0;
1548     for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw)
1549         count++;
1550     for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw)
1551         count++;
1552
1553     memset(mi, 0, sizeof(mi));
1554     for (z = 1; z < MAXNOC; z++)
1555         emp_initque((struct emp_qelem *)&mi[z]);
1556
1557     build_mission_list_type(mi, x, y, MI_AIR_DEFENSE, EF_PLANE, victim);
1558
1559     for (cn = 1; cn < MAXNOC; cn++) {
1560         /* Check our relations */
1561         rel = getrel(getnatp(cn), victim);
1562
1563         if (rel > HOSTILE)
1564             continue;
1565
1566         if (QEMPTY(&mi[cn].queue))
1567             continue;
1568
1569         /* Ok, make a list of all the interceptors.  Note that this *copies* the
1570          * list from the mission creation.  This list must be deleted later. */
1571         emp_initque(&interceptors);
1572         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1573             next = qp->q_forw;
1574
1575             glp = (struct genlist *)qp;
1576             gp = (struct genitem *)glp->thing;
1577             plp = (struct plist *)qp;
1578
1579             dist = mapdist(x, y, gp->x, gp->y);
1580
1581             plp = (struct plist *)malloc(sizeof(struct plist));
1582             memset(plp, 0, sizeof(struct plist));
1583             plp->pcp = (struct plchrstr *)glp->cp;
1584             memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
1585
1586             /* missiles go one way, so we can use all the range */
1587             if (!(plp->pcp->pl_flags & P_M))
1588                 dist *= 2;
1589             /* If it's out of range, free it and continue on */
1590             if (dist > plp->plane.pln_range) {
1591                 free(plp);
1592                 continue;
1593             }
1594             emp_insque(&plp->queue, &interceptors);
1595         }
1596
1597         /* Remove those who cannot go */
1598         mission_pln_sel(&interceptors, P_F, 0, SECT_HARDTARGET);
1599
1600         if (QEMPTY(&interceptors))
1601             continue;
1602
1603         /* Now, delete all the extras, but delete the first ones, not the last ones, so
1604          * that the higher numbered planes go into battle (they should be the better ones
1605          * at fighting, if all went well.) */
1606         tcount = 0;
1607         for (qp = interceptors.q_forw; qp != (&interceptors);
1608              qp = qp->q_forw)
1609             tcount++;
1610         tcount -= (count * 2);
1611         /* Just in case there are more incoming than we have */
1612         if (tcount < 0)
1613             tcount = 0;
1614         for (qp = interceptors.q_forw; qp != (&interceptors); qp = next) {
1615             next = qp->q_forw;
1616             if (tcount) {
1617                 tcount--;
1618                 /* Free it up and continue */
1619                 emp_remque(qp);
1620                 glp = (struct genlist *)qp;
1621                 free((s_char *)glp);
1622             }
1623         }
1624
1625         /* Now, make a list of all the airports these planes are coming from */
1626         emp_initque(&airp);
1627         for (qp = interceptors.q_forw; qp != (&interceptors);
1628              qp = qp->q_forw) {
1629             plp = (struct plist *)qp;
1630             if (!find_airport(&airp, plp->plane.pln_x, plp->plane.pln_y))
1631                 add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
1632         }
1633
1634         /* Now, fly them out one airport at a time */
1635         for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
1636             struct airport *air;
1637
1638             air = (struct airport *)qp;
1639             dist = mapdist(x, y, air->x, air->y);
1640
1641             emp_initque(&i);
1642
1643             /* Split off the interceptors at this base into i */
1644             divide(&interceptors, &i, air->x, air->y);
1645
1646             tech = 0;
1647             mission_flags = 0;
1648             mission_flags |= P_X;       /* stealth (shhh) */
1649             /* gets turned off if not all choppers */
1650             mission_flags |= P_H;
1651             sam_intercept(bomb_list, &i, cn, victim, x, y, 0);
1652             sam_intercept(esc_list, &i, cn, victim, x, y, 1);
1653
1654             /* Did we run out of interceptors? */
1655             if (QEMPTY(&i))
1656                 continue;
1657             /* Did we run out of bombers? */
1658             if (QEMPTY(bomb_list)) {
1659                 /* Yes, so we have to put the rest of the interceptors back, and
1660                    then continue, or we leak memory */
1661                 pln_put(&i);
1662                 continue;
1663             }
1664             mission_flags =
1665                 mission_pln_arm(&i, air->x, air->y, 2 * dist, 'r', 0, P_F,
1666                                 mission_flags, &tech);
1667
1668             /* Did we run out of interceptors? */
1669             if (QEMPTY(&i))
1670                 continue;
1671             /* Did we run out of bombers? */
1672             if (QEMPTY(bomb_list)) {
1673                 /* Yes, so we have to put the rest of the interceptors back, and
1674                    then continue, or we leak memory */
1675                 pln_put(&i);
1676                 continue;
1677             }
1678
1679             BestAirPath(path, air->x, air->y, x, y);
1680             wu(0, cn, "Flying %s mission from %s\n",
1681                mission_name(MI_AIR_DEFENSE), xyas(air->x, air->y, cn));
1682             if (air->own && (air->own != cn)) {
1683                 wu(0, air->own, "%s is flying %s mission from %s\n",
1684                    cname(cn), mission_name(MI_AIR_DEFENSE),
1685                    xyas(air->x, air->y, air->own));
1686             }
1687
1688             /* Now, fly the planes to the sector */
1689             emp_initque(&empty);
1690             ac_encounter(&i, &empty, air->x, air->y,
1691                          path, mission_flags, 1, bomb_list, esc_list);
1692
1693             /* If none made it, continue */
1694             if (QEMPTY(&i))
1695                 continue;
1696
1697             /* Some made it, so now they get to try to fight. */
1698             /* Intercept the escorts first */
1699             combat = 0;
1700             if (!QEMPTY(esc_list)) {
1701                 mpr(victim, "%s air defense planes intercept!\n",
1702                     cname(cn));
1703                 ac_combat_headers(victim, cn);
1704                 ac_airtoair(esc_list, &i, air->own);
1705                 combat = 1;
1706             }
1707             /* Now intercept the bombers */
1708             if (!QEMPTY(bomb_list)) {
1709                 if (!combat) {
1710                     mpr(victim, "%s air defense planes intercept!\n",
1711                         cname(cn));
1712                     ac_combat_headers(victim, cn);
1713                 }
1714                 ac_airtoair(bomb_list, &i, air->own);
1715                 PR(cn, "\n");
1716                 PR(victim, "\n");
1717             }
1718
1719             pln_put(&i);
1720         }
1721     }
1722
1723     /* We have to free all of these, if they are still there, otherwise they get
1724        lost and we leak memory all over the place. */
1725     for (cn = 1; cn < MAXNOC; cn++) {
1726         /* free up all this memory if it's still there */
1727         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1728             next = qp->q_forw;
1729             glp = (struct genlist *)qp;
1730             free(glp->thing);
1731             free((s_char *)glp);
1732         }
1733     }
1734
1735     return dam;
1736 }