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