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