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