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