]> git.pond.sub.org Git - empserver/blob - src/lib/subs/mission.c
395b170fef105b3841bf83722a973c3b0c1bd8d5
[empserver] / src / lib / subs / mission.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2009, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future 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  *     Markus Armbruster, 2003-2009
34  */
35
36 #include <config.h>
37
38 #include <stdlib.h>
39 #include "empobj.h"
40 #include "file.h"
41 #include "item.h"
42 #include "misc.h"
43 #include "mission.h"
44 #include "nsc.h"
45 #include "optlist.h"
46 #include "path.h"
47 #include "player.h"
48 #include "prototypes.h"
49 #include "queue.h"
50 #include "xy.h"
51
52 struct genlist {
53     struct emp_qelem queue;     /* list of units */
54     void *cp;                   /* pointer to desc of thing */
55     struct empobj *thing;       /* thing's struct */
56 };
57
58 struct airport {
59     struct emp_qelem queue;
60     coord x, y;
61     natid own;
62 };
63
64 static void add_airport(struct emp_qelem *, coord, coord);
65 static int air_damage(struct emp_qelem *, coord, coord, int, natid,
66                       char *, int);
67 static void build_mission_list(struct genlist *, coord, coord, int, natid);
68 static void build_mission_list_type(struct genlist *, coord, coord, int,
69                                     int, natid);
70 static void divide(struct emp_qelem *, struct emp_qelem *, coord, coord);
71 static int dosupport(struct genlist *, coord, coord, natid, natid);
72 static int find_airport(struct emp_qelem *, coord, coord);
73 static void mission_pln_arm(struct emp_qelem *, coord, coord, int,
74                             int, struct ichrstr *);
75 static void mission_pln_sel(struct emp_qelem *, int, int, int);
76 static int perform_mission_land(int, struct lndstr *, coord, coord,
77                                 natid, int, char *, int, int);
78 static int perform_mission_ship(int, struct shpstr *, coord, coord,
79                                 natid, int, char *, int, int);
80 static int perform_mission_msl(int, struct emp_qelem *, coord, coord,
81                                natid, int);
82 static int perform_mission_bomb(int, struct emp_qelem *, coord, coord,
83                                 natid, int, char *, int, int);
84 static int perform_mission(coord, coord, natid, struct emp_qelem *, int,
85                            char *, int);
86
87 /*
88  * Interdict commodities & transported planes
89  */
90 int
91 ground_interdict(coord x, coord y, natid victim, char *s)
92 {
93     int cn;
94     int dam = 0, newdam, rel;
95     struct genlist mi[MAXNOC];
96     int z;
97
98     memset(mi, 0, sizeof(mi));
99     for (z = 1; z < MAXNOC; z++)
100         emp_initque((struct emp_qelem *)&mi[z]);
101
102     build_mission_list(mi, x, y, MI_INTERDICT, victim);
103
104     for (cn = 1; cn < MAXNOC; cn++) {
105         rel = getrel(getnatp(cn), victim);
106         if (rel > HOSTILE)
107             continue;
108
109         if (QEMPTY(&mi[cn].queue))
110             continue;
111
112         newdam = perform_mission(x, y, victim, &mi[cn].queue,
113                                  MI_INTERDICT, s, SECT_HARDTARGET);
114         dam += newdam;
115         if (newdam)
116             mpr(victim, "%s interdiction mission does %d damage!\n",
117                 cname(cn), newdam);
118     }
119     if (dam) {
120         collateral_damage(x, y, dam);
121     }
122     return dam;
123 }
124
125 int
126 collateral_damage(coord x, coord y, int dam)
127 {
128     int coll;
129     struct sctstr sect;
130
131     if (!dam)
132         return 0;
133
134     getsect(x, y, &sect);
135     if (sect.sct_own) {
136         coll = ldround((double)dam * collateral_dam, 1);
137         if (coll == 0)
138             return 0;
139         mpr(sect.sct_own, "%s takes %d%% collateral damage\n",
140             xyas(x, y, sect.sct_own), coll);
141         sectdamage(&sect, coll);
142         putsect(&sect);
143         return coll;
144     }
145     return 0;
146 }
147
148 static int
149 only_subs(struct emp_qelem *list)
150 {
151     struct emp_qelem *qp;
152     struct genlist *glp;
153     struct mchrstr *mcp;
154
155     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
156         glp = (struct genlist *)qp;
157
158         if (glp->thing->ef_type != EF_SHIP)
159             return 0;
160         mcp = glp->cp;
161         if (!(mcp->m_flags & M_SUB))
162             return 0;
163         /* It's a sub! */
164     }
165     /* They were all subs! */
166     return 1;
167 }
168
169
170 /*
171  *  Interdict ships & land units
172  */
173 int
174 unit_interdict(coord x, coord y, natid victim, char *s, int hardtarget,
175                int mission)
176 {
177     int cn;
178     int dam = 0, newdam;
179     struct genlist mi[MAXNOC];
180     int z;
181     int osubs;
182
183     memset(mi, 0, sizeof(mi));
184     for (z = 1; z < MAXNOC; z++)
185         emp_initque((struct emp_qelem *)&mi[z]);
186
187     build_mission_list(mi, x, y, mission, victim);
188
189     for (cn = 1; cn < MAXNOC; cn++) {
190         if (cn == victim)
191             continue;
192         if (mission == MI_SINTERDICT) {
193             if (getrel(getnatp(cn), victim) >= FRIENDLY)
194                 continue;
195         } else if (getrel(getnatp(cn), victim) > HOSTILE)
196             continue;
197
198         if (QEMPTY(&mi[cn].queue))
199             continue;
200
201         osubs = only_subs(&mi[cn].queue);
202         newdam = perform_mission(x, y, victim, &mi[cn].queue,
203                                  mission, s, hardtarget);
204         dam += newdam;
205         if (newdam) {
206             /* If only subs responded, then we don't know who's
207                subs they are */
208             mpr(victim, "%s interdiction mission does %d damage!\n",
209                 osubs ? "Enemy" : cname(cn), newdam);
210         }
211     }
212     if (dam) {
213         collateral_damage(x, y, dam);
214     }
215     return dam;
216 }
217
218 /*
219  *  Perform a mission against victim, on behalf of actee
220  */
221 int
222 off_support(coord x, coord y, natid victim, natid actee)
223 {
224     int dam = 0;
225     struct genlist mi[MAXNOC];
226     int z;
227
228     memset(mi, 0, sizeof(mi));
229     for (z = 1; z < MAXNOC; z++)
230         emp_initque((struct emp_qelem *)&mi[z]);
231
232     build_mission_list(mi, x, y, MI_SUPPORT, victim);
233     build_mission_list(mi, x, y, MI_OSUPPORT, victim);
234
235     dam = dosupport(mi, x, y, victim, actee);
236     return dam;
237 }
238
239 /*
240  *  Perform a mission against victim, on behalf of actee
241  */
242 int
243 def_support(coord x, coord y, natid victim, natid actee)
244 {
245     int dam = 0;
246     struct genlist mi[MAXNOC];
247     int z;
248
249     memset(mi, 0, sizeof(mi));
250     for (z = 1; z < MAXNOC; z++)
251         emp_initque((struct emp_qelem *)&mi[z]);
252
253     build_mission_list(mi, x, y, MI_SUPPORT, victim);
254     build_mission_list(mi, x, y, MI_DSUPPORT, victim);
255
256     dam = dosupport(mi, x, y, victim, actee);
257     return dam;
258 }
259
260 static int
261 dosupport(struct genlist *mi, coord x, coord y, natid victim, natid actee)
262 {
263     int cn;
264     int rel;
265     int dam = 0;
266
267     for (cn = 1; cn < MAXNOC; cn++) {
268         rel = getrel(getnatp(cn), actee);
269         if ((cn != actee) && (rel != ALLIED))
270             continue;
271         rel = getrel(getnatp(cn), victim);
272         if ((cn != actee) && (rel != AT_WAR))
273             continue;
274
275         if (QEMPTY(&mi[cn].queue))
276             continue;
277
278         dam += perform_mission(x, y, victim, &mi[cn].queue, MI_SUPPORT,
279                                "", SECT_HARDTARGET);
280     }
281     return dam;
282 }
283
284 static void
285 build_mission_list(struct genlist *mi, coord x, coord y, int mission,
286                    natid victim)
287 {
288     build_mission_list_type(mi, x, y, mission, EF_LAND, victim);
289     build_mission_list_type(mi, x, y, mission, EF_SHIP, victim);
290     build_mission_list_type(mi, x, y, mission, EF_PLANE, victim);
291 }
292
293 static void
294 build_mission_list_type(struct genlist *mi, coord x, coord y, int mission,
295                         int type, natid victim)
296 {
297     struct nstr_item ni;
298     struct genlist *glp;
299     struct empobj *gp;
300     union empobj_storage item;
301     int relat;
302     struct sctstr sect;
303
304     snxtitem_all(&ni, type);
305     while (nxtitem(&ni, &item)) {
306         gp = (struct empobj *)&item;
307
308         if (gp->own == 0)
309             continue;
310
311         if (gp->mobil < 1)
312             continue;
313
314         if ((gp->mission != mission) && (mission != MI_SINTERDICT))
315             continue;
316
317         if ((gp->mission != mission) && (mission == MI_SINTERDICT) &&
318             (gp->mission != MI_INTERDICT))
319             continue;
320
321         relat = getrel(getnatp(gp->own), victim);
322         if (mission == MI_SINTERDICT) {
323             if (relat >= FRIENDLY)
324                 continue;
325             else if (type != EF_PLANE && relat > HOSTILE)
326                 continue;
327         } else if (relat > HOSTILE)
328             continue;
329
330         if (!in_oparea(gp, x, y))
331             continue;
332
333         if (opt_SLOW_WAR) {
334             if (mission != MI_AIR_DEFENSE) {
335                 getsect(x, y, &sect);
336                 if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) {
337
338                     /*
339                      * If the owner of the unit isn't at war
340                      * with the victim, and doesn't own the
341                      * sect being acted upon, and isn't the
342                      * old owner of that sect, bounce them.
343                      */
344                     if (sect.sct_type != SCT_WATER &&
345                         sect.sct_own != gp->own &&
346                         sect.sct_oldown != gp->own)
347                         continue;
348                 }
349             }
350         }
351
352         glp = malloc(sizeof(struct genlist));
353         memset(glp, 0, sizeof(struct genlist));
354         glp->cp = get_empobj_chr(gp);
355         glp->thing = malloc(sizeof(item));
356         memcpy(glp->thing, &item, sizeof(item));
357         emp_insque(&glp->queue, &mi[gp->own].queue);
358     }
359 }
360
361 static void
362 find_escorts(coord x, coord y, natid cn, struct emp_qelem *escorts)
363 {
364     struct nstr_item ni;
365     struct plist *plp;
366     struct plnstr plane;
367
368     snxtitem_all(&ni, EF_PLANE);
369     while (nxtitem(&ni, &plane)) {
370         if (plane.pln_own != cn)
371             continue;
372         if (plane.pln_mission != MI_ESCORT)
373             continue;
374         if (!in_oparea((struct empobj *)&plane, x, y))
375             continue;
376         plp = malloc(sizeof(struct plist));
377         memset(plp, 0, sizeof(struct plist));
378         plp->pcp = &plchr[(int)plane.pln_type];
379         plp->plane = plane;
380         emp_insque(&plp->queue, escorts);
381     }
382 }
383
384 static int
385 perform_mission(coord x, coord y, natid victim, struct emp_qelem *list,
386                 int mission, char *s, int hardtarget)
387 {
388     struct emp_qelem *qp, missiles, bombers;
389     struct genlist *glp;
390     struct plist *plp;
391     struct empobj *gp;
392     struct sctstr sect;
393     struct plchrstr *pcp;
394     int dam = 0;
395     int md;
396     int targeting_ships = *s == 's'; /* "subs" or "ships" FIXME gross! */
397
398     getsect(x, y, &sect);
399
400     emp_initque(&missiles);
401     emp_initque(&bombers);
402
403     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
404         glp = (struct genlist *)qp;
405         gp = glp->thing;
406
407         md = mapdist(x, y, gp->x, gp->y);
408
409         if (glp->thing->ef_type == EF_LAND) {
410             dam = perform_mission_land(dam, (struct lndstr *)glp->thing,
411                                        x, y, victim, mission, s,
412                                        md, targeting_ships);
413         } else if (glp->thing->ef_type == EF_SHIP) {
414             dam = perform_mission_ship(dam, (struct shpstr *)glp->thing,
415                                        x, y, victim, mission, s,
416                                        md, targeting_ships);
417         } else if (glp->thing->ef_type == EF_PLANE) {
418             pcp = glp->cp;
419             if (pcp->pl_flags & P_M)
420                 /* units have their own missile interdiction */
421                 if (hardtarget != SECT_HARDTARGET || pcp->pl_flags & P_MAR)
422                     continue;
423
424             /* save planes for later */
425             plp = malloc(sizeof(struct plist));
426
427             memset(plp, 0, sizeof(struct plist));
428             plp->pcp = pcp;
429             memcpy(&plp->plane, glp->thing, sizeof(struct plnstr));
430             if (plp->pcp->pl_flags & P_M)
431                 emp_insque(&plp->queue, &missiles);
432             else
433                 emp_insque(&plp->queue, &bombers);
434         } else {
435             CANT_REACH();
436             break;
437         }
438     }
439
440     dam = perform_mission_msl(dam, &missiles, x, y, victim, hardtarget);
441     dam = perform_mission_bomb(dam, &bombers, x, y, victim, mission, s,
442                                hardtarget, targeting_ships);
443
444     qp = list->q_forw;
445     while (qp != list) {
446         glp = (struct genlist *)qp;
447         qp = qp->q_forw;
448
449         free(glp->thing);
450         free(glp);
451     }
452
453     return dam;
454 }
455
456 static int
457 perform_mission_land(int dam, struct lndstr *lp, coord x, coord y,
458                      natid victim, int mission, char *s, int md,
459                      int targeting_ships)
460 {
461     int range, dam2;
462
463     if (mission == MI_SINTERDICT)
464         return dam;
465
466     if ((mission == MI_INTERDICT) &&
467         (md > land_max_interdiction_range))
468         return dam;
469
470     range = roundrange(lnd_fire_range(lp));
471     if (md > range)
472         return dam;
473
474     dam2 = lnd_fire(lp);
475     putland(lp->lnd_uid, lp);
476     if (dam2 < 0)
477         return dam;
478
479     if (targeting_ships) {
480         if (chance(lnd_acc(lp) / 100.0))
481             dam2 = ldround(dam2 / 2.0, 1);
482     }
483     if (targeting_ships)
484         nreport(lp->lnd_own, N_SHP_SHELL, victim, 1);
485     else
486         nreport(lp->lnd_own, N_SCT_SHELL, victim, 1);
487     wu(0, lp->lnd_own,
488        "%s fires at %s %s at %s\n",
489        prland(lp), cname(victim), s, xyas(x, y, lp->lnd_own));
490
491     mpr(victim, "%s %s fires at you at %s\n",
492         cname(lp->lnd_own), prland(lp), xyas(x, y, victim));
493
494     return dam + dam2;
495 }
496
497 static int
498 perform_mission_ship(int dam, struct shpstr *sp, coord x, coord y,
499                      natid victim, int mission, char *s, int md,
500                      int targeting_ships)
501 {
502     struct mchrstr *mcp = &mchr[sp->shp_type];
503     double vrange, hitchance;
504     int range, dam2;
505
506     if (((mission == MI_INTERDICT) ||
507          (mission == MI_SINTERDICT)) &&
508         (md > ship_max_interdiction_range))
509         return dam;
510     if (mission == MI_SINTERDICT) {
511         if (!(mcp->m_flags & M_SONAR))
512             return dam;
513         if (!(mcp->m_flags & M_DCH) && !(mcp->m_flags & M_SUBT))
514             return dam;
515         vrange = techfact(sp->shp_tech, mcp->m_vrnge);
516         vrange *= sp->shp_effic / 200.0;
517         if (md > vrange)
518             return dam;
519         /* can't look all the time */
520         if (chance(0.5))
521             return dam;
522     }
523     if (mcp->m_flags & M_SUB) {
524         if (!targeting_ships)
525             return dam; /* subs interdict only ships */
526         range = roundrange(torprange(sp));
527         if (md > range)
528             return dam;
529         if (!line_of_sight(NULL, x, y, sp->shp_x, sp->shp_y))
530             return dam;
531         dam2 = shp_torp(sp, 1);
532         putship(sp->shp_uid, sp);
533         if (dam2 < 0)
534             return dam;
535         hitchance = shp_torp_hitchance(sp, md);
536
537         wu(0, sp->shp_own,
538            "%s locking on %s %s in %s\n",
539            prship(sp), cname(victim), s, xyas(x, y, sp->shp_own));
540         wu(0, sp->shp_own,
541            "\tEffective torpedo range is %d.0\n", range);
542         wu(0, sp->shp_own,
543            "\tWhooosh... Hitchance = %d%%\n",
544            (int)(hitchance * 100));
545
546         if (!chance(hitchance)) {
547             wu(0, sp->shp_own, "\tMissed\n");
548             mpr(victim,
549                 "Incoming torpedo sighted @ %s missed (whew)!\n",
550                 xyas(x, y, victim));
551             return dam;
552         }
553         wu(0, sp->shp_own, "\tBOOM!...\n");
554         nreport(victim, N_TORP_SHIP, 0, 1);
555         wu(0, sp->shp_own,
556            "\tTorpedo hit %s %s for %d damage\n",
557            cname(victim), s, dam2);
558
559         mpr(victim,
560             "Incoming torpedo sighted @ %s hits and does %d damage!\n",
561             xyas(x, y, victim), dam2);
562     } else {
563         range = roundrange(shp_fire_range(sp));
564         if (md > range)
565             return dam;
566         if (mission == MI_SINTERDICT)
567             dam2 = shp_dchrg(sp);
568         else
569             dam2 = shp_fire(sp);
570         putship(sp->shp_uid, sp);
571         if (dam2 < 0)
572             return dam;
573         if (targeting_ships)
574             nreport(sp->shp_own, N_SHP_SHELL, victim, 1);
575         else
576             nreport(sp->shp_own, N_SCT_SHELL, victim, 1);
577         wu(0, sp->shp_own,
578            "%s fires at %s %s at %s\n",
579            prship(sp), cname(victim), s, xyas(x, y, sp->shp_own));
580
581         mpr(victim, "%s %s fires at you at %s\n",
582             cname(sp->shp_own), prship(sp), xyas(x, y, victim));
583     }
584
585     return dam + dam2;
586 }
587
588 static int
589 perform_mission_msl(int dam, struct emp_qelem *missiles, coord x, coord y,
590                     natid victim, int hardtarget)
591 {
592     int air_dam;
593     struct emp_qelem *qp, *newqp;
594     struct plist *plp;
595     int sublaunch, dam2;
596
597     /*
598      * Missiles, except for interdiction of ships or land units,
599      * because that happens elsewhere, in shp_missile_interdiction()
600      * and lnd_missile_interdiction().
601      */
602     air_dam = 0;
603     for (qp = missiles->q_back; qp != missiles; qp = newqp) {
604         newqp = qp->q_back;
605         plp = (struct plist *)qp;
606
607         if (air_dam < 100
608             && !CANT_HAPPEN(hardtarget != SECT_HARDTARGET
609                             || (plp->pcp->pl_flags & P_MAR))
610             && mission_pln_equip(plp, NULL, 'p') >= 0) {
611             if (msl_launch(&plp->plane, EF_SECTOR, "sector", x, y, victim,
612                            &sublaunch) < 0)
613                 goto use_up_msl;
614             if (!msl_hit(&plp->plane, SECT_HARDTARGET, EF_SECTOR,
615                          N_SCT_MISS, N_SCT_SMISS, sublaunch, victim))
616                 CANT_REACH();
617             dam2 = pln_damage(&plp->plane, 'p', 1);
618             air_dam += dam2;
619         use_up_msl:
620             plp->plane.pln_effic = 0;
621             putplane(plp->plane.pln_uid, &plp->plane);
622         }
623         emp_remque(qp);
624         free(qp);
625     }
626     return dam + air_dam;
627 }
628
629 static int
630 perform_mission_bomb(int dam, struct emp_qelem *bombers, coord x, coord y,
631                      natid victim, int mission, char *s, int hardtarget,
632                      int targeting_ships)
633 {
634     struct emp_qelem *qp, *newqp, escorts, airp, b, e;
635     struct plist *plp;
636     int plane_owner, air_dam, md;
637
638     emp_initque(&escorts);
639     emp_initque(&airp);
640
641     if (QEMPTY(bombers))
642         return dam;
643
644     plp = (struct plist *)bombers->q_forw;
645     plane_owner = plp->plane.pln_own;
646
647     /*
648      * If there are planes performing an
649      * interdict or support mission, find
650      * some escorts for them, if possible.
651      * Up to 2 per bomber, if possible.
652      */
653     find_escorts(x, y, plane_owner, &escorts);
654
655     if (mission == MI_SINTERDICT)
656         mission_pln_sel(bombers, P_T | P_A, 0, hardtarget);
657     else
658         mission_pln_sel(bombers, P_T, P_A, SECT_HARDTARGET);
659
660     mission_pln_sel(&escorts, P_ESC | P_F, 0, SECT_HARDTARGET);
661
662     for (qp = bombers->q_forw; qp != bombers; qp = qp->q_forw) {
663         plp = (struct plist *)qp;
664         if (!find_airport(&airp, plp->plane.pln_x, plp->plane.pln_y))
665             add_airport(&airp, plp->plane.pln_x, plp->plane.pln_y);
666     }
667
668     air_dam = 0;
669     for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
670         struct airport *air;
671         char buf[512];
672         char *pp;
673
674         air = (struct airport *)qp;
675         md = mapdist(x, y, air->x, air->y);
676
677         emp_initque(&b);
678         emp_initque(&e);
679
680         /* Split off the bombers at this base into b */
681         divide(bombers, &b, air->x, air->y);
682
683         /* Split off the escorts at this base into e */
684         divide(&escorts, &e, air->x, air->y);
685
686         mission_pln_arm(&b, air->x, air->y, 2 * md, 'p', NULL);
687
688         if (QEMPTY(&b))
689             continue;
690
691         mission_pln_arm(&e, air->x, air->y, 2 * md, 'e', NULL);
692
693         pp = BestAirPath(buf, air->x, air->y, x, y);
694         if (CANT_HAPPEN(!pp))
695             continue;
696         wu(0, plane_owner, "Flying %s mission from %s to %s\n",
697            mission_name(mission),
698            xyas(air->x, air->y, plane_owner),
699            xyas(x, y, plane_owner));
700         if (air->own && (air->own != plane_owner)) {
701             wu(0, air->own, "%s is flying %s mission from %s to %s\n",
702                cname(plane_owner), mission_name(mission),
703                xyas(air->x, air->y, air->own),
704                xyas(x, y, air->own));
705         }
706
707         ac_encounter(&b, &e, air->x, air->y, pp, 0);
708
709         if (!QEMPTY(&b))
710             air_dam +=
711                 air_damage(&b, x, y, mission, victim, s, hardtarget);
712
713         pln_put(&b);
714         pln_put(&e);
715     }
716
717     if (air_dam > 0) {
718         if (targeting_ships)
719             nreport(plane_owner, N_SHP_BOMB, victim, 1);
720         else
721             nreport(plane_owner, N_SCT_BOMB, victim, 1);
722     }
723
724     qp = escorts.q_forw;
725     while (qp != (&escorts)) {
726         newqp = qp->q_forw;
727         emp_remque(qp);
728         free(qp);
729         qp = newqp;
730     }
731
732     qp = bombers->q_forw;
733     while (qp != bombers) {
734         newqp = qp->q_forw;
735         emp_remque(qp);
736         free(qp);
737         qp = newqp;
738     }
739
740     return dam + air_dam;
741 }
742
743 int
744 cando(int mission, int type)
745 {
746     switch (mission) {
747     case MI_ESCORT:
748         if (type == EF_PLANE)
749             return 1;
750         return 0;
751     case MI_AIR_DEFENSE:
752         if (type == EF_PLANE)
753             return 1;
754         return 0;
755     case MI_SINTERDICT:
756         if ((type == EF_PLANE) || (type == EF_SHIP))
757             return 1;
758         return 0;
759     case MI_INTERDICT:
760         return 1;
761     case MI_SUPPORT:
762     case MI_OSUPPORT:
763     case MI_DSUPPORT:
764         if (type == EF_PLANE)
765             return 1;
766         return 0;
767     case MI_RESERVE:
768         if (type == EF_LAND)
769             return 1;
770         return 0;
771     }
772
773     return 0;
774 }
775
776 char *
777 mission_name(int mission)
778 {
779     switch (mission) {
780     case MI_INTERDICT:
781         return "an interdiction";
782     case MI_SUPPORT:
783         return "a support";
784     case MI_OSUPPORT:
785         return "an offensive support";
786     case MI_DSUPPORT:
787         return "a defensive support";
788     case MI_RESERVE:
789         return "a reserve";
790     case MI_ESCORT:
791         return "an escort";
792     case MI_SINTERDICT:
793         return "a sub interdiction";
794     case MI_AIR_DEFENSE:
795         return "an air defense";
796     }
797     CANT_REACH();
798     return "a mysterious";
799 }
800
801 /*
802  * Maximum distance GP can perform its mission.
803  * Note: this has nothing to do with the radius of the op-area.
804  * oprange() governs where the unit *can* strike, the op-area governs
805  * where the player wants it to strike.
806  */
807 int
808 oprange(struct empobj *gp)
809 {
810     switch (gp->ef_type) {
811     case EF_SHIP:
812         return ldround(shp_fire_range((struct shpstr *)gp), 1);
813     case EF_LAND:
814         if (gp->mission == MI_RESERVE)
815             return lnd_reaction_range((struct lndstr *)gp);
816         return ldround(lnd_fire_range((struct lndstr *)gp), 1);
817     case EF_PLANE:
818         /* missiles go one way, so we can use all the range */
819         if (plchr[(int)gp->type].pl_flags & P_M)
820             return ((struct plnstr *)gp)->pln_range;
821         return ((struct plnstr *)gp)->pln_range / 2;
822     }
823     CANT_REACH();
824     return -1;
825 }
826
827 /*
828  * Does GP's mission op area cover X,Y?
829  */
830 int
831 in_oparea(struct empobj *gp, coord x, coord y)
832 {
833     return mapdist(x, y, gp->opx, gp->opy) <= gp->radius
834         && mapdist(x, y, gp->x, gp->y) <= oprange(gp);
835 }
836
837 /*
838  *  Remove all planes who cannot go on
839  *  the mission from the plane list.
840  */
841 static void
842 mission_pln_sel(struct emp_qelem *list, int wantflags, int nowantflags,
843                 int hardtarget)
844 {
845     struct emp_qelem *qp, *next;
846     struct plnstr *pp;
847     struct plchrstr *pcp;
848     struct plist *plp;
849
850     for (qp = list->q_forw; qp != list; qp = next) {
851         next = qp->q_forw;
852         plp = (struct plist *)qp;
853         pp = &plp->plane;
854         pcp = plp->pcp;
855
856         if (pp->pln_effic < 40) {
857             emp_remque(qp);
858             free(qp);
859             continue;
860         }
861
862         if (pp->pln_mobil < 1) {
863             emp_remque(qp);
864             free(qp);
865             continue;
866         }
867
868         if (opt_MARKET) {
869             if (ontradingblock(EF_PLANE, pp)) {
870                 emp_remque(qp);
871                 free(qp);
872                 continue;
873             }
874         }
875
876         if (!pln_capable(pp, wantflags, nowantflags)) {
877             emp_remque(qp);
878             free(qp);
879             continue;
880         }
881
882         if (!pln_airbase_ok(pp, 0, 0)) {
883             emp_remque(qp);
884             free(qp);
885             continue;
886         }
887
888         if (pcp->pl_flags & P_A) {
889             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
890                 emp_remque(qp);
891                 free(qp);
892                 continue;
893             }
894         }
895
896         putplane(pp->pln_uid, pp);
897     }
898 }
899
900 /*
901  * Arm only the planes at x,y
902  */
903 static void
904 mission_pln_arm(struct emp_qelem *list, coord x, coord y, int dist,
905                 int mission, struct ichrstr *ip)
906 {
907     struct emp_qelem *qp;
908     struct emp_qelem *next;
909     struct plist *plp;
910     struct plnstr *pp;
911
912     for (qp = list->q_forw; qp != list; qp = next) {
913         next = qp->q_forw;
914         plp = (struct plist *)qp;
915         pp = &plp->plane;
916
917         if (pp->pln_x != x)
918             continue;
919         if (pp->pln_y != y)
920             continue;
921
922         if (CANT_HAPPEN(pp->pln_flags & PLN_LAUNCHED)
923             || mission_pln_equip(plp, ip, mission) < 0) {
924             emp_remque(qp);
925             free(qp);
926             continue;
927         }
928
929         pp->pln_flags |= PLN_LAUNCHED;
930         pp->pln_mobil -= pln_mobcost(dist, pp, mission);
931         putplane(pp->pln_uid, pp);
932     }
933 }
934
935 int
936 mission_pln_equip(struct plist *plp, struct ichrstr *ip, char mission)
937 {
938     struct plchrstr *pcp;
939     struct plnstr *pp;
940     int load, needed;
941     struct lndstr land;
942     struct shpstr ship;
943     struct sctstr sect;
944     i_type itype;
945     short *item;
946
947     pp = &plp->plane;
948     pcp = plp->pcp;
949     if (pp->pln_ship >= 0) {
950         getship(pp->pln_ship, &ship);
951         item = ship.shp_item;
952     } else if (pp->pln_land >= 0) {
953         getland(pp->pln_land, &land);
954         item = land.lnd_item;
955     } else {
956         getsect(pp->pln_x, pp->pln_y, &sect);
957         item = sect.sct_item;
958     }
959     if (pcp->pl_fuel > item[I_PETROL]) {
960         return -1;
961     }
962     item[I_PETROL] -= pcp->pl_fuel;
963     load = pln_load(pp);
964     itype = I_NONE;
965     switch (mission) {
966     case 'p':           /* pinpoint bomb */
967         itype = I_SHELL;
968         break;
969     case 'i':           /* missile interception */
970         if (load)
971             itype = I_SHELL;
972         break;
973     case 'e':           /* escort */
974     case 0:             /* plane interception */
975         load = 0;
976         break;
977     default:
978         CANT_REACH();
979         load = 0;
980     }
981
982     if (itype != I_NONE) {
983         needed = load / ichr[itype].i_lbs;
984         if (needed <= 0)
985             return -1;
986         if (CANT_HAPPEN(nuk_on_plane(pp) >= 0))
987             return -1;
988         if (itype == I_SHELL && item[itype] < needed) {
989             if (pp->pln_ship >= 0)
990                 shp_supply(&ship, I_SHELL, needed);
991             else if (pp->pln_land >= 0)
992                 lnd_supply(&land, I_SHELL, needed);
993             else
994                 sct_supply(&sect, I_SHELL, needed);
995         }
996         if (item[itype] < needed)
997             return -1;
998         item[itype] -= needed;
999         plp->load = needed;
1000     }
1001
1002     if (pp->pln_ship >= 0)
1003         putship(ship.shp_uid, &ship);
1004     else if (pp->pln_land >= 0)
1005         putland(land.lnd_uid, &land);
1006     else
1007         putsect(&sect);
1008     return 0;
1009 }
1010
1011 /*
1012  *  Return 1 if this x,y pair is in the list
1013  */
1014 static int
1015 find_airport(struct emp_qelem *airp, coord x, coord y)
1016 {
1017     struct emp_qelem *qp;
1018     struct airport *a;
1019
1020     for (qp = airp->q_forw; qp != airp; qp = qp->q_forw) {
1021         a = (struct airport *)qp;
1022         if ((a->x == x) && (a->y == y))
1023             return 1;
1024     }
1025
1026     return 0;
1027 }
1028
1029 /* #*# This needs to be changed to include acc's -KHS */
1030 static void
1031 add_airport(struct emp_qelem *airp, coord x, coord y)
1032 {
1033     struct airport *a;
1034     struct sctstr sect;
1035
1036     a = malloc(sizeof(struct airport));
1037
1038     a->x = x;
1039     a->y = y;
1040     getsect(x, y, &sect);
1041     a->own = sect.sct_own;
1042
1043     emp_insque((struct emp_qelem *)a, airp);
1044 }
1045
1046 /*
1047  *  Take all the planes in list 1 that
1048  *  are at x,y, and put them into list 2.
1049  */
1050 static void
1051 divide(struct emp_qelem *l1, struct emp_qelem *l2, coord x, coord y)
1052 {
1053     struct emp_qelem *qp, *next;
1054     struct plist *plp;
1055
1056     for (qp = l1->q_forw; qp != l1; qp = next) {
1057         next = qp->q_forw;
1058         plp = (struct plist *)qp;
1059
1060         if (plp->plane.pln_x != x)
1061             continue;
1062         if (plp->plane.pln_y != y)
1063             continue;
1064
1065         emp_remque(qp);
1066         emp_insque(qp, l2);
1067     }
1068 }
1069
1070 static int
1071 air_damage(struct emp_qelem *bombers, coord x, coord y, int mission,
1072            natid victim, char *s, int hardtarget)
1073 {
1074     struct emp_qelem *qp;
1075     struct plist *plp;
1076     struct plnstr *pp;
1077     int newdam, dam = 0;
1078     int hitchance;
1079
1080     for (qp = bombers->q_forw; qp != bombers; qp = qp->q_forw) {
1081         plp = (struct plist *)qp;
1082         pp = &plp->plane;
1083
1084         if ((mission == MI_SINTERDICT) && !(plp->pcp->pl_flags & P_A))
1085             continue;
1086
1087         if (!plp->load)
1088             continue;
1089
1090         newdam = 0;
1091         if (plp->pcp->pl_flags & P_A) {
1092             if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1093                 wu(0, pp->pln_own,
1094                    "\t%s detects sub movement in %s\n",
1095                    prplane(pp), xyas(x, y, pp->pln_own));
1096                 continue;
1097             }
1098             if (getrel(getnatp(pp->pln_own), victim) > HOSTILE) {
1099                 wu(0, pp->pln_own,
1100                    "\t%s tracks %s %s at %s\n",
1101                    prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1102                 continue;
1103             }
1104             wu(0, pp->pln_own,
1105                "\t%s depth-charging %s %s in %s\n",
1106                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1107         } else {
1108             wu(0, pp->pln_own,
1109                "\t%s pinbombing %s %s in %s\n",
1110                prplane(pp), cname(victim), s, xyas(x, y, pp->pln_own));
1111         }
1112         hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
1113         if (nuk_on_plane(&plp->plane) >= 0)
1114             hitchance = 100;
1115         else if (hardtarget != SECT_HARDTARGET)
1116             wu(0, pp->pln_own, "\t\t%d%% hitchance...", hitchance);
1117         if (roll(100) <= hitchance) {
1118             newdam = pln_damage(&plp->plane, 'p', 1);
1119             wu(0, pp->pln_own,
1120                "\t\thit %s %s for %d damage\n",
1121                cname(victim), s, newdam);
1122             dam += newdam;
1123         } else {
1124             newdam = pln_damage(&plp->plane, 'p', 0);
1125             wu(0, pp->pln_own, "missed\n");
1126             if (mission == MI_SINTERDICT) {
1127                 mpr(victim,
1128                     "RUMBLE... your sub in %s hears a depth-charge explode nearby\n",
1129                     xyas(x, y, victim));
1130             } else if (*s == 's') {
1131                 mpr(victim, "SPLASH!  Bombs miss your %s in %s\n",
1132                     s, xyas(x, y, victim));
1133             } else {
1134                 mpr(victim, "SPLAT!  Bombs miss your %s in %s\n",
1135                     s, xyas(x, y, victim));
1136             }
1137             /* Now, even though we missed, the bombs
1138                land somewhere. */
1139             collateral_damage(x, y, newdam);
1140         }
1141
1142         /* use up missiles */
1143         if (plp->pcp->pl_flags & P_M)
1144             pp->pln_effic = 0;
1145     }
1146
1147     return dam;
1148 }