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