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