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