]> git.pond.sub.org Git - empserver/blob - src/lib/subs/mission.c
349b63eaf94ff4b2c327298eb2534bf0c8fe1092
[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 = lp->lnd_item[I_SHELL];
496             gun = lp->lnd_item[I_GUN];
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 (sp->shp_item[I_MILIT] < 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 = sp->shp_item[I_GUN];
562                 if (gun < 1)
563                     continue;
564                 shell = sp->shp_item[I_SHELL];
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                 sp->shp_item[I_SHELL] = shell - 3;
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 = sp->shp_item[I_GUN];
624                 gun = min(gun, sp->shp_glim);
625                 shell = sp->shp_item[I_SHELL];
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, sp->shp_item[I_MILIT] / 2.0);
632                 if (gun == 0)
633                     continue;
634                 gun = max(gun, 1);
635                 dam2 = seagun(sp->shp_effic, gun);
636                 if (range2 == 0.0)
637                     prb = 1.0;
638                 else
639                     prb = ((double)md) / range2;
640                 prb *= prb;
641                 if (chance(prb))
642                     dam2 = (int)((float)dam2 / 2.0);
643                 dam += dam2;
644                 if (sect.sct_type == SCT_WATER)
645                     nreport(sp->shp_own, N_SHP_SHELL, victim, 1);
646                 else
647                     nreport(sp->shp_own, N_SCT_SHELL, victim, 1);
648                 wu(0, sp->shp_own,
649                    "%s fires at %s %s at %s\n",
650                    prship(sp), cname(victim), s, xyas(x, y, sp->shp_own));
651
652                 mpr(victim, "%s %s fires at you at %s\n",
653                     cname(sp->shp_own), prship(sp), xyas(x, y, victim));
654
655                 sp->shp_item[I_SHELL] = shell - gun;
656                 putship(sp->shp_uid, sp);
657             }
658         } else if (glp->type == EF_PLANE) {
659             pcp = (struct plchrstr *)glp->cp;
660             if (pcp->pl_flags & P_M)
661                 /* units have their own missile interdiction */
662                 if (hardtarget != SECT_HARDTARGET || pcp->pl_flags & P_MAR)
663                     continue;
664
665             /* save planes for later */
666             plp = (struct plist *)malloc(sizeof(struct plist));
667
668             memset(plp, 0, sizeof(struct plist));
669             plp->pcp = pcp;
670             memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
671             if (plp->pcp->pl_flags & P_M)
672                 emp_insque(&plp->queue, &missiles);
673             else
674                 emp_insque(&plp->queue, &bombers);
675             plane_owner = plp->plane.pln_own;
676         }
677     }
678     if (!QEMPTY(&missiles)) {
679         /* I arbitrarily chose 100 mindam -KHS */
680         dam +=
681             msl_launch_mindam(&missiles, x, y, hardtarget, EF_SECTOR, 100,
682                               "sector", victim, mission);
683         qp = missiles.q_forw;
684         while (qp != (&missiles)) {
685             newqp = qp->q_forw;
686             emp_remque(qp);
687             free(qp);
688             qp = newqp;
689         }
690     }
691
692     if (QEMPTY(&bombers)) {
693         qp = list->q_forw;
694         while (qp != list) {
695             glp = (struct genlist *)qp;
696             qp = qp->q_forw;
697
698             free(glp->thing);
699             free((s_char *)glp);
700         }
701         return dam;
702     }
703     /*
704      * If there are planes performing an
705      * interdict or support mission, find
706      * some escorts for them, if possible.
707      * Up to 2 per bomber, if possible.
708      */
709     find_escorts(x, y, plane_owner, &escorts);
710
711     if (mission == MI_SINTERDICT)
712         mission_pln_sel(&bombers, P_T | P_A, 0, hardtarget);
713     else
714         mission_pln_sel(&bombers, P_T, P_A, SECT_HARDTARGET);
715
716     mission_pln_sel(&escorts, P_ESC | P_F, 0, SECT_HARDTARGET);
717
718     for (qp = bombers.q_forw; qp != (&bombers); qp = qp->q_forw) {
719         plp = (struct plist *)qp;
720         if (!find_airport(&airp, plp->plane.pln_x, plp->plane.pln_y))
721             add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
722     }
723
724     for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
725         struct airport *air;
726         s_char pp[512];
727
728         air = (struct airport *)qp;
729         md = mapdist(x, y, air->x, air->y);
730
731         emp_initque(&b);
732         emp_initque(&e);
733
734         /* Split off the bombers at this base into b */
735         divide(&bombers, &b, air->x, air->y);
736
737         /* Split off the escorts at this base into e */
738         divide(&escorts, &e, air->x, air->y);
739
740         tech = 0;
741         mission_flags = 0;
742         mission_flags |= P_X;   /* stealth (shhh) */
743         mission_flags |= P_H;   /* gets turned off if not all choppers */
744
745         mission_flags = mission_pln_arm(&b, air->x, air->y, 2 * md, 'p', 0,
746                                         0, mission_flags, &tech);
747
748         if (QEMPTY(&b)) {
749             continue;
750         }
751
752         mission_flags = mission_pln_arm(&e, air->x, air->y, 2 * md, 'p', 0,
753                                         P_F | P_ESC, mission_flags, &tech);
754
755         BestAirPath(pp, air->x, air->y, x, y);
756         wu(0, plane_owner, "Flying %s mission from %s\n",
757            mission_name(mission), xyas(air->x, air->y, plane_owner));
758         if (air->own && (air->own != plane_owner)) {
759             wu(0, air->own, "%s is flying %s mission from %s\n",
760                cname(plane_owner), mission_name(mission),
761                xyas(air->x, air->y, air->own));
762         }
763
764         ac_encounter(&b, &e, air->x, air->y, pp, mission_flags, 0, 0, 0);
765
766         if (!QEMPTY(&b))
767             air_dam +=
768                 air_damage(&b, x, y, mission, victim, s, hardtarget);
769
770         pln_put(&b);
771         pln_put(&e);
772     }
773
774     if (air_dam > 0) {
775         dam += air_dam;
776         if (sect.sct_type == SCT_WATER)
777             nreport(plane_owner, N_SHP_BOMB, victim, 1);
778         else
779             nreport(plane_owner, N_SCT_BOMB, victim, 1);
780     }
781
782     /* free up all this memory */
783     qp = list->q_forw;
784     while (qp != list) {
785         glp = (struct genlist *)qp;
786         qp = qp->q_forw;
787
788         free(glp->thing);
789         free((s_char *)glp);
790     }
791
792     qp = escorts.q_forw;
793     while (qp != (&escorts)) {
794         newqp = qp->q_forw;
795         emp_remque(qp);
796         free((s_char *)qp);
797         qp = newqp;
798     }
799
800     qp = bombers.q_forw;
801     while (qp != (&bombers)) {
802         newqp = qp->q_forw;
803         emp_remque(qp);
804         free((s_char *)qp);
805         qp = newqp;
806     }
807
808     return dam;
809 }
810
811 int
812 cando(int mission, int type)
813 {
814     switch (mission) {
815     case MI_ESCORT:
816         if (type == EF_PLANE)
817             return 1;
818         return 0;
819     case MI_AIR_DEFENSE:
820         if (type == EF_PLANE)
821             return 1;
822         return 0;
823     case MI_SINTERDICT:
824         if ((type == EF_PLANE) || (type == EF_SHIP))
825             return 1;
826         return 0;
827     case MI_INTERDICT:
828         return 1;
829     case MI_SUPPORT:
830     case MI_OSUPPORT:
831     case MI_DSUPPORT:
832         if (type == EF_PLANE)
833             return 1;
834         return 0;
835     case MI_RESERVE:
836         if (type == EF_LAND)
837             return 1;
838         return 0;
839     }
840
841     return 0;
842 }
843
844 s_char *
845 nameofitem(struct genitem *gp, int type)
846 {
847     switch (type) {
848     case EF_SHIP:
849         return prship((struct shpstr *)gp);
850     case EF_PLANE:
851         return prplane((struct plnstr *)gp);
852     case EF_LAND:
853         return prland((struct lndstr *)gp);
854     }
855     return NULL;
856 }
857
858 s_char *
859 mission_short_name(int mission)
860 {
861     switch (mission) {
862     case MI_INTERDICT:
863         return "interdict";
864     case MI_SUPPORT:
865         return "support  ";
866     case MI_OSUPPORT:
867         return "offensive";
868     case MI_DSUPPORT:
869         return "defensive";
870     case MI_RESERVE:
871         return "reserve  ";
872     case MI_ESCORT:
873         return "escort   ";
874     case MI_SINTERDICT:
875         return "interdict";
876     case MI_AIR_DEFENSE:
877         return "air def  ";
878     default:
879         return "         ";
880     }
881 }
882
883 s_char *
884 mission_name(short int mission)
885 {
886     switch (mission) {
887     case MI_INTERDICT:
888         return "an interdiction";
889     case MI_SUPPORT:
890         return "a support";
891     case MI_OSUPPORT:
892         return "a offensive support";
893     case MI_DSUPPORT:
894         return "a defensive support";
895     case MI_RESERVE:
896         return "a reserve";
897     case MI_ESCORT:
898         return "an escort";
899     case MI_SINTERDICT:
900         return "a sub interdiction";
901     case MI_AIR_DEFENSE:
902         return "an air defense";
903     }
904     return "a mysterious";
905 }
906
907 void
908 show_mission(int type, struct nstr_item *np)
909 {
910     int size, first = 1, radius;
911     s_char *block;
912     struct genitem *gp;
913
914     size = max(sizeof(struct lndstr), sizeof(struct plnstr));
915     size = max(size, sizeof(struct shpstr));
916     block = (s_char *)malloc(size);
917
918     while (nxtitem(np, block)) {
919         gp = (struct genitem *)block;
920         if (!player->owner || gp->own == 0)
921             continue;
922
923         if (first) {
924             pr("Thing                         x,y   op-sect rad mission\n");
925             first = 0;
926         }
927         pr("%-25s", nameofitem(gp, type));
928         pr(" %7s", xyas(gp->x, gp->y, player->cnum));
929         if (gp->mission == MI_INTERDICT || gp->mission == MI_SUPPORT ||
930             gp->mission == MI_OSUPPORT ||
931             gp->mission == MI_DSUPPORT || gp->mission == MI_AIR_DEFENSE) {
932             radius = 999;
933             oprange(gp, type, &radius);
934             pr(" %7s", xyas(gp->opx, gp->opy, player->cnum));
935             if (radius < gp->radius)
936                 pr("  %4d", radius);
937             else
938                 pr("  %4d", gp->radius);
939         } else if (gp->mission == MI_RESERVE) {
940             struct sctstr sect;
941             int plus = 2;
942
943             getsect(gp->x, gp->y, &sect);
944             if ((sect.sct_type == SCT_HEADQ) && (sect.sct_effic >= 60))
945                 plus++;
946
947             if (((struct lndstr *)block)->lnd_rad_max == 0)
948                 plus = 0;
949             else
950                 plus += ((struct lndstr *)block)->lnd_rad_max;
951             pr(" %7s", xyas(gp->x, gp->y, player->cnum));
952             pr("  %4d", plus);
953         } else if (gp->mission == MI_ESCORT) {
954             pr("        ");
955             pr("  %4d", (int)
956                ((float)((struct plnstr *)block)->pln_range / 2.0)
957                 );
958         } else
959             pr("              ");
960         if (gp->mission)
961             pr(" is on %s mission\n", mission_name(gp->mission));
962         else
963             pr(" has no mission.\n");
964     }
965 }
966
967 int
968 oprange(struct genitem *gp, int type, int *radius)
969 {
970     int range;
971     struct shpstr ship;
972     struct lndstr land;
973     struct plnstr plane;
974
975     switch (type) {
976     case EF_SHIP:
977         getship(gp->uid, &ship);
978         range = ldround(techfact(gp->tech,
979                                  (double)ship.shp_frnge / 2.0), 1);
980         break;
981     case EF_LAND:
982         getland(gp->uid, &land);
983         range = ldround(techfact((int)land.lnd_tech,
984                                  (double)land.lnd_frg / 2.0), 1);
985         break;
986     case EF_PLANE:
987         getplane(gp->uid, &plane);
988         /* missiles go one way, so we can use all the range */
989         if (plchr[(int)plane.pln_type].pl_flags & P_M)
990             range = plane.pln_range;
991         else
992             range = ldround((double)plane.pln_range / 2.0, 1);;
993         break;
994     }
995
996     if ((*radius) > range)
997         *radius = range;
998
999     return range;
1000 }
1001
1002 /*
1003  *  Remove all planes who cannot go on
1004  *  the mission from the plane list.
1005  */
1006 static void
1007 mission_pln_sel(struct emp_qelem *list, int wantflags, int nowantflags,
1008                 int hardtarget)
1009 {
1010     struct emp_qelem *qp, *next;
1011     struct plnstr *pp;
1012     struct shpstr ship;
1013     struct lndstr land;
1014     struct sctstr sect;
1015     struct plchrstr *pcp;
1016     struct plist *plp;
1017     register int y, bad, bad1;
1018     unsigned int x;
1019
1020     for (qp = list->q_forw; qp != list; qp = next) {
1021         next = qp->q_forw;
1022         plp = (struct plist *)qp;
1023         pp = &plp->plane;
1024         pcp = plp->pcp;
1025
1026         if (pp->pln_effic < 40) {
1027             emp_remque(qp);
1028             free((s_char *)qp);
1029             continue;
1030         }
1031
1032         if (pp->pln_mobil < 1) {
1033             emp_remque(qp);
1034             free((s_char *)qp);
1035             continue;
1036         }
1037
1038         if (opt_MARKET) {
1039             if (ontradingblock(EF_PLANE, (int *)pp)) {
1040                 emp_remque(qp);
1041                 free((s_char *)qp);
1042                 continue;
1043             }
1044         }
1045
1046         bad = 0;
1047         bad1 = 0;
1048         if (wantflags) {
1049             for (x = 0; x < sizeof(wantflags) * 8; x++) {
1050                 y = (1 << x);
1051                 if ((wantflags & y) == y)
1052                     if ((pcp->pl_flags & y) != y) {
1053                         switch (y) {
1054                         case P_F:
1055                         case P_ESC:
1056                             bad1 = 2;
1057                             break;
1058                         case P_E:
1059                         case P_L:
1060                         case P_K:
1061                             bad1 = 1;
1062                             break;
1063                         default:
1064                             bad = 1;
1065                         }
1066                     }
1067             }
1068             if (bad) {
1069                 emp_remque(qp);
1070                 free((s_char *)qp);
1071                 continue;
1072             }
1073             if (bad1 == 2) {
1074                 if ((pcp->pl_flags & P_ESC) || (pcp->pl_flags & P_F))
1075                     bad1 = 0;
1076             }
1077             if (bad1 == 1) {
1078                 if ((pcp->pl_flags & P_E) ||
1079                     (pcp->pl_flags & P_K) || (pcp->pl_flags & P_L))
1080                     bad1 = 0;
1081             }
1082             if (bad1) {
1083                 emp_remque(qp);
1084                 free((s_char *)qp);
1085                 continue;
1086             }
1087         }
1088         bad = 0;
1089         bad1 = 0;
1090         if (nowantflags) {
1091             for (x = 0; x < sizeof(nowantflags) * 8; x++) {
1092                 y = (1 << x);
1093                 if ((nowantflags & y) == y)
1094                     if ((pcp->pl_flags & y) == y)
1095                         bad = 1;
1096             }
1097             if (bad) {
1098                 emp_remque(qp);
1099                 free((s_char *)qp);
1100                 continue;
1101             }
1102         }
1103         if (pp->pln_ship >= 0) {
1104             if (!getship(pp->pln_ship, &ship)) {
1105               shipsunk:
1106                 pp->pln_effic = 0;
1107                 putplane(pp->pln_uid, pp);
1108                 emp_remque(qp);
1109                 free((s_char *)qp);
1110                 continue;
1111             }
1112             if (!can_be_on_ship(pp->pln_uid, ship.shp_uid)) {
1113                 goto shipsunk;
1114             }
1115             if (ship.shp_effic < SHIP_MINEFF) {
1116                 goto shipsunk;
1117             }
1118             /* Can't fly off of ships < 50%, or non-owned or non-allied ships */
1119             if ((ship.shp_effic < 50) ||
1120                 ((ship.shp_own != pp->pln_own) &&
1121                  (getrel(getnatp(ship.shp_own), pp->pln_own) != ALLIED))) {
1122                 emp_remque(qp);
1123                 free((s_char *)qp);
1124                 continue;
1125             }
1126         }
1127         if (pp->pln_land >= 0) {
1128             if (!getland(pp->pln_land, &land)) {
1129               landdead:
1130                 pp->pln_effic = 0;
1131                 putplane(pp->pln_uid, pp);
1132                 emp_remque(qp);
1133                 free((s_char *)qp);
1134                 continue;
1135             }
1136             if (!(pcp->pl_flags & P_E))
1137                 goto landdead;
1138             if (land.lnd_effic < LAND_MINEFF)
1139                 goto landdead;
1140
1141             /* Can't fly off of units < 50%, or non-owned or non-allied units */
1142             if ((land.lnd_effic < 50) ||
1143                 ((land.lnd_own != pp->pln_own) &&
1144                  (getrel(getnatp(land.lnd_own), pp->pln_own) != ALLIED))) {
1145                 emp_remque(qp);
1146                 free((s_char *)qp);
1147                 continue;
1148             }
1149
1150             /* Can't fly off units in ships or other units */
1151             if ((land.lnd_ship >= 0) || (land.lnd_land >= 0)) {
1152                 emp_remque(qp);
1153                 free((s_char *)qp);
1154                 continue;
1155             }
1156         }
1157         /* Now, check the sector status if not on a plane or unit */
1158         if ((pp->pln_ship < 0) && (pp->pln_land < 0)) {
1159             /* If we can't get the sector, we can't check it, and can't fly */
1160             if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
1161                 emp_remque(qp);
1162                 free((s_char *)qp);
1163                 continue;
1164             }
1165             /* First, check allied status */
1166             /* Can't fly from non-owned sectors or non-allied sectors */
1167             if ((sect.sct_own != pp->pln_own) &&
1168                 (getrel(getnatp(sect.sct_own), pp->pln_own) != ALLIED)) {
1169                 emp_remque(qp);
1170                 free((s_char *)qp);
1171                 continue;
1172             }
1173             /* non-vtol plane */
1174             if ((pcp->pl_flags & P_V) == 0) {
1175                 if ((sect.sct_type != SCT_AIRPT) || (sect.sct_effic < 40)) {
1176                     emp_remque(qp);
1177                     free((s_char *)qp);
1178                     continue;
1179                 }
1180             }
1181         }
1182         if (pcp->pl_flags & P_A) {
1183             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1184                 emp_remque(qp);
1185                 free((s_char *)qp);
1186                 continue;
1187             }
1188         }
1189
1190         putplane(pp->pln_uid, pp);
1191     }
1192 }
1193
1194 /*
1195  * Arm only the planes at x,y
1196  *
1197  */
1198 static int
1199 mission_pln_arm(struct emp_qelem *list, coord x, coord y, int dist,
1200                 int mission, struct ichrstr *ip, int flags,
1201                 int mission_flags, int *tech)
1202 {
1203     struct emp_qelem *qp;
1204     struct emp_qelem *next;
1205     struct plist *plp;
1206
1207     if (*tech == 0)
1208         *tech = 9999;
1209     for (qp = list->q_forw; qp != list; qp = next) {
1210         next = qp->q_forw;
1211         plp = (struct plist *)qp;
1212
1213         if (plp->plane.pln_x != x)
1214             continue;
1215         if (plp->plane.pln_y != y)
1216             continue;
1217
1218         if (mission_pln_equip(plp, ip, flags, mission) < 0) {
1219             emp_remque(qp);
1220             free((s_char *)qp);
1221             continue;
1222         }
1223         if (flags & (P_S | P_I)) {
1224             if (plp->pcp->pl_flags & P_S)
1225                 mission_flags |= P_S;
1226             if (plp->pcp->pl_flags & P_I)
1227                 mission_flags |= P_I;
1228         }
1229         if (*tech > plp->plane.pln_tech)
1230             *tech = plp->plane.pln_tech;
1231         if (!(plp->pcp->pl_flags & P_H))
1232             /* no stealth on this mission */
1233             mission_flags &= ~P_H;
1234         if (!(plp->pcp->pl_flags & P_X))
1235             /* no stealth on this mission */
1236             mission_flags &= ~P_X;
1237         if (!(plp->pcp->pl_flags & P_A)) {
1238             /* no asw on this mission */
1239             mission_flags &= ~P_A;
1240         }
1241         if (!(plp->pcp->pl_flags & P_MINE)) {
1242             /* no asw on this mission */
1243             mission_flags &= ~P_MINE;
1244         }
1245
1246         /*
1247          *      Mob costs for missions are 1/2 normal
1248          *       Not anymore. :)
1249          */
1250 /*      plp->plane.pln_mobil -= pln_mobcost(dist,&plp->plane,flags)/2;*/
1251         plp->plane.pln_mobil -= pln_mobcost(dist, &plp->plane, flags);
1252
1253     }
1254     return mission_flags;
1255 }
1256
1257 int
1258 mission_pln_equip(struct plist *plp, struct ichrstr *ip, int flags,
1259                   s_char mission)
1260 {
1261     register struct plchrstr *pcp;
1262     struct plnstr *pp;
1263     int needed;
1264     struct lndstr land;
1265     struct shpstr ship;
1266     struct sctstr sect;
1267     int type;
1268     s_char *ptr;
1269     int item;
1270     int rval;
1271     int vec[I_MAX + 1];
1272
1273     pp = &plp->plane;
1274     pcp = plp->pcp;
1275     if (pp->pln_ship >= 0) {
1276         getship(pp->pln_ship, &ship);
1277         type = EF_SHIP;
1278         ptr = (s_char *)&ship;
1279     } else if (pp->pln_land >= 0) {
1280         getland(pp->pln_land, &land);
1281         type = EF_LAND;
1282         ptr = (s_char *)&land;
1283     } else {
1284         getsect(pp->pln_x, pp->pln_y, &sect);
1285         type = EF_SECTOR;
1286         ptr = (s_char *)&sect;
1287     }
1288     getvec(VT_ITEM, vec, ptr, type);
1289     if (pcp->pl_fuel > vec[I_PETROL]) {
1290         return -1;
1291     }
1292     vec[I_PETROL] -= pcp->pl_fuel;
1293     rval = 0;
1294     if (!(flags & P_F)) {
1295         item = 0;
1296         needed = 0;
1297         switch (mission) {
1298         case 's':
1299         case 'p':
1300             if (pp->pln_nuketype == -1) {
1301                 item = I_SHELL;
1302                 needed = pp->pln_load;
1303             }
1304             break;
1305         case 't':
1306             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1307                 break;
1308             item = ip - ichr;
1309             needed = (pp->pln_load * 2) / ip->i_lbs;
1310             break;
1311         case 'd':
1312             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1313                 break;
1314             item = ip - ichr;
1315             needed = (pp->pln_load * 2) / ip->i_lbs;
1316             break;
1317         case 'a':
1318             if ((pcp->pl_flags & (P_V | P_C)) == 0)
1319                 break;
1320             item = I_MILIT;
1321             needed = pp->pln_load / ip->i_lbs;
1322             break;
1323         case 'n':
1324             if (pp->pln_nuketype == -1)
1325                 rval = -1;
1326             break;
1327         case 'i':               /* missile interception */
1328             if (pp->pln_load) {
1329                 item = I_SHELL;
1330                 needed = pp->pln_load;
1331             }
1332             break;
1333         default:
1334             break;
1335         }
1336         if (rval < 0 || (item && needed <= 0)) {
1337             return -1;
1338         }
1339         if ((vec[item] < needed) && (item == I_SHELL))
1340             vec[item] += supply_commod(plp->plane.pln_own,
1341                                        plp->plane.pln_x, plp->plane.pln_y,
1342                                        I_SHELL, needed);
1343         if (vec[item] < needed) {
1344             return -1;
1345         } else {
1346             vec[item] -= needed;
1347         }
1348         if (item == I_SHELL && (mission == 's' || mission == 'p'))
1349             plp->bombs = needed;
1350         else
1351             plp->misc = needed;
1352     }
1353     putvec(VT_ITEM, vec, ptr, type);
1354     if (type == EF_SHIP)
1355         putship(ship.shp_uid, &ship);
1356     else if (type == EF_LAND)
1357         putland(land.lnd_uid, &land);
1358     else
1359         putsect(&sect);
1360     return rval;
1361 }
1362
1363 /*
1364  *  Return 1 if this x,y pair is in the list
1365  */
1366 static int
1367 find_airport(struct emp_qelem *airp, coord x, coord y)
1368 {
1369     struct emp_qelem *qp;
1370     struct airport *a;
1371
1372     for (qp = airp->q_forw; qp != airp; qp = qp->q_forw) {
1373         a = (struct airport *)qp;
1374         if ((a->x == x) && (a->y == y))
1375             return 1;
1376     }
1377
1378     return 0;
1379 }
1380
1381 /* #*# This needs to be changed to include acc's -KHS */
1382 static void
1383 add_airport(struct emp_qelem *airp, coord x, coord y)
1384 {
1385     struct airport *a;
1386     struct sctstr sect;
1387
1388     a = (struct airport *)malloc(sizeof(struct airport));
1389
1390     a->x = x;
1391     a->y = y;
1392     getsect(x, y, &sect);
1393     a->own = sect.sct_own;
1394
1395     emp_insque((struct emp_qelem *)a, airp);
1396 }
1397
1398 /*
1399  *  Take all the planes in list 1 that
1400  *  are at x,y, and put them into list 2.
1401  */
1402 static void
1403 divide(struct emp_qelem *l1, struct emp_qelem *l2, coord x, coord y)
1404 {
1405     struct emp_qelem *qp, *next;
1406     struct plist *plp;
1407
1408     for (qp = l1->q_forw; qp != l1; qp = next) {
1409         next = qp->q_forw;
1410         plp = (struct plist *)qp;
1411
1412         if (plp->plane.pln_x != x)
1413             continue;
1414         if (plp->plane.pln_y != y)
1415             continue;
1416
1417         emp_remque(qp);
1418         emp_insque(qp, l2);
1419     }
1420 }
1421
1422 static int
1423 air_damage(struct emp_qelem *bombers, coord x, coord y, int mission,
1424            natid victim, s_char *s, int hardtarget)
1425 {
1426     struct emp_qelem *qp;
1427     struct plist *plp;
1428     struct plnstr *pp;
1429     int newdam, dam = 0;
1430     int hitchance;
1431     int nukedam;
1432
1433     for (qp = bombers->q_forw; qp != bombers; qp = qp->q_forw) {
1434         plp = (struct plist *)qp;
1435         pp = &plp->plane;
1436
1437         if ((mission == MI_SINTERDICT) && !(plp->pcp->pl_flags & P_A))
1438             continue;
1439
1440         if (!plp->bombs)
1441             continue;
1442
1443         newdam = 0;
1444         if (plp->pcp->pl_flags & P_A) {
1445             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1446                 wu(0, pp->pln_own,
1447                    "\t%s detects sub movement in %s\n",
1448                    prplane(pp), xyas(x, y, pp->pln_own));
1449                 continue;
1450             }
1451             if (getrel(getnatp(pp->pln_own), victim) > HOSTILE) {
1452                 wu(0, pp->pln_own,
1453                    "\t%s tracks %s %s at %s\n",
1454                    prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1455                 continue;
1456             }
1457             wu(0, pp->pln_own,
1458                "\t%s depth-charging %s %s in %s\n",
1459                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1460         } else {
1461             wu(0, pp->pln_own,
1462                "\t%s pinbombing %s %s in %s\n",
1463                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1464         }
1465         hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
1466         if (plp->plane.pln_nuketype != -1)
1467             hitchance = 100;
1468         else if (hardtarget != SECT_HARDTARGET)
1469             wu(0, pp->pln_own, "\t\t%d%% hitchance...", hitchance);
1470         /* Always calculate damage */
1471         if (roll(100) <= hitchance) {
1472             newdam = pln_damage(&plp->plane, x, y, 'p', &nukedam, 1);
1473             if (nukedam) {
1474                 if (mission == MI_INTERDICT) {
1475                     wu(0, pp->pln_own,
1476                        "\t\tnuclear warhead on plane %s does %d damage to %s %s\n",
1477                        prplane(pp), nukedam, cname(victim), s);
1478                     dam += nukedam;
1479                 }
1480             } else {
1481                 wu(0, pp->pln_own,
1482                    "\t\thit %s %s for %d damage\n",
1483                    cname(victim), s, newdam);
1484                 dam += newdam;
1485             }
1486         } else {
1487             newdam = pln_damage(&plp->plane, x, y, 'p', &nukedam, 0);
1488             wu(0, pp->pln_own, "missed\n");
1489             if (mission == MI_SINTERDICT) {
1490                 mpr(victim,
1491                     "RUMBLE... your sub in %s hears a depth-charge explode nearby\n",
1492                     xyas(x, y, victim));
1493             } else if (*s == 's') {
1494                 mpr(victim,
1495                     "SPLASH!  Bombs miss your %s in %s\n",
1496                     s, xyas(x, y, victim));
1497             } else {
1498                 mpr(victim, "SPLAT!  Bombs miss your %s in %s\n",
1499                     s, xyas(x, y, victim));
1500             }
1501             /* Now, even though we missed, the bombs
1502                land somewhere. */
1503             collateral_damage(x, y, newdam, bombers);
1504         }
1505
1506         /* use up missiles */
1507         if (plp->pcp->pl_flags & P_M) {
1508             makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x,
1509                      pp->pln_y);
1510             pp->pln_own = 0;
1511         }
1512     }
1513
1514     return dam;
1515 }
1516
1517 /*
1518  * Check to see if anyone hostile to the victim
1519  * is running an air defense mission on this
1520  * sector. If so, do air combat
1521  */
1522 int
1523 air_defense(coord x, coord y, natid victim, struct emp_qelem *bomb_list,
1524             struct emp_qelem *esc_list)
1525 {
1526     int dam = 0, cn;
1527     int mission_flags, tech, combat = 0, rel, dist, z;
1528     struct emp_qelem *qp, interceptors, airp, i, empty, *next;
1529     struct plist *plp;
1530     struct genlist *glp;
1531     struct genitem *gp;
1532     struct genlist mi[MAXNOC];
1533     s_char path[512];
1534     int count;
1535     int tcount;
1536
1537     count = 0;
1538     for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw)
1539         count++;
1540     for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw)
1541         count++;
1542
1543     memset(mi, 0, sizeof(mi));
1544     for (z = 1; z < MAXNOC; z++)
1545         emp_initque((struct emp_qelem *)&mi[z]);
1546
1547     build_mission_list_type(mi, x, y, MI_AIR_DEFENSE, EF_PLANE, victim);
1548
1549     for (cn = 1; cn < MAXNOC; cn++) {
1550         /* Check our relations */
1551         rel = getrel(getnatp(cn), victim);
1552
1553         if (rel > HOSTILE)
1554             continue;
1555
1556         if (QEMPTY(&mi[cn].queue))
1557             continue;
1558
1559         /* Ok, make a list of all the interceptors.  Note that this *copies* the
1560          * list from the mission creation.  This list must be deleted later. */
1561         emp_initque(&interceptors);
1562         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1563             next = qp->q_forw;
1564
1565             glp = (struct genlist *)qp;
1566             gp = (struct genitem *)glp->thing;
1567             plp = (struct plist *)qp;
1568
1569             dist = mapdist(x, y, gp->x, gp->y);
1570
1571             plp = (struct plist *)malloc(sizeof(struct plist));
1572             memset(plp, 0, sizeof(struct plist));
1573             plp->pcp = (struct plchrstr *)glp->cp;
1574             memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
1575
1576             /* missiles go one way, so we can use all the range */
1577             if (!(plp->pcp->pl_flags & P_M))
1578                 dist *= 2;
1579             /* If it's out of range, free it and continue on */
1580             if (dist > plp->plane.pln_range) {
1581                 free(plp);
1582                 continue;
1583             }
1584             emp_insque(&plp->queue, &interceptors);
1585         }
1586
1587         /* Remove those who cannot go */
1588         mission_pln_sel(&interceptors, P_F, 0, SECT_HARDTARGET);
1589
1590         if (QEMPTY(&interceptors))
1591             continue;
1592
1593         /* Now, delete all the extras, but delete the first ones, not the last ones, so
1594          * that the higher numbered planes go into battle (they should be the better ones
1595          * at fighting, if all went well.) */
1596         tcount = 0;
1597         for (qp = interceptors.q_forw; qp != (&interceptors);
1598              qp = qp->q_forw)
1599             tcount++;
1600         tcount -= (count * 2);
1601         /* Just in case there are more incoming than we have */
1602         if (tcount < 0)
1603             tcount = 0;
1604         for (qp = interceptors.q_forw; qp != (&interceptors); qp = next) {
1605             next = qp->q_forw;
1606             if (tcount) {
1607                 tcount--;
1608                 /* Free it up and continue */
1609                 emp_remque(qp);
1610                 glp = (struct genlist *)qp;
1611                 free((s_char *)glp);
1612             }
1613         }
1614
1615         /* Now, make a list of all the airports these planes are coming from */
1616         emp_initque(&airp);
1617         for (qp = interceptors.q_forw; qp != (&interceptors);
1618              qp = qp->q_forw) {
1619             plp = (struct plist *)qp;
1620             if (!find_airport(&airp, plp->plane.pln_x, plp->plane.pln_y))
1621                 add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
1622         }
1623
1624         /* Now, fly them out one airport at a time */
1625         for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
1626             struct airport *air;
1627
1628             air = (struct airport *)qp;
1629             dist = mapdist(x, y, air->x, air->y);
1630
1631             emp_initque(&i);
1632
1633             /* Split off the interceptors at this base into i */
1634             divide(&interceptors, &i, air->x, air->y);
1635
1636             tech = 0;
1637             mission_flags = 0;
1638             mission_flags |= P_X;       /* stealth (shhh) */
1639             /* gets turned off if not all choppers */
1640             mission_flags |= P_H;
1641             sam_intercept(bomb_list, &i, cn, victim, x, y, 0);
1642             sam_intercept(esc_list, &i, cn, victim, x, y, 1);
1643
1644             /* Did we run out of interceptors? */
1645             if (QEMPTY(&i))
1646                 continue;
1647             /* Did we run out of bombers? */
1648             if (QEMPTY(bomb_list)) {
1649                 /* Yes, so we have to put the rest of the interceptors back, and
1650                    then continue, or we leak memory */
1651                 pln_put(&i);
1652                 continue;
1653             }
1654             mission_flags =
1655                 mission_pln_arm(&i, air->x, air->y, 2 * dist, 'r', 0, P_F,
1656                                 mission_flags, &tech);
1657
1658             /* Did we run out of interceptors? */
1659             if (QEMPTY(&i))
1660                 continue;
1661             /* Did we run out of bombers? */
1662             if (QEMPTY(bomb_list)) {
1663                 /* Yes, so we have to put the rest of the interceptors back, and
1664                    then continue, or we leak memory */
1665                 pln_put(&i);
1666                 continue;
1667             }
1668
1669             BestAirPath(path, air->x, air->y, x, y);
1670             wu(0, cn, "Flying %s mission from %s\n",
1671                mission_name(MI_AIR_DEFENSE), xyas(air->x, air->y, cn));
1672             if (air->own && (air->own != cn)) {
1673                 wu(0, air->own, "%s is flying %s mission from %s\n",
1674                    cname(cn), mission_name(MI_AIR_DEFENSE),
1675                    xyas(air->x, air->y, air->own));
1676             }
1677
1678             /* Now, fly the planes to the sector */
1679             emp_initque(&empty);
1680             ac_encounter(&i, &empty, air->x, air->y,
1681                          path, mission_flags, 1, bomb_list, esc_list);
1682
1683             /* If none made it, continue */
1684             if (QEMPTY(&i))
1685                 continue;
1686
1687             /* Some made it, so now they get to try to fight. */
1688             /* Intercept the escorts first */
1689             combat = 0;
1690             if (!QEMPTY(esc_list)) {
1691                 mpr(victim, "%s air defense planes intercept!\n",
1692                     cname(cn));
1693                 ac_combat_headers(victim, cn);
1694                 ac_airtoair(esc_list, &i, air->own);
1695                 combat = 1;
1696             }
1697             /* Now intercept the bombers */
1698             if (!QEMPTY(bomb_list)) {
1699                 if (!combat) {
1700                     mpr(victim, "%s air defense planes intercept!\n",
1701                         cname(cn));
1702                     ac_combat_headers(victim, cn);
1703                 }
1704                 ac_airtoair(bomb_list, &i, air->own);
1705                 PR(cn, "\n");
1706                 PR(victim, "\n");
1707             }
1708
1709             pln_put(&i);
1710         }
1711     }
1712
1713     /* We have to free all of these, if they are still there, otherwise they get
1714        lost and we leak memory all over the place. */
1715     for (cn = 1; cn < MAXNOC; cn++) {
1716         /* free up all this memory if it's still there */
1717         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1718             next = qp->q_forw;
1719             glp = (struct genlist *)qp;
1720             free(glp->thing);
1721             free((s_char *)glp);
1722         }
1723     }
1724
1725     return dam;
1726 }