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