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