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