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