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