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