]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
New get_planes(), factored out of plane flying commands
[empserver] / src / lib / subs / plnsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  plnsub.c: Plane subroutine stuff
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  *     Markus Armbruster, 2004-2008
35  */
36
37 #include <config.h>
38
39 #include "file.h"
40 #include "item.h"
41 #include "land.h"
42 #include "lost.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 "path.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 pln_equip(struct plist *, struct ichrstr *, int, char);
58 static int fit_plane_on_ship(struct plnstr *, struct shpstr *);
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     char buf[1024];
74
75     if (!snxtitem(ni_bomb, EF_PLANE, input_bomb))
76         return -1;
77     if (!snxtitem(ni_esc, EF_PLANE,
78                   getstarg(input_esc, "escort(s)? ", buf)))
79         pr("No escorts...\n");
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 (ap_sect->sct_own == player->cnum
107         || getrel(getnatp(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 (ship.shp_own == player->cnum
116             || getrel(getnatp(ship.shp_own), player->cnum) == ALLIED)
117             return ap_sect;
118     }
119
120     pr("Assembly point not owned by you or an ally!\n");
121     return NULL;
122 }
123
124 int
125 pln_onewaymission(struct sctstr *target, int *shipno, int *flagp)
126 {
127     int nships;
128     int cno;
129     int flags, fl;
130     struct shpstr ship;
131     char buf[1024];
132     char *p;
133
134     flags = *flagp;
135
136     /* offer carriers */
137     nships = carriersatxy(target->sct_x, target->sct_y, player->cnum);
138     if (nships) {
139         for (;;) {
140             p = getstring("Carrier #? ", buf);
141             if (!p)
142                 return -1;
143             if (!*p)
144                 break;
145             cno = atoi(p);
146             if (cno < 0
147                 || !getship(cno, &ship)
148                 || (!player->owner
149                     && (getrel(getnatp(ship.shp_own), player->cnum)
150                         != ALLIED))) {
151                 pr("Not yours\n");
152                 continue;
153             }
154             if (ship.shp_x != target->sct_x || ship.shp_y != target->sct_y) {
155                 pr("Ship #%d not in %s\n", cno,
156                    xyas(target->sct_x, target->sct_y, player->cnum));
157                 continue;
158             }
159             fl = carrier_planes(&ship, 0);
160             if (fl == 0) {
161                 pr("Can't land on %s.\n", prship(&ship));
162                 continue;
163             }
164             /* clear to land on ship#CNO */
165             pr("landing on carrier %d\n", cno);
166             flags |= fl;
167             *shipno = cno;
168             *flagp = flags;
169             return 0;
170         }
171     }
172
173     /* try to land at sector */
174     if (target->sct_own != player->cnum
175         && getrel(getnatp(target->sct_own), player->cnum) != ALLIED) {
176         pr("Nowhere to land at sector %s!\n",
177            xyas(target->sct_x, target->sct_y, player->cnum));
178         return -1;
179     }
180     if (target->sct_type == SCT_MOUNT) {
181         pr("Nowhere to land at sector %s!\n",
182            xyas(target->sct_x, target->sct_y, player->cnum));
183         return -1;
184     }
185     if (target->sct_type != SCT_AIRPT || target->sct_effic < 60)
186         flags |= P_V;
187
188     /* clear to land at sector */
189     *shipno = -1;
190     *flagp = flags;
191     return 0;
192 }
193
194 int
195 pln_oneway_to_carrier_ok(struct emp_qelem *bomb_list,
196                          struct emp_qelem *esc_list, int cno)
197 {
198     struct emp_qelem *list, *qp;
199     struct plist *plp;
200     struct shpstr ship;
201
202     if (cno < 0 || !getship(cno, &ship))
203         return 0;
204
205     count_planes(&ship);
206
207     /* for both lists */
208     for (list = bomb_list;
209          list;
210          list = list == bomb_list ? esc_list : NULL) {
211         for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
212             plp = (struct plist *)qp;
213             if (plp->plane.pln_ship == ship.shp_uid)
214                 continue;
215             if (!fit_plane_on_ship(&plp->plane, &ship))
216                 return 0;
217         }
218     }
219     return 1;
220 }
221
222 void
223 pln_newlanding(struct emp_qelem *list, coord tx, coord ty, int cno)
224 {
225     struct emp_qelem *qp;
226     struct plist *plp;
227     struct shpstr ship;
228     struct sctstr sect;
229
230     if (cno >= 0)
231         getship(cno, &ship);
232     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
233         plp = (struct plist *)qp;
234         if (cno >= 0) {
235             count_planes(&ship);
236             if (!could_be_on_ship(&plp->plane, &ship))
237                 pr("\t%s cannot land on ship #%d! %s aborts!\n",
238                    prplane(&plp->plane), cno, prplane(&plp->plane));
239             else if (!put_plane_on_ship(&plp->plane, &ship))
240                 pr("\tNo room on ship #%d! %s aborts!\n",
241                    cno, prplane(&plp->plane));
242             else {
243                 if (plp->plane.pln_own != ship.shp_own) {
244                     wu(0, ship.shp_own, "%s %s lands on your %s\n",
245                        cname(player->cnum), prplane(&plp->plane),
246                        prship(&ship));
247                 }
248             }
249         } else {
250             plp->plane.pln_x = tx;
251             plp->plane.pln_y = ty;
252             getsect(tx, ty, &sect);
253             if (plp->plane.pln_own != sect.sct_own) {
254                 wu(0, sect.sct_own,
255                    "%s %s lands at your sector %s\n",
256                    cname(player->cnum),
257                    prplane(&plp->plane), xyas(tx, ty, sect.sct_own));
258             }
259             plp->plane.pln_ship = cno;
260         }
261     }
262     if (cno >= 0)
263         putship(ship.shp_uid, &ship);
264 }
265
266 void
267 pln_dropoff(struct emp_qelem *list, struct ichrstr *ip, coord tx, coord ty,
268             int cno)
269 {
270     struct emp_qelem *qp;
271     struct plist *plp;
272     int amt;
273     struct sctstr sect;
274     struct shpstr ship;
275     int there;
276     int max;
277
278     if (ip == 0)
279         return;
280     amt = 0;
281     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
282         plp = (struct plist *)qp;
283         amt += plp->misc;
284     }
285     if (cno < 0) {
286         getsect(tx, ty, &sect);
287         if (!sect.sct_own) {
288             if (sect.sct_type == SCT_WATER)
289                 pr("Your %s sink like a rock!\n", ip->i_name);
290             else
291                 pr("Your %s vanish without a trace.\n", ip->i_name);
292             return;
293         }
294         if (sect.sct_own != player->cnum
295             && getrel(getnatp(sect.sct_own), player->cnum) != ALLIED) {
296             pr("You don't own %s!  Cargo jettisoned...\n",
297                xyas(tx, ty, player->cnum));
298             return;
299         }
300         if (ip->i_uid == I_CIVIL && sect.sct_own != sect.sct_oldown) {
301             pr("%s is occupied.  Your civilians suffer from identity crisis and die.\n",
302                xyas(tx, ty, player->cnum));
303             return;
304         }
305         there = sect.sct_item[ip->i_uid];
306         max = ITEM_MAX;
307     } else {
308         getship(cno, &ship);
309         there = ship.shp_item[ip->i_uid];
310         max = mchr[ship.shp_type].m_item[ip->i_uid];
311     }
312     there += amt;
313     if (there > max) {
314         pr("%d excess %s discarded\n", max - there, ip->i_name);
315         amt = max - there;
316         there = max;
317     }
318     pr("%d %s landed safely", amt, ip->i_name);
319     if (cno < 0) {
320         sect.sct_item[ip->i_uid] = there;
321         if (sect.sct_own != player->cnum)
322             wu(0, sect.sct_own, "%s planes drop %d %s in %s\n",
323                cname(player->cnum), amt, ip->i_name,
324                xyas(tx, ty, sect.sct_own));
325         pr(" at %s\n", xyas(tx, ty, player->cnum));
326         putsect(&sect);
327     } else {
328         ship.shp_item[ip->i_uid] = there;
329         if (ship.shp_own != player->cnum)
330             wu(0, ship.shp_own, "%s planes land %d %s on carrier %d\n",
331                cname(player->cnum), amt, ip->i_name, ship.shp_uid);
332         pr(" on carrier #%d\n", ship.shp_uid);
333         putship(ship.shp_uid, &ship);
334     }
335 }
336
337 void
338 pln_mine(struct emp_qelem *list, coord tx, coord ty)
339 {
340     struct emp_qelem *qp;
341     struct plist *plp;
342     int amt;
343     struct sctstr sect;
344
345     amt = 0;
346     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
347         plp = (struct plist *)qp;
348         amt += plp->misc;
349
350     }
351     if (amt > 0) {
352         getsect(tx, ty, &sect);
353         if (sect.sct_type != SCT_WATER) {
354             pr("Your seamines have no effect here.\n");
355             return;
356         }
357         sect.sct_mines = MIN(sect.sct_mines + amt, MINES_MAX);
358         pr("%d mines laid in %s.\n", amt, xyas(tx, ty, player->cnum));
359         if (map_set(player->cnum, tx, ty, 'X', 0))
360             writemap(player->cnum);
361         putsect(&sect);
362     }
363 }
364
365 /*
366  * Has PP's type capabilities satisfying WANTFLAGS and NOWANTFLAGS?
367  * A plane type is capable unless
368  * - it lacks all of the P_F, P_ESC in WANTFLAGS, or
369  * - it lacks all of the P_E, P_L, P_K in WANTFLAGS, or
370  * - it lacks any of the other capabilities in WANTFLAGS, or
371  * - it has any of the capabilities in NOWANTFLAGS.
372  */
373 int
374 pln_capable(struct plnstr *pp, int wantflags, int nowantflags)
375 {
376     int flags = plchr[(int)pp->pln_type].pl_flags;
377
378     if (wantflags & (P_F | P_ESC)) {
379         if ((flags & wantflags & (P_F | P_ESC)) == 0)
380             return 0;
381         wantflags &= ~(P_F | P_ESC);
382     }
383
384     if (wantflags & (P_E | P_L | P_K)) {
385         if ((flags & wantflags & (P_E | P_L | P_K)) == 0)
386             return 0;
387         wantflags &= ~(P_E | P_L | P_K);
388     }
389
390     if ((flags & wantflags) != wantflags)
391         return 0;
392
393     if (flags & nowantflags)
394         return 0;
395
396     return 1;
397 }
398
399 /*
400  * Find plane types that can operate from carrier SP.
401  * If MSL find missile types, else non-missile types.
402  * Return a combination of P_L, P_K, P_E.
403  * It's zero if SP can't support air operations due to its type or
404  * state (low efficiency).
405  */
406 int
407 carrier_planes(struct shpstr *sp, int msl)
408 {
409     struct mchrstr *mcp = mchr + sp->shp_type;
410     int res;
411
412     if (sp->shp_effic < SHP_AIROPS_EFF)
413         return 0;
414
415     res = 0;
416     if (mcp->m_flags & M_FLY)
417         res |= P_L;
418     if ((mcp->m_flags & M_MSL) && msl)
419         res |= P_L;
420     if (mcp->m_nchoppers && !msl)
421         res |= P_K;
422     if (mcp->m_nxlight)
423         res |= P_E;
424     return res;
425 }
426
427 int
428 pln_airbase_ok(struct plnstr *pp, int oneway, int noisy)
429 {
430     struct shpstr ship;
431     struct lndstr land;
432     struct sctstr sect;
433     struct plchrstr *pcp = plchr + pp->pln_type;
434
435     if (CANT_HAPPEN(noisy && pp->pln_own != player->cnum))
436         noisy = 0;
437
438     if (pp->pln_ship >= 0) {
439         /* ship: needs to be own or allied, efficient */
440         if (!getship(pp->pln_ship, &ship)) {
441             CANT_REACH();
442             return 0;
443         }
444         if (ship.shp_own != pp->pln_own
445             && getrel(getnatp(ship.shp_own), pp->pln_own) != ALLIED) {
446             if (noisy)
447                 pr("(note) An ally does not own the ship %s is on\n",
448                    prplane(pp));
449             return 0;
450         }
451         if (!(carrier_planes(&ship, pcp->pl_flags & P_M) & pcp->pl_flags))
452             return 0;
453
454     } else if (pp->pln_land >= 0) {
455         /* land: needs to be own or allied, efficient, not embarked */
456         if (!getland(pp->pln_land, &land)) {
457             CANT_REACH();
458             return 0;
459         }
460         if (land.lnd_own != pp->pln_own
461             && getrel(getnatp(land.lnd_own), pp->pln_own) != ALLIED) {
462             if (noisy)
463                 pr("(note) An ally does not own the unit %s is on\n",
464                    prplane(pp));
465             return 0;
466         }
467         if (land.lnd_effic < LND_AIROPS_EFF || !(pcp->pl_flags & P_E))
468             return 0;
469         if (land.lnd_ship >= 0 || land.lnd_land >= 0)
470             return 0;
471
472     } else {
473         /* sector: needs to be own or allied, efficient airfield */
474         if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
475             CANT_REACH();
476             return 0;
477         }
478
479         if (sect.sct_own != pp->pln_own
480             && getrel(getnatp(sect.sct_own), pp->pln_own) != ALLIED) {
481             if (noisy)
482                 pr("(note) An ally does not own the sector %s is in\n",
483                    prplane(pp));
484             return 0;
485         }
486         /* need airfield unless VTOL */
487         if ((pcp->pl_flags & P_V) == 0) {
488             if (sect.sct_type != SCT_AIRPT) {
489                 if (noisy)
490                     pr("%s not at airport\n", prplane(pp));
491                 return 0;
492             }
493             if (sect.sct_effic < 40) {
494                 if (noisy)
495                     pr("%s is not 40%% efficient, %s can't take off from there.\n",
496                        xyas(sect.sct_x, sect.sct_y, pp->pln_own),
497                        prplane(pp));
498                 return 0;
499             }
500             if (!oneway && sect.sct_effic < 60) {
501                 if (noisy)
502                     pr("%s is not 60%% efficient, %s can't land there.\n",
503                        xyas(sect.sct_x, sect.sct_y, pp->pln_own),
504                        prplane(pp));
505                 return 0;
506             }
507         }
508     }
509
510     return 1;
511 }
512
513 void
514 pln_sel(struct nstr_item *ni, struct emp_qelem *list, struct sctstr *ap,
515         int ap_to_target, int rangemult, int wantflags, int nowantflags)
516 {
517     struct plnstr plane;
518     int range;
519     struct plchrstr *pcp;
520     struct plist *plp;
521
522     emp_initque(list);
523     while (nxtitem(ni, &plane)) {
524         if (!player->owner)
525             continue;
526         if (plane.pln_mobil <= 0)
527             continue;
528         if (plane.pln_effic < 40) {
529             pr("%s not efficient enough (must be 40%%)\n",
530                prplane(&plane));
531             continue;
532         }
533         if (!pln_capable(&plane, wantflags, nowantflags))
534             continue;
535         if (opt_MARKET) {
536             if (ontradingblock(EF_PLANE, &plane)) {
537                 pr("plane #%d inelligible - it's for sale.\n",
538                    plane.pln_uid);
539                 continue;
540             }
541         }
542
543         range = mapdist(plane.pln_x, plane.pln_y, ap->sct_x, ap->sct_y);
544         if (range > 4) {
545             pr("%s too far from assembly point\n", prplane(&plane));
546             continue;
547         }
548         range += ap_to_target;
549         range *= rangemult;
550         pcp = &plchr[(int)plane.pln_type];
551         if (plane.pln_range < range) {
552             pr("%s out of range (%d:%d)\n",
553                prplane(&plane), plane.pln_range, range);
554             continue;
555         }
556         if (!pln_airbase_ok(&plane, rangemult != 2, 1))
557             continue;
558         pr("%s standing by\n", prplane(&plane));
559         plane.pln_mission = 0;
560         putplane(plane.pln_uid, &plane);
561         plp = malloc(sizeof(struct plist));
562         plp->misc = 0;
563         plp->bombs = 0;
564         plp->pcp = pcp;
565         plp->plane = plane;
566         emp_insque(&plp->queue, list);
567     }
568 }
569
570 int
571 pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip,
572         int flags, int mission_flags)
573 {
574     struct emp_qelem *qp;
575     struct emp_qelem *next;
576     struct plist *plp;
577     struct plnstr *pp;
578
579     for (qp = list->q_forw; qp != list; qp = next) {
580         next = qp->q_forw;
581         plp = (struct plist *)qp;
582         pp = &plp->plane;
583         getplane(pp->pln_uid, pp);
584         if ((pp->pln_flags & PLN_LAUNCHED)
585             || pln_equip(plp, ip, flags, mission) < 0) {
586             emp_remque(qp);
587             free(qp);
588             continue;
589         }
590         if (flags & (P_S | P_I)) {
591             if (plp->pcp->pl_flags & P_S)
592                 mission_flags |= P_S;
593             if (plp->pcp->pl_flags & P_I)
594                 mission_flags |= P_I;
595         }
596         if (!(plp->pcp->pl_flags & P_H))
597             /* no stealth on this mission */
598             mission_flags &= ~P_H;
599         if (!(plp->pcp->pl_flags & P_X))
600             /* no stealth on this mission */
601             mission_flags &= ~P_X;
602         if (!(plp->pcp->pl_flags & P_A)) {
603             /* no asw on this mission */
604             mission_flags &= ~P_A;
605             /* FIXME escorts turn ASW patrol into ordinary recon */
606         }
607         if (!(plp->pcp->pl_flags & P_MINE)) {
608             /* no asw on this mission */
609             mission_flags &= ~P_MINE;
610             /* FIXME no effect */
611         }
612         pp->pln_flags |= PLN_LAUNCHED;
613         pp->pln_mobil -= pln_mobcost(dist, pp, flags);
614         putplane(pp->pln_uid, pp);
615         pr("%s equipped\n", prplane(pp));
616     }
617     return mission_flags;
618 }
619
620 static int
621 pln_equip(struct plist *plp, struct ichrstr *ip, int flags, char mission)
622 {
623     struct plchrstr *pcp;
624     struct plnstr *pp;
625     int load, needed;
626     struct lndstr land;
627     struct shpstr ship;
628     struct sctstr sect;
629     i_type itype;
630     short *item;
631     int own;
632     int abandon_needed;
633
634     pp = &plp->plane;
635     pcp = plp->pcp;
636     if (pp->pln_ship >= 0) {
637         getship(pp->pln_ship, &ship);
638         item = ship.shp_item;
639         own = ship.shp_own;
640     } else if (pp->pln_land >= 0) {
641         getland(pp->pln_land, &land);
642         item = land.lnd_item;
643         own = land.lnd_own;
644     } else {
645         getsect(pp->pln_x, pp->pln_y, &sect);
646         item = sect.sct_item;
647         own = sect.sct_oldown;
648     }
649     if (ip) {
650         if (ip->i_uid == I_CIVIL) {
651             if (pp->pln_own != own) {
652                 pr("You don't control those civilians!\n");
653                 return -1;
654             }
655         }
656     }
657     if (pcp->pl_fuel > item[I_PETROL]) {
658         pr("%s not enough petrol there!\n", prplane(pp));
659         return -1;
660     }
661     item[I_PETROL] -= pcp->pl_fuel;
662     if ((flags & P_F) == 0) {
663         load = pln_load(pp);
664         itype = I_NONE;
665         needed = 0;
666         switch (mission) {
667         case 's':               /* strategic bomb */
668         case 'p':               /* pinpoint bomb */
669             if (pp->pln_nuketype == -1) {
670                 itype = I_SHELL;
671                 needed = load;
672             }
673             break;
674         case 't':               /* transport */
675         case 'd':               /* drop */
676             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
677                 break;
678             itype = ip->i_uid;
679             needed = (load * 2) / ip->i_lbs;
680             break;
681         case 'm':               /* mine */
682             if ((pcp->pl_flags & P_MINE) == 0)
683                 break;
684             itype = I_SHELL;
685             needed = (load * 2) / ip->i_lbs;
686             break;
687         case 'a':               /* paradrop */
688             if ((pcp->pl_flags & (P_V | P_C)) == 0)
689                 break;
690             itype = I_MILIT;
691             needed = load / ip->i_lbs;
692             break;
693         case 'r':               /* reconnaissance */
694             break;
695         default:
696             CANT_REACH();
697         }
698         if (itype != I_NONE && needed <= 0) {
699             pr("%s can't contribute to mission\n", prplane(pp));
700             return -1;
701         }
702         if (itype != I_NONE) {
703 #if 0
704             /* Supply is broken somewhere, so don't use it for now */
705             if (itype == I_SHELL && item[itype] < needed)
706                 item[itype] += supply_commod(plp->plane.pln_own,
707                                              plp->plane.pln_x,
708                                              plp->plane.pln_y,
709                                              I_SHELL, needed);
710 #endif
711             abandon_needed = !!would_abandon(&sect, itype, needed, NULL);
712             if (item[itype] < needed + abandon_needed) {
713                 pr("Not enough %s for %s\n", ichr[itype].i_name, prplane(pp));
714                 return -1;
715             }
716             item[itype] -= needed;
717         }
718         if (itype == I_SHELL && (mission == 's' || mission == 'p'))
719             plp->bombs = needed;
720         else
721             plp->misc = needed;
722     }
723     if (pp->pln_ship >= 0) {
724         if (pp->pln_own != ship.shp_own) {
725             wu(0, ship.shp_own,
726                "%s %s prepares for takeoff from ship %s\n",
727                cname(pp->pln_own), prplane(pp), prship(&ship));
728         }
729         putship(ship.shp_uid, &ship);
730     } else if (pp->pln_land >= 0) {
731         if (pp->pln_own != land.lnd_own) {
732             wu(0, land.lnd_own,
733                "%s %s prepares for takeoff from unit %s\n",
734                cname(pp->pln_own), prplane(pp), prland(&land));
735         }
736         putland(land.lnd_uid, &land);
737     } else {
738         if (pp->pln_own != sect.sct_own) {
739             wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
740                cname(pp->pln_own), prplane(pp),
741                xyas(sect.sct_x, sect.sct_y, sect.sct_own));
742         }
743         putsect(&sect);
744     }
745     return 0;
746 }
747
748 void
749 pln_put(struct emp_qelem *list)
750 {
751     struct emp_qelem *qp, *next;
752
753     for (qp = list->q_forw; qp != list; qp = next) {
754         next = qp->q_forw;
755         pln_put1((struct plist *)qp);
756     }
757 }
758
759 void
760 pln_put1(struct plist *plp)
761 {
762     struct plnstr *pp;
763     struct shpstr ship;
764     struct lndstr land;
765     struct sctstr sect;
766
767     pp = &plp->plane;
768
769     if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
770                     && (plchr[pp->pln_type].pl_flags & P_M)
771                     && pp->pln_effic >= PLANE_MINEFF))
772         pp->pln_effic = 0;   /* bug: missile launched but not used up */
773
774     if (!(pp->pln_flags & PLN_LAUNCHED))
775         ;                       /* never took off */
776     else if (pp->pln_effic < PLANE_MINEFF) {
777         /* destroyed */
778         if (pp->pln_ship >= 0) {
779             getship(pp->pln_ship, &ship);
780             take_plane_off_ship(pp, &ship);
781         }
782         if (pp->pln_land >= 0) {
783             getland(pp->pln_land, &land);
784             take_plane_off_land(pp, &land);
785         }
786     } else if (pp->pln_ship >= 0) {
787         /* It is landing on a carrier */
788         getship(pp->pln_ship, &ship);
789         /* We should do more, like make sure it's really
790            a carrier, etc. but for now just make sure it's
791            not sunk. */
792         if (ship.shp_effic < SHIP_MINEFF) {
793             mpr(pp->pln_own,
794                 "Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
795                 "splashes into the sea.\n",
796                 pp->pln_ship, pp->pln_uid);
797             pp->pln_effic = 0;
798         }
799     } else {
800         /* Presume we are landing back in a sector. */
801         getsect(pp->pln_x, pp->pln_y, &sect);
802         if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
803             mpr(pp->pln_own,
804                 "Nowhere to land at %s, plane #%d crashes and burns...\n",
805                 xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
806             pp->pln_effic = 0;
807         }
808     }
809     pp->pln_flags &= ~PLN_LAUNCHED;
810     putplane(pp->pln_uid, pp);
811     emp_remque(&plp->queue);
812     free(plp);
813 }
814
815 /*
816  * Fit a plane of PP's type on ship SP.
817  * Adjust SP's plane counters.
818  * Updating the plane accordingly is the caller's job.
819  * Return whether it fits.
820  */
821 static int
822 fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp)
823 {
824     struct plchrstr *pcp = plchr + pp->pln_type;
825     struct mchrstr *mcp = mchr + sp->shp_type;
826     int wanted;
827
828     if (pcp->pl_flags & P_K) {
829         /* chopper, try chopper slot first */
830         if (sp->shp_nchoppers < mcp->m_nchoppers)
831             return ++sp->shp_nchoppers;
832         /* else try plane slot */
833         wanted = M_FLY;
834     } else if (pcp->pl_flags & P_E) {
835         /* x-light, try x-light slot first */
836         if (sp->shp_nxlight < mcp->m_nxlight)
837             return ++sp->shp_nxlight;
838         /* else try plane slot */
839         wanted = M_MSL | M_FLY;
840     } else if (!(pcp->pl_flags & P_L)) {
841         /* not light, no go */
842         wanted = 0;
843     } else if (pcp->pl_flags & P_M) {
844         /* missile, use plane slot */
845         wanted = M_MSL | M_FLY;
846     } else {
847         /* fixed-wing plane, use plane slot */
848         wanted = M_FLY;
849     }
850
851     if ((mcp->m_flags & wanted) == 0)
852         return 0;               /* ship not capable */
853
854     if (sp->shp_nplane < mcp->m_nplanes)
855         return ++sp->shp_nplane;
856
857     return 0;
858 }
859
860 /*
861  * Fit a plane of PP's type off ship SP.
862  * Adjust SP's plane counters, badly.  You need to run count_planes()
863  * before the next fit_plane_on_ship().
864  * Updating the plane accordingly is the caller's job.
865  */
866 static void
867 fit_plane_off_ship(struct plnstr *pp, struct shpstr *sp)
868 {
869     /*
870      * Frees chopper and nxlight slots first, which is why we need to
871      * run count_planes() before fit_plane_on_ship().
872      */
873     struct plchrstr *pcp = plchr + pp->pln_type;
874
875     if (pcp->pl_flags & P_K) {
876         if (sp->shp_nchoppers) {
877             sp->shp_nchoppers--;
878             return;
879         }
880     } else if (pcp->pl_flags & P_E) {
881         if (sp->shp_nxlight) {
882             sp->shp_nxlight--;
883             return;
884         }
885     }
886
887     if (CANT_HAPPEN(sp->shp_nplane == 0))
888         sp->shp_nplane = 1;
889     sp->shp_nplane--;
890 }
891
892 int
893 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
894 {
895     if (plane->pln_ship == ship->shp_uid)
896         return 1;               /* Already on ship */
897
898     if (!fit_plane_on_ship(plane, ship))
899         return 0;
900
901     plane->pln_x = ship->shp_x;
902     plane->pln_y = ship->shp_y;
903     plane->pln_ship = ship->shp_uid;
904     putplane(plane->pln_uid, plane);
905     putship(ship->shp_uid, ship);
906     return 1;
907 }
908
909 void
910 take_plane_off_ship(struct plnstr *plane, struct shpstr *ship)
911 {
912     if (CANT_HAPPEN(plane->pln_ship != ship->shp_uid))
913         return;
914
915     fit_plane_off_ship(plane, ship);
916     plane->pln_ship = -1;
917     putship(ship->shp_uid, ship);
918     putplane(plane->pln_uid, plane);
919 }
920
921 /*
922  * Fit a plane of PP's type on land unit LP.
923  * Adjust LP's plane counters.
924  * Updating the plane accordingly is the caller's job.
925  * Return whether it fits.
926  */
927 static int
928 fit_plane_on_land(struct plnstr *pp, struct lndstr *lp)
929 {
930     struct plchrstr *pcp = plchr + pp->pln_type;
931     struct lchrstr *lcp = lchr + lp->lnd_type;
932
933     if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight)
934         return ++lp->lnd_nxlight;
935
936     return 0;
937 }
938
939 /*
940  * Fit a plane of PP's type off land unit LP.
941  * Adjust LP's plane counters.
942  * Updating the plane accordingly is the caller's job.
943  */
944 static void
945 fit_plane_off_land(struct plnstr *pp, struct lndstr *lp)
946 {
947     if (CANT_HAPPEN(lp->lnd_nxlight == 0))
948         lp->lnd_nxlight = 1;
949     lp->lnd_nxlight--;
950 }
951
952 int
953 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
954 {
955     if (plane->pln_land == land->lnd_uid)
956         return 1;               /* Already on unit */
957
958     if (!fit_plane_on_land(plane, land))
959         return 0;
960
961     plane->pln_x = land->lnd_x;
962     plane->pln_y = land->lnd_y;
963     plane->pln_land = land->lnd_uid;
964     putplane(plane->pln_uid, plane);
965     putland(land->lnd_uid, land);
966     return 1;
967 }
968
969 void
970 take_plane_off_land(struct plnstr *plane, struct lndstr *land)
971 {
972     if (CANT_HAPPEN(plane->pln_land != land->lnd_uid))
973         return;
974
975     fit_plane_off_land(plane, land);
976     plane->pln_land = -1;
977     putland(land->lnd_uid, land);
978     putplane(plane->pln_uid, plane);
979 }
980
981 /*
982  * Could a plane of PP's type be on on ship SP?
983  */
984 int
985 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
986 {
987     struct shpstr ship;
988
989     ship = *sp;
990     ship.shp_nplane = ship.shp_nchoppers = ship.shp_nxlight = 0;
991     return fit_plane_on_ship(pp, &ship);
992 }
993
994 void
995 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
996 {
997     struct plnstr *pp;
998     struct plchrstr *pcp;
999     struct emp_qelem *qp;
1000     struct emp_qelem *next;
1001     struct plist *ip;
1002     struct sctstr sect;
1003     int mines_there;
1004     int found = 0;
1005
1006     getsect(x, y, &sect);
1007     mines_there = sect.sct_mines;
1008
1009     if (mines_there == 0)
1010         return;
1011
1012     if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
1013         return;
1014
1015     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
1016          qp = next) {
1017         next = qp->q_forw;
1018         ip = (struct plist *)qp;
1019         pp = &ip->plane;
1020         pcp = ip->pcp;
1021         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
1022             continue;
1023
1024         if (chance((100.0 - pln_acc(pp)) / 100.0)) {
1025             pr("Sweep! in %s\n",
1026                xyas(sect.sct_x, sect.sct_y, pp->pln_own));
1027             mines_there--;
1028             found = 1;
1029         }
1030     }
1031
1032     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
1033         writemap(player->cnum);
1034     sect.sct_mines = mines_there;
1035     putsect(&sect);
1036 }
1037
1038 void
1039 count_planes(struct shpstr *sp)
1040 {
1041     struct nstr_item ni;
1042     struct plnstr plane;
1043     int nplane, nchoppers, nxlight;
1044
1045     if (sp->shp_effic < SHIP_MINEFF)
1046         return;
1047
1048     nplane = sp->shp_nplane;
1049     sp->shp_nplane = 0;
1050     nchoppers = sp->shp_nchoppers;
1051     sp->shp_nchoppers = 0;
1052     nxlight = sp->shp_nxlight;
1053     sp->shp_nxlight = 0;
1054
1055     snxtitem_xy(&ni, EF_PLANE, sp->shp_x, sp->shp_y);
1056     while (nxtitem(&ni, &plane)) {
1057         if (plane.pln_own == 0)
1058             continue;
1059         if (plane.pln_ship == sp->shp_uid) {
1060             if (!fit_plane_on_ship(&plane, sp))
1061                 CANT_REACH();
1062         }
1063     }
1064
1065     if (nplane != sp->shp_nplane ||
1066         nxlight != sp->shp_nxlight || nchoppers != sp->shp_nchoppers) {
1067         putship(sp->shp_uid, sp);
1068     }
1069 }
1070
1071 void
1072 count_land_planes(struct lndstr *lp)
1073 {
1074     struct nstr_item ni;
1075     struct plnstr plane;
1076     int nplane;
1077
1078     if (lp->lnd_effic < LAND_MINEFF)
1079         return;
1080
1081     nplane = lp->lnd_nxlight;
1082     lp->lnd_nxlight = 0;
1083
1084     snxtitem_all(&ni, EF_PLANE);
1085     while (nxtitem(&ni, &plane)) {
1086         if (plane.pln_own == 0)
1087             continue;
1088         if (plane.pln_land == lp->lnd_uid)
1089             if (!fit_plane_on_land(&plane, lp))
1090                 CANT_REACH();
1091     }
1092
1093     if (lp->lnd_nxlight != nplane)
1094         putland(lp->lnd_uid, lp);
1095 }
1096
1097 int
1098 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
1099 {
1100     struct plchrstr *pcp = plchr + pp->pln_type;
1101     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
1102         (pp->pln_tech - pcp->pl_tech / 2);
1103     int acc = pln_acc(pp);
1104     int hitchance;
1105
1106     if (type == EF_SHIP) {
1107         if (pcp->pl_flags & P_A)
1108             acc -= 20;
1109         if (!(pcp->pl_flags & P_T))
1110             acc += 35;
1111     }
1112     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1113                       (1.0 - acc / 100.0)) - hardtarget;
1114
1115     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1116     if (hitchance < 20)
1117         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1118     if (hitchance > 100)
1119         hitchance = 100;
1120     return hitchance;
1121 }
1122
1123 /* return 0 if there was a nuclear detonation */
1124
1125 int
1126 pln_damage(struct plnstr *pp, coord x, coord y, char type, int *nukedamp,
1127            int noisy)
1128 {
1129     struct nukstr nuke;
1130     struct plchrstr *pcp = plchr + pp->pln_type;
1131     int load, i;
1132     int hitroll;
1133     int dam = 0;
1134     int aim;
1135     int effective = 1;
1136     int pinbomber = 0;
1137
1138     if (pp->pln_nuketype != -1) {
1139         if (nuk_on_plane(&nuke, pp->pln_uid) >= 0) {
1140             mpr(pp->pln_own, "Releasing RV's for %s detonation...\n",
1141                 pp->pln_flags & PLN_AIRBURST ? "airburst" : "groundburst");
1142             pp->pln_nuketype = -1;
1143             *nukedamp = detonate(&nuke, x, y,
1144                                  pp->pln_flags & PLN_AIRBURST);
1145             return 0;
1146         }
1147         CANT_REACH();
1148     }
1149     *nukedamp = 0;
1150
1151     load = pln_load(pp);
1152     if (!load)                 /* e.g. ab, blowing up on launch pad */
1153         return 0;
1154
1155     i = roll(load) + 1;
1156     if (i > load)
1157         i = load;
1158
1159     if (pcp->pl_flags & P_M) {
1160         if (pcp->pl_flags & P_MAR)
1161             pinbomber = 1;
1162     } else if (pcp->pl_flags & P_T)
1163         pinbomber = 1;
1164
1165     aim = pln_acc(pp);
1166     if (type == 's') {
1167         effective = !pinbomber;
1168         aim = 30 + (pinbomber ? aim : 100 - aim);
1169     } else {
1170         effective = pinbomber;
1171         aim = 100 - aim;
1172     }
1173
1174     while (i--) {
1175         dam += roll(6);
1176         hitroll = roll(100);
1177         if (hitroll >= 90) {
1178             dam += 8;
1179             if (noisy)
1180                 mpr(pp->pln_own, "BLAM");
1181         } else if (hitroll < aim) {
1182             dam += 5;
1183             if (noisy)
1184                 mpr(pp->pln_own, "Blam");
1185         } else {
1186             dam += 1;
1187             if (noisy)
1188                 mpr(pp->pln_own, "blam");
1189         }
1190         if (i && noisy)
1191             mpr(pp->pln_own, "-");
1192     }
1193     if (noisy)
1194         mpr(pp->pln_own, "\n");
1195     if (effective)
1196         dam *= 2;
1197     return dam;
1198 }
1199
1200 int
1201 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1202 {
1203     double misschance =
1204         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1205     return (int)(100 - 100 * misschance * misschance);
1206 }
1207
1208 int
1209 pln_mobcost(int dist, struct plnstr *pp, int flags)
1210 {
1211     double cost;
1212
1213     cost = 20.0 / (pp->pln_effic / 100.0);
1214     if ((flags & P_F) || (flags & P_ESC))
1215         cost /= 2;
1216
1217     return ldround(cost * dist / pln_range_max(pp) + 5, 1);
1218 }
1219
1220 int
1221 pln_is_in_orbit(struct plnstr *pp)
1222 {
1223     return (plchr[pp->pln_type].pl_flags & (P_M | P_O)) == P_O
1224         && (pp->pln_flags & PLN_LAUNCHED);
1225 }
1226
1227 /*
1228  * Set PP's tech to TLEV along with everything else that depends on it.
1229  */
1230 void
1231 pln_set_tech(struct plnstr *pp, int tlev)
1232 {
1233     struct plchrstr *pcp = plchr + pp->pln_type;
1234     int limited_range = pp->pln_range < pln_range_max(pp);
1235     int range_max;
1236
1237     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1238         tlev = pcp->pl_tech;
1239     pp->pln_tech = tlev;
1240
1241     range_max = pln_range_max(pp);
1242     if (!limited_range || pp->pln_range > range_max)
1243         pp->pln_range = range_max;
1244 }