]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
bomb launch interdiction: Fix crash on bombs missing target
[empserver] / src / lib / subs / plnsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2020, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  plnsub.c: Plane subroutine stuff
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1986
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1998-2000
33  *     Markus Armbruster, 2004-2020
34  */
35
36 #include <config.h>
37
38 #include "chance.h"
39 #include "empobj.h"
40 #include "item.h"
41 #include "land.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "nat.h"
45 #include "nsc.h"
46 #include "nuke.h"
47 #include "optlist.h"
48 #include "plague.h"
49 #include "plane.h"
50 #include "player.h"
51 #include "prototypes.h"
52 #include "sect.h"
53 #include "ship.h"
54 #include "xy.h"
55
56 static int ship_can_carry(struct shpstr *, int, int, int, int);
57 static int inc_shp_nplane(struct plnstr *, int *, int *, int *);
58
59 /*
60  * Get planes and escorts argument.
61  * Read planes into *@ni_bomb, and (optional) escorts into *@ni_esc.
62  * If @input_bomb is not empty, use it, else prompt for more input.
63  * Same for @input_esc.
64  * If we got a plane argument, initialize *@ni_bomb and *@ni_esc, and
65  * return 0.
66  * Else return -1 (*@ni_bomb and *@ni_esc may be modified).
67  */
68 int
69 get_planes(struct nstr_item *ni_bomb, struct nstr_item *ni_esc,
70            char *input_bomb, char *input_esc)
71 {
72     if (!snxtitem(ni_bomb, EF_PLANE, input_bomb, NULL))
73         return -1;
74     if (!snxtitem(ni_esc, EF_PLANE, input_esc, "escort(s)? ")) {
75         if (player->aborted)
76             return -1;
77         pr("No escorts...\n");
78     }
79     return 0;
80 }
81
82 /*
83  * Get assembly point argument.
84  * If @input is not empty, use it.
85  * Else prompt for more input using @prompt.
86  * If this yields a valid assembly point, read it into *@ap_sect and
87  * return @ap_sect.
88  * Else complain and return NULL.
89  * *@ap_sect and @buf[1024] may be modified in either case.
90  */
91 struct sctstr *
92 get_assembly_point(char *input, struct sctstr *ap_sect, char *buf)
93 {
94     char *p;
95     coord x, y;
96     struct nstr_item ni;
97     struct shpstr ship;
98
99     p = getstarg(input, "assembly point? ", buf);
100     if (!p || *p == 0)
101         return NULL;
102     if (!sarg_xy(p, &x, &y) || !getsect(x, y, ap_sect))
103         return NULL;
104
105     /* over own or allied sector is fine */
106     if (relations_with(ap_sect->sct_own, player->cnum) == ALLIED)
107         return ap_sect;
108
109     /* over own or allied ship is fine */
110     snxtitem_xy(&ni, EF_SHIP, x, y);
111     while (nxtitem(&ni, &ship)) {
112         if (!ship.shp_own)
113             continue;
114         if (relations_with(ship.shp_own, player->cnum) == ALLIED)
115             return ap_sect;
116     }
117
118     pr("Assembly point not owned by you or an ally!\n");
119     return NULL;
120 }
121
122 /*
123  * Find out whether planes can fly one-way to @x,@y.
124  * Offer the player any carriers there.  If he chooses one, read it
125  * into @target->ship.  Else read the target sector into @target->sect.
126  * If planes can land there, set required plane flags in *@flagsp, and
127  * return 0.  Else return -1.
128  */
129 int
130 pln_where_to_land(coord x, coord y,
131                   union empobj_storage *target, int *flagsp)
132 {
133     /* Keep conditions for landing consistent with pln_airbase_ok() */
134     int nships;
135     int cno;
136     int fl;
137     char buf[1024];
138     char *p;
139
140     /* offer carriers */
141     nships = carriersatxy(x, y, player->cnum);
142     if (nships) {
143         for (;;) {
144             p = getstring("Carrier #? ", buf);
145             if (!p)
146                 return -1;
147             if (!*p)
148                 break;
149             cno = atoi(p);
150             if (!getship(cno, &target->ship)
151                 || (!player->owner
152                     && (relations_with(target->ship.shp_own, player->cnum)
153                         != ALLIED))) {
154                 pr("Not yours\n");
155                 continue;
156             }
157             if (target->ship.shp_x != x || target->ship.shp_y != y) {
158                 pr("Ship #%d not in %s\n", cno, xyas(x, y, player->cnum));
159                 continue;
160             }
161             fl = carrier_planes(&target->ship, 0);
162             if (fl == 0) {
163                 pr("Can't land on %s.\n", prship(&target->ship));
164                 continue;
165             }
166             /* clear to land on ship#CNO */
167             pr("landing on carrier %d\n", cno);
168             *flagsp |= fl;
169             return 0;
170         }
171     }
172
173     /* try to land at sector */
174     getsect(x, y, &target->sect);
175     if (relations_with(target->sect.sct_own, player->cnum) != ALLIED) {
176         pr("Nowhere to land at sector %s!\n", xyas(x, y, player->cnum));
177         return -1;
178     }
179     /* clear to land at sector */
180     if (target->sect.sct_type == SCT_MOUNT) {
181         *flagsp |= P_K;
182     }
183     if (target->sect.sct_type != SCT_AIRPT || target->sect.sct_effic < 60)
184         *flagsp |= P_V;
185     return 0;
186 }
187
188 int
189 pln_can_land_on_carrier(struct emp_qelem *bomb_list,
190                         struct emp_qelem *esc_list,
191                         struct shpstr *sp)
192
193 {
194     int n, nch, nxl, nmsl;
195     struct emp_qelem *list, *qp;
196     struct plist *plp;
197
198     n = shp_nplane(sp, &nch, &nxl, &nmsl);
199
200     /* for both lists */
201     for (list = bomb_list;
202          list;
203          list = list == bomb_list ? esc_list : NULL) {
204         for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
205             plp = (struct plist *)qp;
206             if (plp->plane.pln_ship == sp->shp_uid)
207                 continue;
208             n++;
209             if (!inc_shp_nplane(&plp->plane, &nch, &nxl, &nmsl))
210                 return 0;
211         }
212     }
213     return ship_can_carry(sp, n, nch, nxl, nmsl);
214 }
215
216 void
217 pln_newlanding(struct emp_qelem *list, coord tx, coord ty, int cno)
218 {
219     struct emp_qelem *qp;
220     struct plist *plp;
221     struct shpstr ship;
222     struct sctstr sect;
223
224     if (cno >= 0)
225         getship(cno, &ship);
226     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
227         plp = (struct plist *)qp;
228         if (cno >= 0) {
229             if (!could_be_on_ship(&plp->plane, &ship))
230                 pr("\t%s cannot land on ship #%d! %s aborts!\n",
231                    prplane(&plp->plane), cno, prplane(&plp->plane));
232             else if (!put_plane_on_ship(&plp->plane, &ship))
233                 pr("\tNo room on ship #%d! %s aborts!\n",
234                    cno, prplane(&plp->plane));
235             else {
236                 if (plp->plane.pln_own != ship.shp_own) {
237                     wu(0, ship.shp_own, "%s %s lands on your %s\n",
238                        cname(player->cnum), prplane(&plp->plane),
239                        prship(&ship));
240                 }
241                 if (plp->pstage == PLG_INFECT
242                     && ship.shp_pstage == PLG_HEALTHY)
243                     ship.shp_pstage = PLG_EXPOSED;
244             }
245         } else {
246             plp->plane.pln_x = tx;
247             plp->plane.pln_y = ty;
248             getsect(tx, ty, &sect);
249             if (plp->plane.pln_own != sect.sct_own) {
250                 wu(0, sect.sct_own,
251                    "%s %s lands at your sector %s\n",
252                    cname(player->cnum),
253                    prplane(&plp->plane), xyas(tx, ty, sect.sct_own));
254             }
255             if (plp->pstage == PLG_INFECT
256                 && sect.sct_pstage == PLG_HEALTHY)
257                 sect.sct_pstage = PLG_EXPOSED;
258             plp->plane.pln_ship = cno;
259         }
260     }
261 }
262
263 void
264 pln_dropoff(struct emp_qelem *list, struct ichrstr *ip, coord tx, coord ty,
265             int cno)
266 {
267     struct emp_qelem *qp;
268     struct plist *plp;
269     int amt;
270     struct sctstr sect;
271     struct shpstr ship;
272     int there;
273     int max;
274     int pstage;
275
276     if (!ip)
277         return;
278     if (cno < 0) {
279         getsect(tx, ty, &sect);
280         if (!sect.sct_own) {
281             if (sect.sct_type == SCT_WATER)
282                 pr("Your %s sink like a rock!\n", ip->i_name);
283             else
284                 pr("Your %s vanish without a trace.\n", ip->i_name);
285             return;
286         }
287         if (relations_with(sect.sct_own, player->cnum) != ALLIED) {
288             pr("You don't own %s!  Cargo jettisoned...\n",
289                xyas(tx, ty, player->cnum));
290             return;
291         }
292         if (ip->i_uid == I_CIVIL && sect.sct_own != sect.sct_oldown) {
293             pr("%s is occupied.  Your civilians suffer from identity crisis and die.\n",
294                xyas(tx, ty, player->cnum));
295             return;
296         }
297         there = sect.sct_item[ip->i_uid];
298         max = ITEM_MAX;
299         pstage = sect.sct_pstage;
300     } else {
301         getship(cno, &ship);
302         there = ship.shp_item[ip->i_uid];
303         max = mchr[ship.shp_type].m_item[ip->i_uid];
304         pstage = ship.shp_pstage;
305     }
306
307     amt = 0;
308     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
309         plp = (struct plist *)qp;
310         amt += plp->load;
311         if (plp->load
312             && plp->pstage == PLG_INFECT && pstage == PLG_HEALTHY)
313             pstage = PLG_EXPOSED;
314     }
315
316     there += amt;
317     if (there > max) {
318         pr("%d excess %s discarded\n", there - max, ip->i_name);
319         amt -= there - max;
320         there = max;
321     }
322     pr("%d %s landed safely", amt, ip->i_name);
323     if (cno < 0) {
324         sect.sct_item[ip->i_uid] = there;
325         sect.sct_pstage = pstage;
326         if (sect.sct_own != player->cnum)
327             wu(0, sect.sct_own, "%s planes drop %d %s in %s\n",
328                cname(player->cnum), amt, ip->i_name,
329                xyas(tx, ty, sect.sct_own));
330         pr(" at %s\n", xyas(tx, ty, player->cnum));
331         putsect(&sect);
332     } else {
333         ship.shp_item[ip->i_uid] = there;
334         ship.shp_pstage = pstage;
335         if (ship.shp_own != player->cnum)
336             wu(0, ship.shp_own, "%s planes land %d %s on carrier %d\n",
337                cname(player->cnum), amt, ip->i_name, ship.shp_uid);
338         pr(" on carrier #%d\n", ship.shp_uid);
339         putship(ship.shp_uid, &ship);
340     }
341 }
342
343 void
344 pln_mine(struct emp_qelem *list, coord tx, coord ty)
345 {
346     struct emp_qelem *qp;
347     struct plist *plp;
348     int amt;
349     struct sctstr sect;
350
351     amt = 0;
352     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
353         plp = (struct plist *)qp;
354         amt += plp->load;
355
356     }
357     if (amt > 0) {
358         getsect(tx, ty, &sect);
359         if (sect.sct_type != SCT_WATER) {
360             pr("Your sea mines have no effect here.\n");
361             return;
362         }
363         sect.sct_mines = MIN(sect.sct_mines + amt, MINES_MAX);
364         pr("%d mines laid in %s.\n", amt, xyas(tx, ty, player->cnum));
365         if (map_set(player->cnum, tx, ty, 'X', 0))
366             writemap(player->cnum);
367         putsect(&sect);
368     }
369 }
370
371 /*
372  * Has @pp's type capabilities satisfying @wantflags and @nowantflags?
373  * A plane type is capable unless
374  * - it lacks all of the P_B, P_T in @wantflags, or
375  * - it lacks all of the P_F, P_ESC in @wantflags, or
376  * - it lacks all of the P_E, P_L, P_K in @wantflags, or
377  * - it lacks any of the other capabilities in @wantflags, or
378  * - it has any of the capabilities in @nowantflags.
379  */
380 int
381 pln_capable(struct plnstr *pp, int wantflags, int nowantflags)
382 {
383     int flags = plchr[(int)pp->pln_type].pl_flags;
384
385     if (wantflags & (P_B | P_T)) {
386         if ((flags & wantflags & (P_B | P_T)) == 0)
387             return 0;
388         wantflags &= ~(P_B | P_T);
389     }
390
391     if (wantflags & (P_F | P_ESC)) {
392         if ((flags & wantflags & (P_F | P_ESC)) == 0)
393             return 0;
394         wantflags &= ~(P_F | P_ESC);
395     }
396
397     if (wantflags & (P_E | P_L | P_K)) {
398         if ((flags & wantflags & (P_E | P_L | P_K)) == 0)
399             return 0;
400         wantflags &= ~(P_E | P_L | P_K);
401     }
402
403     if ((flags & wantflags) != wantflags)
404         return 0;
405
406     if (flags & nowantflags)
407         return 0;
408
409     return 1;
410 }
411
412 /*
413  * Return union of capabilities of planes in @list.
414  */
415 int
416 pln_caps(struct emp_qelem *list)
417 {
418     struct emp_qelem *qp;
419     struct plist *plp;
420     int fl;
421
422     fl = 0;
423     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
424         plp = (struct plist *)qp;
425         fl |= plp->pcp->pl_flags;
426     }
427
428     return fl;
429 }
430
431 /*
432  * Find plane types that can operate from carrier @sp.
433  * If @msl find missile types, else non-missile types.
434  * Return a combination of P_L, P_K, P_E.
435  * It's zero if @sp can't support air operations due to its type or
436  * state (low efficiency).
437  */
438 int
439 carrier_planes(struct shpstr *sp, int msl)
440 {
441     struct mchrstr *mcp = mchr + sp->shp_type;
442     int res;
443
444     if (sp->shp_effic < SHP_AIROPS_EFF)
445         return 0;
446
447     res = 0;
448     if (mcp->m_flags & M_FLY)
449         res |= P_L;
450     if ((mcp->m_flags & M_MSL) && msl)
451         res |= P_L;
452     if (mcp->m_nchoppers && !msl)
453         res |= P_K;
454     if (mcp->m_nxlight)
455         res |= P_E;
456     return res;
457 }
458
459 /*
460  * Can @pp operate out its sector?
461  * If @oneway, consider only takeoff, else takeoff and landing.
462  * If @noisy, report to current player when it can't.
463  */
464 int
465 pln_airbase_ok(struct plnstr *pp, int oneway, int noisy)
466 {
467     /* Keep conditions for landing consistent with pln_where_to_land() */
468     struct shpstr ship;
469     struct lndstr land;
470     struct sctstr sect;
471     struct plchrstr *pcp = plchr + pp->pln_type;
472
473     if (CANT_HAPPEN(noisy && pp->pln_own != player->cnum))
474         noisy = 0;
475
476     if (pp->pln_ship >= 0) {
477         /* ship: needs to be own or allied, efficient */
478         if (!getship(pp->pln_ship, &ship)) {
479             CANT_REACH();
480             return 0;
481         }
482         if (relations_with(ship.shp_own, pp->pln_own) != ALLIED) {
483             if (noisy)
484                 pr("(note) An ally does not own the ship %s is on\n",
485                    prplane(pp));
486             return 0;
487         }
488         if (!(carrier_planes(&ship, pcp->pl_flags & P_M) & pcp->pl_flags))
489             return 0;
490
491     } else if (pp->pln_land >= 0) {
492         /* land: needs to be own or allied, efficient, not embarked */
493         if (!getland(pp->pln_land, &land)) {
494             CANT_REACH();
495             return 0;
496         }
497         if (relations_with(land.lnd_own, pp->pln_own) != ALLIED) {
498             if (noisy)
499                 pr("(note) An ally does not own the unit %s is on\n",
500                    prplane(pp));
501             return 0;
502         }
503         if (land.lnd_effic < LND_AIROPS_EFF || !(pcp->pl_flags & P_E))
504             return 0;
505         if (land.lnd_ship >= 0 || land.lnd_land >= 0)
506             return 0;
507
508     } else {
509         /* sector: needs to be own or allied, efficient, suitable type */
510         if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
511             CANT_REACH();
512             return 0;
513         }
514         /* mountain requires helo or missile */
515         if (sect.sct_type == SCT_MOUNT && !(pcp->pl_flags & (P_K | P_M))) {
516             if (noisy)
517                 pr("(note) %s is in a mountain and can't take off\n",
518                    prplane(pp));
519             return 0;
520         }
521
522         if (relations_with(sect.sct_own, pp->pln_own) != ALLIED) {
523             if (noisy)
524                 pr("(note) An ally does not own the sector %s is in\n",
525                    prplane(pp));
526             return 0;
527         }
528         /* need airfield unless VTOL */
529         if ((pcp->pl_flags & P_V) == 0) {
530             if (sect.sct_type != SCT_AIRPT) {
531                 if (noisy)
532                     pr("%s not at airport\n", prplane(pp));
533                 return 0;
534             }
535             if (sect.sct_effic < 40) {
536                 if (noisy)
537                     pr("%s is not 40%% efficient, %s can't take off from there.\n",
538                        xyas(sect.sct_x, sect.sct_y, player->cnum),
539                        prplane(pp));
540                 return 0;
541             }
542             if (!oneway && sect.sct_effic < 60) {
543                 if (noisy)
544                     pr("%s is not 60%% efficient, %s can't land there.\n",
545                        xyas(sect.sct_x, sect.sct_y, player->cnum),
546                        prplane(pp));
547                 return 0;
548             }
549         }
550     }
551
552     return 1;
553 }
554
555 void
556 pln_sel(struct nstr_item *ni, struct emp_qelem *list, struct sctstr *ap,
557         int ap_to_target, int rangemult, int wantflags, int nowantflags)
558 {
559     struct plnstr plane;
560     int range;
561     struct plchrstr *pcp;
562     struct plist *plp;
563
564     emp_initque(list);
565     while (nxtitem(ni, &plane)) {
566         /*
567          * It would be nice to let deities fly foreign planes, but
568          * much of the code assumes that only the plane's owner can
569          * fly it.
570          */
571         if (!plane.pln_own || plane.pln_own != player->cnum)
572             continue;
573         if (plane.pln_mobil <= 0)
574             continue;
575         if (plane.pln_effic < 40) {
576             pr("%s not efficient enough (must be 40%%)\n",
577                prplane(&plane));
578             continue;
579         }
580         if (!pln_capable(&plane, wantflags, nowantflags))
581             continue;
582         if (opt_MARKET) {
583             if (ontradingblock(EF_PLANE, &plane)) {
584                 pr("plane #%d ineligible - it's for sale.\n",
585                    plane.pln_uid);
586                 continue;
587             }
588         }
589
590         range = mapdist(plane.pln_x, plane.pln_y, ap->sct_x, ap->sct_y);
591         if (range > 4) {
592             pr("%s too far from assembly point\n", prplane(&plane));
593             continue;
594         }
595         range += ap_to_target;
596         range *= rangemult;
597         pcp = &plchr[(int)plane.pln_type];
598         if (plane.pln_range < range) {
599             pr("%s out of range (%d:%d)\n",
600                prplane(&plane), plane.pln_range, range);
601             continue;
602         }
603         if (!pln_airbase_ok(&plane, rangemult != 2, 1))
604             continue;
605         pr("%s standing by\n", prplane(&plane));
606         plane.pln_mission = 0;
607         putplane(plane.pln_uid, &plane);
608         plp = malloc(sizeof(struct plist));
609         plp->load = 0;
610         plp->pstage = PLG_HEALTHY;
611         plp->pcp = pcp;
612         plp->plane = plane;
613         emp_insque(&plp->queue, list);
614     }
615 }
616
617 void
618 pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip)
619 {
620     struct emp_qelem *qp;
621     struct emp_qelem *next;
622     struct plist *plp;
623     struct plnstr *pp;
624
625     for (qp = list->q_forw; qp != list; qp = next) {
626         next = qp->q_forw;
627         plp = (struct plist *)qp;
628         pp = &plp->plane;
629         getplane(pp->pln_uid, pp);
630         if ((pp->pln_flags & PLN_LAUNCHED)
631             || pln_equip(plp, ip, mission) < 0) {
632             emp_remque(qp);
633             free(qp);
634             continue;
635         }
636         pp->pln_flags |= PLN_LAUNCHED;
637         pp->pln_mobil -= pln_mobcost(dist, pp, mission);
638         putplane(pp->pln_uid, pp);
639         pr("%s equipped\n", prplane(pp));
640     }
641 }
642
643 int
644 pln_equip(struct plist *plp, struct ichrstr *ip, char mission)
645 {
646     struct plchrstr *pcp;
647     struct plnstr *pp;
648     int load, needed;
649     struct lndstr land;
650     struct shpstr ship;
651     struct sctstr sect;
652     i_type itype;
653     short *item;
654     int own;
655     int abandon_needed;
656
657     pp = &plp->plane;
658     pcp = plp->pcp;
659     if (pp->pln_ship >= 0) {
660         getship(pp->pln_ship, &ship);
661         plp->pstage = ship.shp_pstage;
662         item = ship.shp_item;
663         own = ship.shp_own;
664     } else if (pp->pln_land >= 0) {
665         getland(pp->pln_land, &land);
666         plp->pstage = land.lnd_pstage;
667         item = land.lnd_item;
668         own = land.lnd_own;
669     } else {
670         getsect(pp->pln_x, pp->pln_y, &sect);
671         plp->pstage = sect.sct_pstage;
672         item = sect.sct_item;
673         own = sect.sct_oldown;
674     }
675     if (pcp->pl_fuel > item[I_PETROL]) {
676         pr("%s not enough petrol there!\n", prplane(pp));
677         return -1;
678     }
679     item[I_PETROL] -= pcp->pl_fuel;
680     load = pln_load(pp);
681     itype = I_NONE;
682     switch (mission) {
683     case 's':           /* strategic bomb */
684     case 'p':           /* pinpoint bomb */
685         itype = I_SHELL;
686         break;
687     case 't':           /* transport */
688         if (!(pcp->pl_flags & P_C) || !ip)
689             break;
690         itype = ip->i_uid;
691         load *= 2;
692         break;
693     case 'm':           /* mine */
694         if ((pcp->pl_flags & P_MINE) == 0)
695             break;
696         itype = I_SHELL;
697         load *= 2;
698         break;
699     case 'd':           /* drop */
700         if (!(pcp->pl_flags & P_C) || CANT_HAPPEN(!ip))
701             break;
702         itype = ip->i_uid;
703         if (pcp->pl_flags & P_V)
704             load *= 2;
705         break;
706     case 'a':           /* paradrop */
707         if (!(pcp->pl_flags & P_P))
708             break;
709         itype = I_MILIT;
710         if (pcp->pl_flags & P_V)
711             load *= 2;
712         break;
713     case 'r':           /* reconnaissance */
714     case 'e':           /* escort */
715         load = 0;
716         break;
717     case 'i':           /* missile interception */
718         if (CANT_HAPPEN(!(pcp->pl_flags & P_M)
719                         || !(pcp->pl_flags & (P_N | P_O))))
720             break;
721         if (load)
722             itype = I_SHELL;
723         break;
724     default:
725         CANT_REACH();
726         load = 0;
727     }
728
729     if (itype != I_NONE) {
730         needed = load / ichr[itype].i_lbs;
731         if (needed <= 0) {
732             pr("%s can't contribute to mission\n", prplane(pp));
733             return -1;
734         }
735         if (nuk_on_plane(pp) >= 0) {
736             if (mission == 's' || mission == 't')
737                 needed = 0;
738             else {
739                 pr("%s can't fly this mission"
740                    " while it is carrying a nuclear weapon\n",
741                    prplane(pp));
742                 return -1;
743             }
744         }
745         if (itype == I_CIVIL && pp->pln_own != own) {
746             pr("You don't control those civilians!\n");
747             return -1;
748         }
749 #if 0
750         /* Supply is broken somewhere, so don't use it for now */
751         if (itype == I_SHELL && item[itype] < needed)
752             item[itype] += supply_commod(plp->plane.pln_own,
753                                          plp->plane.pln_x,
754                                          plp->plane.pln_y,
755                                          I_SHELL, needed);
756 #endif
757         if (pp->pln_ship >= 0 || pp->pln_land >= 0)
758             abandon_needed = 0;
759         else
760             abandon_needed = !!would_abandon(&sect, itype, needed, NULL);
761         if (item[itype] < needed + abandon_needed) {
762             pr("Not enough %s for %s\n", ichr[itype].i_name, prplane(pp));
763             return -1;
764         }
765         item[itype] -= needed;
766         plp->load = needed;
767     }
768
769     if (pp->pln_ship >= 0) {
770         if (pp->pln_own != ship.shp_own) {
771             wu(0, ship.shp_own,
772                "%s %s prepares for takeoff from ship %s\n",
773                cname(pp->pln_own), prplane(pp), prship(&ship));
774         }
775         putship(ship.shp_uid, &ship);
776     } else if (pp->pln_land >= 0) {
777         if (pp->pln_own != land.lnd_own) {
778             wu(0, land.lnd_own,
779                "%s %s prepares for takeoff from unit %s\n",
780                cname(pp->pln_own), prplane(pp), prland(&land));
781         }
782         putland(land.lnd_uid, &land);
783     } else {
784         if (pp->pln_own != sect.sct_own) {
785             wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
786                cname(pp->pln_own), prplane(pp),
787                xyas(sect.sct_x, sect.sct_y, sect.sct_own));
788         }
789         putsect(&sect);
790     }
791     return 0;
792 }
793
794 void
795 pln_put(struct emp_qelem *list)
796 {
797     struct emp_qelem *qp, *next;
798
799     for (qp = list->q_forw; qp != list; qp = next) {
800         next = qp->q_forw;
801         pln_put1((struct plist *)qp);
802     }
803 }
804
805 void
806 pln_put1(struct plist *plp)
807 {
808     struct plnstr *pp;
809     struct shpstr ship;
810     struct sctstr sect;
811
812     pp = &plp->plane;
813
814     if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
815                     && (plchr[pp->pln_type].pl_flags & P_M)
816                     && pp->pln_effic >= PLANE_MINEFF))
817         pp->pln_effic = 0;   /* bug: missile launched but not used up */
818
819     if (!(pp->pln_flags & PLN_LAUNCHED))
820         ;                       /* never took off */
821     else if (pp->pln_effic < PLANE_MINEFF) {
822         ;                       /* destroyed */
823     } else if (pp->pln_ship >= 0) {
824         /* It is landing on a carrier */
825         getship(pp->pln_ship, &ship);
826         /* We should do more, like make sure it's really
827            a carrier, etc. but for now just make sure it's
828            not sunk. */
829         if (!ship.shp_own) {
830             mpr(pp->pln_own,
831                 "Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
832                 "splashes into the sea.\n",
833                 pp->pln_ship, pp->pln_uid);
834             pp->pln_effic = 0;
835         }
836     } else {
837         /* Presume we are landing back in a sector. */
838         getsect(pp->pln_x, pp->pln_y, &sect);
839         if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
840             mpr(pp->pln_own,
841                 "Nowhere to land at %s, plane #%d crashes and burns...\n",
842                 xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
843             pp->pln_effic = 0;
844         }
845     }
846     pp->pln_flags &= ~PLN_LAUNCHED;
847     putplane(pp->pln_uid, pp);
848     emp_remque(&plp->queue);
849     free(plp);
850 }
851
852 /*
853  * Can a carrier of @sp's type carry this load of planes?
854  * The load consists of @n planes, of which @nch are choppers, @nxl
855  * xlight, @nmsl light missiles, and the rest are light fixed-wing
856  * planes.
857  */
858 static int
859 ship_can_carry(struct shpstr *sp, int n, int nch, int nxl, int nmsl)
860 {
861     struct mchrstr *mcp = &mchr[sp->shp_type];
862     int nfw = n - nch - nxl - nmsl;
863
864     if (nch > mcp->m_nchoppers) /* overflow into fixed-wing slots */
865         nfw += nch - mcp->m_nchoppers;
866     if (nxl > mcp->m_nxlight)   /* overflow into missile slots */
867         nmsl += nxl - mcp->m_nxlight;
868     if (nmsl && !(mcp->m_flags & (M_MSL | M_FLY)))
869         return 0;               /* missile slots wanted */
870     if (nfw && !(mcp->m_flags & M_FLY))
871         return 0;               /* fixed-wing slots wanted */
872     return nfw + nmsl <= mcp->m_nplanes;
873 }
874
875 /*
876  * Increment carrier plane counters for @pp.
877  * If it's a chopper, increment *@nch.
878  * Else, if it's x-light, increment *@nxl.
879  * Else, if it's a light missile, increment *@msl.
880  * Return non-zero if it's a chopper, x-light or light.
881  */
882 static int
883 inc_shp_nplane(struct plnstr *pp, int *nch, int *nxl, int *nmsl)
884 {
885     struct plchrstr *pcp = &plchr[pp->pln_type];
886
887     if (pcp->pl_flags & P_K)
888         (*nch)++;
889     else if (pcp->pl_flags & P_E)
890         (*nxl)++;
891     else if (!(pcp->pl_flags & P_L))
892         return 0;
893     else if (pcp->pl_flags & P_M)
894         (*nmsl)++;
895     return 1;
896 }
897
898 /*
899  * Can @pp be loaded on a ship of @sp's type?
900  */
901 int
902 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
903 {
904     int nch = 0, nxl = 0, nmsl = 0;
905
906     if (!inc_shp_nplane(pp, &nch, &nxl, &nmsl))
907         return 0;
908     return ship_can_carry(sp, 1, nch, nxl, nmsl);
909 }
910
911 int
912 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
913 {
914     int n, nch, nxl, nmsl;
915
916     if (plane->pln_ship == ship->shp_uid)
917         return 1;               /* Already on ship */
918
919     n = shp_nplane(ship, &nch, &nxl, &nmsl);
920     if (!inc_shp_nplane(plane, &nch, &nxl, &nmsl))
921         return 0;               /* not a carrier plane */
922     if (!ship_can_carry(ship, n + 1, nch, nxl, nmsl))
923         return 0;               /* no space */
924
925     plane->pln_x = ship->shp_x;
926     plane->pln_y = ship->shp_y;
927     plane->pln_ship = ship->shp_uid;
928     putplane(plane->pln_uid, plane);
929     return 1;
930 }
931
932 int
933 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
934 {
935     if (plane->pln_land == land->lnd_uid)
936         return 1;               /* Already on unit */
937     if (!(plchr[plane->pln_type].pl_flags & P_E))
938         return 0;
939     if (lnd_nxlight(land) >= lchr[land->lnd_type].l_nxlight)
940         return 0;
941
942     plane->pln_x = land->lnd_x;
943     plane->pln_y = land->lnd_y;
944     plane->pln_land = land->lnd_uid;
945     putplane(plane->pln_uid, plane);
946     return 1;
947 }
948
949 void
950 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
951 {
952     struct plnstr *pp;
953     struct plchrstr *pcp;
954     struct emp_qelem *qp;
955     struct emp_qelem *next;
956     struct plist *ip;
957     struct sctstr sect;
958     int mines_there;
959     int found = 0;
960
961     getsect(x, y, &sect);
962     mines_there = sect.sct_mines;
963
964     if (mines_there == 0)
965         return;
966
967     if (sect.sct_type != SCT_WATER)
968         return;
969
970     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
971          qp = next) {
972         next = qp->q_forw;
973         ip = (struct plist *)qp;
974         pp = &ip->plane;
975         pcp = ip->pcp;
976         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
977             continue;
978
979         if (chance((100.0 - pln_acc(pp)) / 100.0)) {
980             pr("Sweep! in %s\n",
981                xyas(sect.sct_x, sect.sct_y, player->cnum));
982             mines_there--;
983             found = 1;
984         }
985     }
986
987     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
988         writemap(player->cnum);
989     sect.sct_mines = mines_there;
990     putsect(&sect);
991 }
992
993 int
994 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
995 {
996     struct plchrstr *pcp = plchr + pp->pln_type;
997     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
998         (pp->pln_tech - pcp->pl_tech / 2);
999     int acc = pln_acc(pp);
1000     int hitchance;
1001
1002     if (type == EF_SHIP) {
1003         if (pcp->pl_flags & P_A)
1004             acc -= 20;
1005         if (!(pcp->pl_flags & P_T))
1006             acc += 35;
1007     }
1008     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1009                       (1.0 - acc / 100.0)) - hardtarget;
1010
1011     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1012     if (hitchance < 20)
1013         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1014     if (hitchance > 100)
1015         hitchance = 100;
1016     return hitchance;
1017 }
1018
1019 int
1020 pln_damage(struct plnstr *pp, char type, char *noisy)
1021 {
1022     struct plchrstr *pcp = plchr + pp->pln_type;
1023     int load, i, hitroll, aim, len;
1024     int dam = 0;
1025     int effective = 1;
1026     int pinbomber = 0;
1027     char buf[80];
1028
1029     if (CANT_HAPPEN(nuk_on_plane(pp) >= 0))
1030         return 0;
1031
1032     load = pln_load(pp);
1033     if (!load)                 /* e.g. ab, blowing up on launch pad */
1034         return 0;
1035
1036     i = roll(load) + 1;
1037     if (i > load)
1038         i = load;
1039
1040     if (pcp->pl_flags & P_M) {
1041         if (pcp->pl_flags & P_MAR)
1042             pinbomber = 1;
1043     } else if (pcp->pl_flags & P_T)
1044         pinbomber = 1;
1045
1046     aim = pln_acc(pp);
1047     if (type == 's') {
1048         effective = !pinbomber;
1049         aim = 30 + (pinbomber ? aim : 100 - aim);
1050     } else {
1051         effective = pinbomber;
1052         aim = 100 - aim;
1053     }
1054
1055     len = noisy ? snprintf(buf, sizeof(buf), "%s", noisy) : 0;
1056     while (i--) {
1057         if (noisy) {
1058             if (len > 75) {
1059                 mpr(pp->pln_own, "%s\n", buf);
1060                 len = 0;
1061             }
1062         }
1063         dam += roll(6);
1064         hitroll = roll(100);
1065         if (hitroll >= 90) {
1066             dam += 8;
1067             if (noisy)
1068                 len += sprintf(buf + len, "BLAM");
1069         } else if (hitroll < aim) {
1070             dam += 5;
1071             if (noisy)
1072                 len += sprintf(buf + len, "Blam");
1073         } else {
1074             dam += 1;
1075             if (noisy)
1076                 len += sprintf(buf + len, "blam");
1077         }
1078         if (noisy) {
1079             if (i)
1080                 len += sprintf(buf + len, "-");
1081         }
1082     }
1083     if (noisy && len)
1084         mpr(pp->pln_own, "%s\n", buf);
1085     if (effective)
1086         dam *= 2;
1087     return dam;
1088 }
1089
1090 int
1091 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1092 {
1093     double misschance =
1094         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1095     return (int)(100 - 100 * misschance * misschance);
1096 }
1097
1098 int
1099 pln_mobcost(int dist, struct plnstr *pp, char mission)
1100 {
1101     double cost;
1102
1103     cost = 20.0 / (pp->pln_effic / 100.0);
1104     if (mission == 'e' || mission == 0)
1105         cost /= 2;              /* escort or intercept */
1106
1107     return ldround(cost * dist / pln_range_max(pp) + 5, 1);
1108 }
1109
1110 int
1111 pln_is_in_orbit(struct plnstr *pp)
1112 {
1113     return (plchr[pp->pln_type].pl_flags & (P_M | P_O)) == P_O
1114         && (pp->pln_flags & PLN_LAUNCHED);
1115 }
1116
1117 /*
1118  * Set @pp's tech to @tlev along with everything else that depends on it.
1119  */
1120 void
1121 pln_set_tech(struct plnstr *pp, int tlev)
1122 {
1123     struct plchrstr *pcp = plchr + pp->pln_type;
1124     int limited_range = pp->pln_range < pln_range_max(pp);
1125     int range_max;
1126
1127     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1128         tlev = pcp->pl_tech;
1129     pp->pln_tech = tlev;
1130
1131     range_max = pln_range_max(pp);
1132     if (!limited_range || pp->pln_range > range_max)
1133         pp->pln_range = range_max;
1134 }