]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
(pln_dropoff): The previous revision screwed up output; fix.
[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             s_char *ptr, int type)
212 {
213     struct emp_qelem *qp;
214     struct plist *plp;
215     int amt;
216     struct shpstr *ship;
217     int there;
218     int max;
219     struct mchrstr *mp;
220
221     if (ip == 0)
222         return;
223     amt = 0;
224     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
225         plp = (struct plist *)qp;
226         amt += plp->misc;
227     }
228     if (type == EF_SECTOR &&
229         (((struct sctstr *)ptr)->sct_type == SCT_WATER) &&
230         ip->i_vtype == V_SHELL) {
231         ((struct sctstr *)ptr)->sct_mines += amt;
232         pr("%d mines laid in %s.\n", amt,
233            xyas(((struct sctstr *)ptr)->sct_x,
234                 ((struct sctstr *)ptr)->sct_y, player->cnum));
235         if (amt > 0 &&
236             map_set(player->cnum, ((struct sctstr *)ptr)->sct_x,
237                     ((struct sctstr *)ptr)->sct_y, 'X', 0))
238             writemap(player->cnum);
239         putsect((struct sctstr *)ptr);
240     } else {
241         if (type == EF_SHIP) {
242             ship = (struct shpstr *)ptr;
243             there = ship->shp_item[ip->i_vtype];
244             mp = &mchr[(int)ship->shp_type];
245             max = vl_find(ip->i_vtype, mp->m_vtype,
246                           mp->m_vamt, (int)mp->m_nv);
247         } else {
248             there = ((struct sctstr *)ptr)->sct_item[ip->i_vtype];
249             max = 32767;
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             struct sctstr *sectp = (struct sctstr *)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", ship->shp_uid);
274             putship(ship->shp_uid, ship);
275         }
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 type;
525     s_char *ptr;
526     int item;
527     int rval;
528     int vec[I_MAX + 1];
529     int own;
530
531     pp = &plp->plane;
532     pcp = plp->pcp;
533     if (pp->pln_ship >= 0) {
534         getship(pp->pln_ship, &ship);
535         type = EF_SHIP;
536         ptr = (s_char *)&ship;
537         own = ship.shp_own;
538     } else if (pp->pln_land >= 0) {
539         getland(pp->pln_land, &land);
540         type = EF_LAND;
541         ptr = (s_char *)&land;
542         own = land.lnd_own;
543     } else {
544         getsect(pp->pln_x, pp->pln_y, &sect);
545         type = EF_SECTOR;
546         ptr = (s_char *)&sect;
547         own = sect.sct_oldown;
548     }
549     if (ip) {
550         if (ip->i_vtype == V_CIVIL) {
551             if (pp->pln_own != own) {
552                 pr("You don't control those civilians!\n");
553                 return -1;
554             }
555         }
556     }
557     getvec(VT_ITEM, vec, ptr, type);
558     if (pcp->pl_fuel > vec[I_PETROL]) {
559         pr("%s not enough petrol there!\n", prplane(pp));
560         return -1;
561     }
562     vec[I_PETROL] -= pcp->pl_fuel;
563     rval = 0;
564     if ((flags & P_F) == 0) {
565         item = 0;
566         needed = 0;
567         switch (mission) {
568         case 's':
569         case 'p':
570             if (pp->pln_nuketype == -1) {
571                 item = I_SHELL;
572                 needed = pp->pln_load;
573             }
574             break;
575         case 't':
576             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
577                 break;
578             item = ip - ichr;
579             needed = (pp->pln_load * 2) / ip->i_lbs;
580             break;
581         case 'd':
582             item = ip - ichr;
583             needed = (pp->pln_load * 2) / ip->i_lbs;
584             /* Is this mine dropping excursion? */
585             if ((item == I_SHELL) && (pcp->pl_flags & P_MINE))
586                 break;
587             /* Is this a cargo drop? */
588             if ((pcp->pl_flags & P_C) == 0 || ip == 0) {
589                 item = 0;
590                 needed = 0;
591                 break;
592             }
593             break;
594         case 'a':
595             if ((pcp->pl_flags & (P_V | P_C)) == 0)
596                 break;
597             item = I_MILIT;
598             needed = pp->pln_load / ip->i_lbs;
599             break;
600         case 'n':
601             if (pp->pln_nuketype == (s_char)-1)
602                 rval = -1;
603             break;
604         default:
605             break;
606         }
607         if (rval < 0 || (item && needed <= 0)) {
608             pr("%s can't contribute to mission\n", prplane(pp));
609             return -1;
610         }
611 #if 0
612         /* Supply is broken somewhere, so don't use it for now */
613         if ((vec[item] < needed) && (item == I_SHELL))
614             vec[item] += supply_commod(plp->plane.pln_own,
615                                        plp->plane.pln_x, plp->plane.pln_y,
616                                        I_SHELL, needed);
617 #endif
618         if (vec[item] < needed) {
619             pr("Not enough %s for %s\n", ichr[item].i_name, prplane(pp));
620             return -1;
621         } else {
622             vec[item] -= needed;
623         }
624         if (item == I_SHELL && (mission == 's' || mission == 'p'))
625             plp->bombs = needed;
626         else
627             plp->misc = needed;
628     }
629     putvec(VT_ITEM, vec, ptr, type);
630     if (type == EF_SHIP) {
631         if (pp->pln_own != ship.shp_own) {
632             wu(0, ship.shp_own,
633                "%s %s prepares for takeoff from ship %s\n",
634                cname(pp->pln_own), prplane(pp), prship(&ship));
635         }
636         putship(ship.shp_uid, &ship);
637     } else if (type == EF_LAND) {
638         if (pp->pln_own != land.lnd_own) {
639             wu(0, land.lnd_own,
640                "%s %s prepares for takeoff from unit %s\n",
641                cname(pp->pln_own), prplane(pp), prland(&land));
642         }
643         putland(land.lnd_uid, &land);
644     } else {
645         if (pp->pln_own != sect.sct_own) {
646             wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
647                cname(pp->pln_own), prplane(pp), xyas(sect.sct_x,
648                                                      sect.sct_y,
649                                                      sect.sct_own));
650         }
651         putsect(&sect);
652     }
653     return rval;
654 }
655
656 void
657 pln_put(struct emp_qelem *list)
658 {
659     register struct emp_qelem *qp;
660     register struct emp_qelem *newqp;
661     struct plist *plp;
662     struct plnstr *pp;
663     struct shpstr ship;
664     struct sctstr sect;
665
666     /* Here is where planes return home from bombing runs.
667        We need to make sure they still have somewhere to return
668        home to! */
669     qp = list->q_forw;
670     while (qp != list) {
671         plp = (struct plist *)qp;
672         pp = &plp->plane;
673         /* Ok, check out where it wants to land */
674         if (pp->pln_ship >= 0) {
675             /* It is landing on a carrier */
676             getship(pp->pln_ship, &ship);
677             /* We should do more, like make sure it's really
678                a carrier, etc. but for now just make sure it's
679                not sunk. */
680             if (ship.shp_effic < SHIP_MINEFF) {
681                 mpr(pp->pln_own,
682                     "Ship #%d has been sunk, plane #%d has nowhere to land, and\nsplashes into the sea.\n",
683                     pp->pln_ship, pp->pln_uid);
684                 pp->pln_effic = 0;
685             }
686         } else {
687             /* Presume we are landing back in a sector. */
688             getsect(pp->pln_x, pp->pln_y, &sect);
689             if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
690                 mpr(pp->pln_own,
691                     "Nowwhere to land at %s, plane #%d crashes and burns...\n",
692                     xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
693                 pp->pln_effic = 0;
694             }
695         }
696         putplane(pp->pln_uid, pp);
697         newqp = qp->q_forw;
698         emp_remque(qp);
699         free((s_char *)qp);
700         qp = newqp;
701     }
702 }
703
704 void
705 pln_removedupes(struct emp_qelem *bomb_list, struct emp_qelem *esc_list)
706 {
707     struct emp_qelem *bomb;
708     struct emp_qelem *esc;
709     struct plist *bombp;
710     struct plist *escp;
711
712     if (QEMPTY(bomb_list) || QEMPTY(esc_list))
713         return;
714     bomb = bomb_list->q_forw;
715     while (bomb != bomb_list) {
716         if (QEMPTY(esc_list)) {
717             bomb = bomb_list;
718             continue;
719         }
720         esc = esc_list->q_forw;
721         bombp = (struct plist *)bomb;
722         while (esc != esc_list) {
723             escp = (struct plist *)esc;
724             if (escp->plane.pln_uid == bombp->plane.pln_uid) {
725                 emp_remque(esc);
726                 free((s_char *)esc);
727                 esc = esc_list;
728             } else
729                 esc = esc->q_forw;
730         }
731         bomb = bomb->q_forw;
732     }
733 }
734
735 int
736 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
737 {
738     struct plchrstr *pcp;
739     struct mchrstr *mcp;
740
741     pcp = &plchr[(int)plane->pln_type];
742     mcp = &mchr[(int)ship->shp_type];
743
744     if (((int)plane->pln_ship) == ((int)ship->shp_uid))
745         return 1;               /* Already on ship */
746
747     /* Try to put on ship as a chopper plane */
748     if ((pcp->pl_flags & P_K) &&
749         (mcp->m_flags & M_CHOPPER) &&
750         (ship->shp_nchoppers < mcp->m_nchoppers)) {
751
752         ship->shp_nchoppers++;
753         plane->pln_x = ship->shp_x;
754         plane->pln_y = ship->shp_y;
755         plane->pln_ship = ship->shp_uid;
756         putship(ship->shp_uid, ship);
757         putplane(plane->pln_uid, plane);
758         return 1;
759     }
760
761     /* Try to put on ship as an xlight plane */
762     if ((pcp->pl_flags & P_E) &&
763         (mcp->m_flags & M_XLIGHT) &&
764         (ship->shp_nxlight < mcp->m_nxlight)) {
765
766         ship->shp_nxlight++;
767         plane->pln_x = ship->shp_x;
768         plane->pln_y = ship->shp_y;
769         plane->pln_ship = ship->shp_uid;
770         putship(ship->shp_uid, ship);
771         putplane(plane->pln_uid, plane);
772         return 1;
773     }
774
775     /* Try to put on ship as a normal plane */
776     if ((((pcp->pl_flags & P_L) && (mcp->m_flags & M_FLY)) ||
777          ((pcp->pl_flags & P_M) && (pcp->pl_flags & P_L) &&
778           (mcp->m_flags & M_MSL))) &&
779         (ship->shp_nplane < mcp->m_nplanes)) {
780
781         ship->shp_nplane++;
782         plane->pln_x = ship->shp_x;
783         plane->pln_y = ship->shp_y;
784         plane->pln_ship = ship->shp_uid;
785         putship(ship->shp_uid, ship);
786         putplane(plane->pln_uid, plane);
787         return 1;
788     }
789
790     /* We have failed */
791     return 0;
792 }
793
794 int
795 take_plane_off_ship(struct plnstr *plane, struct shpstr *ship)
796 {
797     struct plchrstr *pcp;
798     struct mchrstr *mcp;
799
800     pcp = &plchr[(int)plane->pln_type];
801     mcp = &mchr[(int)ship->shp_type];
802
803     /* Try to take off ship as a chopper plane */
804     if ((pcp->pl_flags & P_K) &&
805         (mcp->m_flags & M_CHOPPER) && (ship->shp_nchoppers)) {
806
807         ship->shp_nchoppers--;
808         plane->pln_ship = -1;
809         putship(ship->shp_uid, ship);
810         putplane(plane->pln_uid, plane);
811         return 1;
812     }
813
814     /* Try to take off ship as an xlight plane */
815     if ((pcp->pl_flags & P_E) &&
816         (mcp->m_flags & M_XLIGHT) && (ship->shp_nxlight)) {
817
818         ship->shp_nxlight--;
819         plane->pln_ship = -1;
820         putship(ship->shp_uid, ship);
821         putplane(plane->pln_uid, plane);
822         return 1;
823     }
824
825     /* Try to take off ship as a normal plane */
826     if ((((pcp->pl_flags & P_L) && (mcp->m_flags & M_FLY)) ||
827          ((pcp->pl_flags & P_M) && (pcp->pl_flags & P_L) &&
828           (mcp->m_flags & M_MSL))) && (ship->shp_nplane)) {
829
830         ship->shp_nplane--;
831         plane->pln_ship = -1;
832         putship(ship->shp_uid, ship);
833         putplane(plane->pln_uid, plane);
834         return 1;
835     }
836
837     /* We have failed */
838     return 0;
839 }
840
841 int
842 take_plane_off_land(struct plnstr *plane, struct lndstr *land)
843 {
844     struct plchrstr *pcp;
845     struct lchrstr *lcp;
846
847     pcp = &plchr[(int)plane->pln_type];
848     lcp = &lchr[(int)land->lnd_type];
849
850     /* Try to take off ship as an xlight plane */
851     if ((pcp->pl_flags & P_E) &&
852         (lcp->l_flags & L_XLIGHT) && (land->lnd_nxlight)) {
853
854         land->lnd_nxlight--;
855         plane->pln_land = -1;
856         putland(land->lnd_uid, land);
857         putplane(plane->pln_uid, plane);
858         return 1;
859     }
860
861     /* We have failed */
862     return 0;
863 }
864
865 int
866 can_be_on_ship(int p, int s)
867 {
868     struct plnstr plane;
869     struct shpstr ship;
870     struct plchrstr *pcp;
871     struct mchrstr *mcp;
872
873     getplane(p, &plane);
874     getship(s, &ship);
875
876     pcp = &plchr[(int)plane.pln_type];
877     mcp = &mchr[(int)ship.shp_type];
878
879     if (pcp->pl_flags & P_L)
880         if (mcp->m_flags & M_FLY)
881             return 1;
882
883     if (pcp->pl_flags & P_K)
884         if (mcp->m_flags & M_CHOPPER)
885             return 1;
886
887     if (pcp->pl_flags & P_M)
888         if (mcp->m_flags & M_MSL)
889             return 1;
890
891     if (pcp->pl_flags & P_E)
892         if (mcp->m_flags & M_XLIGHT)
893             return 1;
894
895     return 0;
896 }
897
898 void
899 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
900 {
901     struct plnstr *pp;
902     struct plchrstr *pcp;
903     struct emp_qelem *qp;
904     struct emp_qelem *next;
905     struct plist *ip;
906     struct sctstr sect;
907     int mines_there;
908     int found = 0;
909
910     getsect(x, y, &sect);
911     mines_there = sect.sct_mines;
912
913     if (mines_there == 0)
914         return;
915
916     if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
917         return;
918
919     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
920          qp = next) {
921         next = qp->q_forw;
922         ip = (struct plist *)qp;
923         pp = &ip->plane;
924         pcp = ip->pcp;
925         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
926             continue;
927
928         if (chance(((double)(100 - pp->pln_acc)) / 100.0)) {
929             pr("Sweep! in %s\n",
930                xyas(sect.sct_x, sect.sct_y, pp->pln_own));
931             mines_there--;
932             found = 1;
933         }
934     }
935
936     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
937         writemap(player->cnum);
938     sect.sct_mines = mines_there;
939     putsect(&sect);
940 }
941
942 void
943 count_planes(struct shpstr *sp)
944 {
945     struct nstr_item ni;
946     struct plnstr plane;
947     struct plchrstr *pcp;
948     struct mchrstr *mcp;
949     int nplane = 0;
950     int nchoppers = 0;
951     int nxlight = 0;
952
953     if (sp->shp_effic < SHIP_MINEFF)
954         return;
955
956     mcp = &mchr[(int)sp->shp_type];
957     snxtitem_xy(&ni, EF_PLANE, sp->shp_x, sp->shp_y);
958     while (nxtitem(&ni, (s_char *)&plane)) {
959         if (plane.pln_own == 0)
960             continue;
961         if (plane.pln_ship == sp->shp_uid) {
962             pcp = &plchr[(int)plane.pln_type];
963             if ((pcp->pl_flags & P_K) && (nchoppers < mcp->m_nchoppers))
964                 nchoppers++;
965             else if ((pcp->pl_flags & P_E) && (nxlight < mcp->m_nxlight))
966                 nxlight++;
967             else if ((pcp->pl_flags & P_L) || (pcp->pl_flags & P_M))
968                 nplane++;
969         }
970     }
971
972     if (nplane != sp->shp_nplane ||
973         nxlight != sp->shp_nxlight || nchoppers != sp->shp_nchoppers) {
974         sp->shp_nplane = nplane;
975         sp->shp_nxlight = nxlight;
976         sp->shp_nchoppers = nchoppers;
977         putship(sp->shp_uid, sp);
978     }
979 }
980
981 void
982 count_land_planes(struct lndstr *lp)
983 {
984     struct nstr_item ni;
985     struct plnstr plane;
986     int nplane = 0;
987
988     if (lp->lnd_effic < LAND_MINEFF)
989         return;
990
991     snxtitem_all(&ni, EF_PLANE);
992     while (nxtitem(&ni, (s_char *)&plane)) {
993         if (plane.pln_own == 0)
994             continue;
995         if (plane.pln_land == lp->lnd_uid)
996             nplane++;
997     }
998
999     if (lp->lnd_nxlight != nplane) {
1000         lp->lnd_nxlight = nplane;
1001         putland(lp->lnd_uid, lp);
1002     }
1003 }
1004
1005 int
1006 count_sect_planes(struct sctstr *sp)
1007 {
1008     int count = 0;
1009     struct nstr_item ni;
1010     struct plnstr plane;
1011
1012     snxtitem_all(&ni, EF_PLANE);
1013     while (nxtitem(&ni, (s_char *)&plane)) {
1014         if (!plane.pln_own)
1015             continue;
1016         if (plane.pln_flags & PLN_LAUNCHED)
1017             continue;
1018         if (plane.pln_x == sp->sct_x && plane.pln_y == sp->sct_y)
1019             ++count;
1020     }
1021
1022     return count;
1023 }
1024
1025 int
1026 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
1027 {
1028     struct plchrstr *pcp;
1029     struct lchrstr *lcp;
1030
1031     pcp = &plchr[(int)plane->pln_type];
1032     lcp = &lchr[(int)land->lnd_type];
1033
1034     if (((int)plane->pln_land) == ((int)land->lnd_uid))
1035         return 1;               /* Already on unit */
1036
1037     /* Try to put on unit as an xlight plane */
1038     if ((pcp->pl_flags & P_E) &&
1039         (lcp->l_flags & L_XLIGHT) &&
1040         (land->lnd_nxlight < lcp->l_nxlight)) {
1041
1042         land->lnd_nxlight++;
1043         plane->pln_x = land->lnd_x;
1044         plane->pln_y = land->lnd_y;
1045         plane->pln_land = land->lnd_uid;
1046         putland(land->lnd_uid, land);
1047         putplane(plane->pln_uid, plane);
1048         return 1;
1049     }
1050
1051     /* We have failed */
1052     return 0;
1053 }
1054
1055 int
1056 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
1057 {
1058     struct plchrstr *pcp = plchr + pp->pln_type;
1059     float tfact = (float)(pp->pln_tech - pcp->pl_tech) /
1060         (pp->pln_tech - pcp->pl_tech / 2);
1061     int acc = pp->pln_acc;
1062     int hitchance;
1063
1064     if (type == EF_SHIP) {
1065         if (pcp->pl_flags & P_A)
1066             acc -= 20;
1067         if (!(pcp->pl_flags & P_T))
1068             acc += 35;
1069     }
1070     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1071                       (1.0 - (float)acc / 100.0)) - hardtarget;
1072
1073     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1074     if (hitchance < 20)
1075         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1076     if (hitchance > 100)
1077         hitchance = 100;
1078     return hitchance;
1079 }
1080
1081 /* return 0 if there was a nuclear detonation */
1082
1083 int
1084 pln_damage(struct plnstr *pp, coord x, coord y, s_char type, int *nukedamp,
1085            int noisy)
1086 {
1087     struct plchrstr *pcp = plchr + pp->pln_type;
1088     int i;
1089     int hitroll;
1090     int dam = 0;
1091     int aim;
1092     int effective = 1;
1093     int pinbomber = 0;
1094
1095     if (pp->pln_nuketype != (s_char)-1) {
1096         mpr(pp->pln_own, "Releasing RV's for %s detonation...\n",
1097             pp->pln_flags & PLN_AIRBURST ? "airburst" : "groundburst");
1098         *nukedamp = detonate(pp, x, y);
1099         return 0;
1100     } else
1101         *nukedamp = 0;
1102
1103     if (!pp->pln_load)          /* e.g. ab, blowing up on launch pad */
1104         return 0;
1105
1106     i = roll(pp->pln_load) + 1;
1107     if (i > pp->pln_load)
1108         i = pp->pln_load;
1109
1110     if (pcp->pl_flags & P_M) {
1111         if (pcp->pl_flags & P_MAR)
1112             pinbomber = 1;
1113     } else if (pcp->pl_flags & P_T)
1114         pinbomber = 1;
1115
1116     aim = 100 - pp->pln_acc;
1117     if (type == 's') {
1118         if (pinbomber) {
1119             aim = pp->pln_acc;
1120             effective = 0;
1121         }
1122         aim += 30;
1123     } else {
1124         if (!pinbomber) {
1125             effective = 0;
1126         }
1127     }
1128     while (i--) {
1129         dam += roll(6);
1130         hitroll = roll(100);
1131         if (hitroll >= 90) {
1132             dam += 8;
1133             if (noisy)
1134                 mpr(pp->pln_own, "BLAM");
1135         } else if (hitroll < aim) {
1136             dam += 5;
1137             if (noisy)
1138                 mpr(pp->pln_own, "Blam");
1139         } else {
1140             dam += 1;
1141             if (noisy)
1142                 mpr(pp->pln_own, "blam");
1143         }
1144         if (i && noisy)
1145             mpr(pp->pln_own, "-");
1146     }
1147     if (noisy)
1148         mpr(pp->pln_own, "\n");
1149     if (effective)
1150         dam *= 2;
1151     return dam;
1152 }
1153
1154 int
1155 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1156 {
1157     double misschance =
1158         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1159     return (int)(100 - 100 * misschance * misschance);
1160 }
1161
1162 int
1163 pln_mobcost(int dist, struct plnstr *pp, int flags)
1164 {
1165     int cost;
1166
1167     if ((flags & P_F) || (flags & P_ESC))
1168         cost = 10 * 100 / pp->pln_effic;
1169     else
1170         cost = 20 * 100 / pp->pln_effic;
1171
1172     cost = ldround((double)cost * dist / pp->pln_range_max, 1);
1173
1174     return min(32 + pp->pln_mobil, cost + 5);
1175 }