]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
launch interception: Drop support for ABM, a-sat consuming shells
[empserver] / src / lib / subs / plnsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, 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-2021
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     default:
718         CANT_REACH();
719         load = 0;
720     }
721
722     if (itype != I_NONE) {
723         needed = load / ichr[itype].i_lbs;
724         if (needed <= 0) {
725             pr("%s can't contribute to mission\n", prplane(pp));
726             return -1;
727         }
728         if (nuk_on_plane(pp) >= 0) {
729             if (mission == 's' || mission == 't')
730                 needed = 0;
731             else {
732                 pr("%s can't fly this mission"
733                    " while it is carrying a nuclear weapon\n",
734                    prplane(pp));
735                 return -1;
736             }
737         }
738         if (itype == I_CIVIL && pp->pln_own != own) {
739             pr("You don't control those civilians!\n");
740             return -1;
741         }
742 #if 0
743         /* Supply is broken somewhere, so don't use it for now */
744         if (itype == I_SHELL && item[itype] < needed)
745             item[itype] += supply_commod(plp->plane.pln_own,
746                                          plp->plane.pln_x,
747                                          plp->plane.pln_y,
748                                          I_SHELL, needed);
749 #endif
750         if (pp->pln_ship >= 0 || pp->pln_land >= 0)
751             abandon_needed = 0;
752         else
753             abandon_needed = !!would_abandon(&sect, itype, needed, NULL);
754         if (item[itype] < needed + abandon_needed) {
755             pr("Not enough %s for %s\n", ichr[itype].i_name, prplane(pp));
756             return -1;
757         }
758         item[itype] -= needed;
759         plp->load = needed;
760     }
761
762     if (pp->pln_ship >= 0) {
763         if (pp->pln_own != ship.shp_own) {
764             wu(0, ship.shp_own,
765                "%s %s prepares for takeoff from ship %s\n",
766                cname(pp->pln_own), prplane(pp), prship(&ship));
767         }
768         putship(ship.shp_uid, &ship);
769     } else if (pp->pln_land >= 0) {
770         if (pp->pln_own != land.lnd_own) {
771             wu(0, land.lnd_own,
772                "%s %s prepares for takeoff from unit %s\n",
773                cname(pp->pln_own), prplane(pp), prland(&land));
774         }
775         putland(land.lnd_uid, &land);
776     } else {
777         if (pp->pln_own != sect.sct_own) {
778             wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
779                cname(pp->pln_own), prplane(pp),
780                xyas(sect.sct_x, sect.sct_y, sect.sct_own));
781         }
782         putsect(&sect);
783     }
784     return 0;
785 }
786
787 void
788 pln_put(struct emp_qelem *list)
789 {
790     struct emp_qelem *qp, *next;
791
792     for (qp = list->q_forw; qp != list; qp = next) {
793         next = qp->q_forw;
794         pln_put1((struct plist *)qp);
795     }
796 }
797
798 void
799 pln_put1(struct plist *plp)
800 {
801     struct plnstr *pp;
802     struct shpstr ship;
803     struct sctstr sect;
804
805     pp = &plp->plane;
806
807     if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
808                     && (plchr[pp->pln_type].pl_flags & P_M)
809                     && pp->pln_effic >= PLANE_MINEFF))
810         pp->pln_effic = 0;   /* bug: missile launched but not used up */
811
812     if (!(pp->pln_flags & PLN_LAUNCHED))
813         ;                       /* never took off */
814     else if (pp->pln_effic < PLANE_MINEFF) {
815         ;                       /* destroyed */
816     } else if (pp->pln_ship >= 0) {
817         /* It is landing on a carrier */
818         getship(pp->pln_ship, &ship);
819         /* We should do more, like make sure it's really
820            a carrier, etc. but for now just make sure it's
821            not sunk. */
822         if (!ship.shp_own) {
823             mpr(pp->pln_own,
824                 "Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
825                 "splashes into the sea.\n",
826                 pp->pln_ship, pp->pln_uid);
827             pp->pln_effic = 0;
828         }
829     } else {
830         /* Presume we are landing back in a sector. */
831         getsect(pp->pln_x, pp->pln_y, &sect);
832         if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
833             mpr(pp->pln_own,
834                 "Nowhere to land at %s, plane #%d crashes and burns...\n",
835                 xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
836             pp->pln_effic = 0;
837         }
838     }
839     pp->pln_flags &= ~PLN_LAUNCHED;
840     putplane(pp->pln_uid, pp);
841     emp_remque(&plp->queue);
842     free(plp);
843 }
844
845 /*
846  * Can a carrier of @sp's type carry this load of planes?
847  * The load consists of @n planes, of which @nch are choppers, @nxl
848  * xlight, @nmsl light missiles, and the rest are light fixed-wing
849  * planes.
850  */
851 static int
852 ship_can_carry(struct shpstr *sp, int n, int nch, int nxl, int nmsl)
853 {
854     struct mchrstr *mcp = &mchr[sp->shp_type];
855     int nfw = n - nch - nxl - nmsl;
856
857     if (nch > mcp->m_nchoppers) /* overflow into fixed-wing slots */
858         nfw += nch - mcp->m_nchoppers;
859     if (nxl > mcp->m_nxlight)   /* overflow into missile slots */
860         nmsl += nxl - mcp->m_nxlight;
861     if (nmsl && !(mcp->m_flags & (M_MSL | M_FLY)))
862         return 0;               /* missile slots wanted */
863     if (nfw && !(mcp->m_flags & M_FLY))
864         return 0;               /* fixed-wing slots wanted */
865     return nfw + nmsl <= mcp->m_nplanes;
866 }
867
868 /*
869  * Increment carrier plane counters for @pp.
870  * If it's a chopper, increment *@nch.
871  * Else, if it's x-light, increment *@nxl.
872  * Else, if it's a light missile, increment *@msl.
873  * Return non-zero if it's a chopper, x-light or light.
874  */
875 static int
876 inc_shp_nplane(struct plnstr *pp, int *nch, int *nxl, int *nmsl)
877 {
878     struct plchrstr *pcp = &plchr[pp->pln_type];
879
880     if (pcp->pl_flags & P_K)
881         (*nch)++;
882     else if (pcp->pl_flags & P_E)
883         (*nxl)++;
884     else if (!(pcp->pl_flags & P_L))
885         return 0;
886     else if (pcp->pl_flags & P_M)
887         (*nmsl)++;
888     return 1;
889 }
890
891 /*
892  * Can @pp be loaded on a ship of @sp's type?
893  */
894 int
895 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
896 {
897     int nch = 0, nxl = 0, nmsl = 0;
898
899     if (!inc_shp_nplane(pp, &nch, &nxl, &nmsl))
900         return 0;
901     return ship_can_carry(sp, 1, nch, nxl, nmsl);
902 }
903
904 int
905 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
906 {
907     int n, nch, nxl, nmsl;
908
909     if (plane->pln_ship == ship->shp_uid)
910         return 1;               /* Already on ship */
911
912     n = shp_nplane(ship, &nch, &nxl, &nmsl);
913     if (!inc_shp_nplane(plane, &nch, &nxl, &nmsl))
914         return 0;               /* not a carrier plane */
915     if (!ship_can_carry(ship, n + 1, nch, nxl, nmsl))
916         return 0;               /* no space */
917
918     plane->pln_x = ship->shp_x;
919     plane->pln_y = ship->shp_y;
920     plane->pln_ship = ship->shp_uid;
921     putplane(plane->pln_uid, plane);
922     return 1;
923 }
924
925 int
926 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
927 {
928     if (plane->pln_land == land->lnd_uid)
929         return 1;               /* Already on unit */
930     if (!(plchr[plane->pln_type].pl_flags & P_E))
931         return 0;
932     if (lnd_nxlight(land) >= lchr[land->lnd_type].l_nxlight)
933         return 0;
934
935     plane->pln_x = land->lnd_x;
936     plane->pln_y = land->lnd_y;
937     plane->pln_land = land->lnd_uid;
938     putplane(plane->pln_uid, plane);
939     return 1;
940 }
941
942 void
943 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
944 {
945     struct plnstr *pp;
946     struct plchrstr *pcp;
947     struct emp_qelem *qp;
948     struct emp_qelem *next;
949     struct plist *ip;
950     struct sctstr sect;
951     int mines_there;
952     int found = 0;
953
954     getsect(x, y, &sect);
955     mines_there = sect.sct_mines;
956
957     if (mines_there == 0)
958         return;
959
960     if (sect.sct_type != SCT_WATER)
961         return;
962
963     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
964          qp = next) {
965         next = qp->q_forw;
966         ip = (struct plist *)qp;
967         pp = &ip->plane;
968         pcp = ip->pcp;
969         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
970             continue;
971
972         if (chance((100.0 - pln_acc(pp)) / 100.0)) {
973             pr("Sweep! in %s\n",
974                xyas(sect.sct_x, sect.sct_y, player->cnum));
975             mines_there--;
976             found = 1;
977         }
978     }
979
980     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
981         writemap(player->cnum);
982     sect.sct_mines = mines_there;
983     putsect(&sect);
984 }
985
986 int
987 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
988 {
989     struct plchrstr *pcp = plchr + pp->pln_type;
990     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
991         (pp->pln_tech - pcp->pl_tech / 2);
992     int acc = pln_acc(pp);
993     int hitchance;
994
995     if (type == EF_SHIP) {
996         if (pcp->pl_flags & P_A)
997             acc -= 20;
998         if (!(pcp->pl_flags & P_T))
999             acc += 35;
1000     }
1001     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1002                       (1.0 - acc / 100.0)) - hardtarget;
1003
1004     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1005     if (hitchance < 20)
1006         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1007     if (hitchance > 100)
1008         hitchance = 100;
1009     return hitchance;
1010 }
1011
1012 int
1013 pln_damage(struct plnstr *pp, char type, char *noisy)
1014 {
1015     struct plchrstr *pcp = plchr + pp->pln_type;
1016     int load, i, hitroll, aim, len;
1017     int dam = 0;
1018     int effective = 1;
1019     int pinbomber = 0;
1020     char buf[80];
1021
1022     if (CANT_HAPPEN(nuk_on_plane(pp) >= 0))
1023         return 0;
1024
1025     load = pln_load(pp);
1026     if (!load)                 /* e.g. ab, blowing up on launch pad */
1027         return 0;
1028
1029     i = roll(load) + 1;
1030     if (i > load)
1031         i = load;
1032
1033     if (pcp->pl_flags & P_M) {
1034         if (pcp->pl_flags & P_MAR)
1035             pinbomber = 1;
1036     } else if (pcp->pl_flags & P_T)
1037         pinbomber = 1;
1038
1039     aim = pln_acc(pp);
1040     if (type == 's') {
1041         effective = !pinbomber;
1042         aim = 30 + (pinbomber ? aim : 100 - aim);
1043     } else {
1044         effective = pinbomber;
1045         aim = 100 - aim;
1046     }
1047
1048     len = noisy ? snprintf(buf, sizeof(buf), "%s", noisy) : 0;
1049     while (i--) {
1050         if (noisy) {
1051             if (len > 75) {
1052                 mpr(pp->pln_own, "%s\n", buf);
1053                 len = 0;
1054             }
1055         }
1056         dam += roll(6);
1057         hitroll = roll(100);
1058         if (hitroll >= 90) {
1059             dam += 8;
1060             if (noisy)
1061                 len += sprintf(buf + len, "BLAM");
1062         } else if (hitroll < aim) {
1063             dam += 5;
1064             if (noisy)
1065                 len += sprintf(buf + len, "Blam");
1066         } else {
1067             dam += 1;
1068             if (noisy)
1069                 len += sprintf(buf + len, "blam");
1070         }
1071         if (noisy) {
1072             if (i)
1073                 len += sprintf(buf + len, "-");
1074         }
1075     }
1076     if (noisy && len)
1077         mpr(pp->pln_own, "%s\n", buf);
1078     if (effective)
1079         dam *= 2;
1080     return dam;
1081 }
1082
1083 int
1084 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1085 {
1086     double misschance =
1087         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1088     return (int)(100 - 100 * misschance * misschance);
1089 }
1090
1091 int
1092 pln_mobcost(int dist, struct plnstr *pp, char mission)
1093 {
1094     double cost;
1095
1096     cost = 20.0 / (pp->pln_effic / 100.0);
1097     if (mission == 'e' || mission == 0)
1098         cost /= 2;              /* escort or intercept */
1099
1100     return ldround(cost * dist / pln_range_max(pp) + 5, 1);
1101 }
1102
1103 int
1104 pln_is_in_orbit(struct plnstr *pp)
1105 {
1106     return (plchr[pp->pln_type].pl_flags & (P_M | P_O)) == P_O
1107         && (pp->pln_flags & PLN_LAUNCHED);
1108 }
1109
1110 /*
1111  * Set @pp's tech to @tlev along with everything else that depends on it.
1112  */
1113 void
1114 pln_set_tech(struct plnstr *pp, int tlev)
1115 {
1116     struct plchrstr *pcp = plchr + pp->pln_type;
1117     int limited_range = pp->pln_range < pln_range_max(pp);
1118     int range_max;
1119
1120     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1121         tlev = pcp->pl_tech;
1122     pp->pln_tech = tlev;
1123
1124     range_max = pln_range_max(pp);
1125     if (!limited_range || pp->pln_range > range_max)
1126         pp->pln_range = range_max;
1127 }